@scottish-government/designsystem-react 0.10.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/@types/components/Accordion.d.ts +3 -2
  2. package/@types/components/ButtonGroup.d.ts +5 -0
  3. package/@types/components/RadioButton.d.ts +2 -2
  4. package/@types/components/SearchFacets.d.ts +18 -0
  5. package/@types/components/SearchFilters.d.ts +14 -0
  6. package/@types/components/SearchResult.d.ts +30 -0
  7. package/@types/components/SearchSort.d.ts +9 -0
  8. package/@types/components/SideNavigation.d.ts +1 -1
  9. package/CHANGELOG.md +31 -5
  10. package/dist/components/Accordion/Accordion.jsx +8 -3
  11. package/dist/components/ButtonGroup/ButtonGroup.jsx +13 -0
  12. package/dist/components/RadioButton/RadioGroup.jsx +1 -1
  13. package/dist/components/SearchFacets/SearchFacets.jsx +101 -0
  14. package/dist/components/SearchFilters/SearchFilters.jsx +63 -0
  15. package/dist/components/SearchResult/SearchResult.jsx +93 -0
  16. package/dist/components/SearchSort/SearchSort.jsx +28 -0
  17. package/dist/components/SequentialNavigation/SequentialNavigation.jsx +0 -1
  18. package/dist/components/SideNavigation/SideNavigation.jsx +2 -2
  19. package/dist/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +2 -2
  21. package/src/components/Accordion/Accordion.Item.stories.tsx +10 -9
  22. package/src/components/Accordion/Accordion.stories.tsx +4 -4
  23. package/src/components/Accordion/Accordion.test.tsx +48 -14
  24. package/src/components/Accordion/Accordion.tsx +12 -1
  25. package/src/components/Breadcrumbs/Breadcrumbs.Item.stories.tsx +8 -1
  26. package/src/components/Button/Button.stories.tsx +1 -1
  27. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +41 -0
  28. package/src/components/ButtonGroup/ButtonGroup.test.tsx +45 -0
  29. package/src/components/ButtonGroup/ButtonGroup.tsx +20 -0
  30. package/src/components/ContentsNav/ContentsNav.Item.stories.tsx +8 -0
  31. package/src/components/ErrorSummary/ErrorSummary.Item.stories.tsx +7 -0
  32. package/src/components/PageMetadata/PageMetadata.Item.stories.tsx +9 -0
  33. package/src/components/RadioButton/RadioGroup.tsx +2 -2
  34. package/src/components/SearchFacets/SearchFacets.Group.stories.tsx +56 -0
  35. package/src/components/SearchFacets/SearchFacets.Item.stories.tsx +53 -0
  36. package/src/components/SearchFacets/SearchFacets.stories.tsx +38 -0
  37. package/src/components/SearchFacets/SearchFacets.test.tsx +214 -0
  38. package/src/components/SearchFacets/SearchFacets.tsx +99 -0
  39. package/src/components/SearchFilters/SearchFilters.Panel.stories.tsx +201 -0
  40. package/src/components/SearchFilters/SearchFilters.stories.tsx +137 -0
  41. package/src/components/SearchFilters/SearchFilters.test.tsx +161 -0
  42. package/src/components/SearchFilters/SearchFilters.tsx +89 -0
  43. package/src/components/SearchResult/SearchResult.stories.tsx +111 -0
  44. package/src/components/SearchResult/SearchResult.test.tsx +215 -0
  45. package/src/components/SearchResult/SearchResult.tsx +137 -0
  46. package/src/components/SearchSort/SearchSort.stories.tsx +32 -0
  47. package/src/components/SearchSort/SearchSort.test.tsx +129 -0
  48. package/src/components/SearchSort/SearchSort.tsx +45 -0
  49. package/src/components/SequentialNavigation/SequentialNavigation.Next.stories.tsx +1 -1
  50. package/src/components/SequentialNavigation/SequentialNavigation.Previous.stories.tsx +1 -1
  51. package/src/components/SequentialNavigation/SequentialNavigation.tsx +0 -1
  52. package/src/components/SideNavigation/SideNavigation.Item.stories.tsx +9 -0
  53. package/src/components/SideNavigation/SideNavigation.List.stories.tsx +7 -0
  54. package/src/components/SideNavigation/SideNavigation.tsx +2 -1
  55. package/src/components/SiteFooter/SiteFooter.License.stories.tsx +7 -0
  56. package/src/components/SiteFooter/SiteFooter.Link.stories.tsx +9 -0
  57. package/src/components/SiteFooter/SiteFooter.Org.stories.tsx +7 -0
  58. package/src/components/SiteNavigation/SiteNavigation.Item.stories.tsx +10 -0
  59. package/src/components/SkipLinks/SkipLinks.Item.stories.tsx +11 -1
  60. package/src/components/SummaryCard/SummaryCard.Action.stories.tsx +7 -0
  61. package/src/components/SummaryCard/SummaryCard.stories.tsx +7 -0
  62. package/src/components/SummaryList/SummaryList.Item.stories.tsx +15 -0
  63. package/src/components/SummaryList/SummaryList.Value.stories.tsx +5 -2
  64. package/src/components/Tabs/Tabs.Item.stories.tsx +4 -1
  65. package/src/components/TaskList/TaskList.Group.stories.tsx +9 -0
  66. package/src/components/TaskList/TaskList.Item.stories.tsx +7 -0
@@ -0,0 +1,201 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import argTypes from '../../../.storybook/sgdsArgTypes';
3
+
4
+ import Filters from './SearchFilters';
5
+ import DatePicker from '../DatePicker/DatePicker';
6
+ import Checkbox from '../Checkbox/Checkbox';
7
+
8
+ const meta = {
9
+ title: 'Components/Search results/Filters/Filter panel',
10
+ component: Filters.Panel,
11
+ decorators: [(Story) => (
12
+ <div className="ds_accordion ds_accordion--small ds_!_margin-top--0">
13
+ <Story />
14
+ </div>
15
+ )],
16
+ argTypes: {
17
+ children: argTypes.children(),
18
+ isScrollable: {
19
+ description: 'Puts internal scrollbars around the filter fields',
20
+ control: 'boolean',
21
+ },
22
+ legend: {
23
+ description: 'Content for the (visually hidden) legend element',
24
+ type: {
25
+ name: 'string',
26
+ required: true
27
+ }
28
+ },
29
+ heading: {
30
+ description: 'Heading of the filter panel item',
31
+ type: {
32
+ name: 'string',
33
+ required: true
34
+ }
35
+ },
36
+ activeFilterCount: {
37
+ description: 'Number of active filter fields in this panel',
38
+ type: {
39
+ name: 'number'
40
+ }
41
+ }
42
+ }
43
+ } satisfies Meta<typeof Filters.Panel>;
44
+
45
+ export default meta;
46
+ type Story = StoryObj<typeof meta>;
47
+
48
+ const CONTENT_TYPES = [
49
+ {
50
+ label: 'Advice and guidance',
51
+ value: 'advice-and-guidance'
52
+ },
53
+ {
54
+ label: 'Agreement',
55
+ value: 'agreement'
56
+ },
57
+ {
58
+ label: 'Consultation analysis',
59
+ value: 'consultation-analysis'
60
+ },
61
+ {
62
+ label: 'Consultation paper',
63
+ value: 'consultation-paper'
64
+ },
65
+ {
66
+ label: 'Corporate report',
67
+ value: 'corporate-report'
68
+ },
69
+ {
70
+ label: 'Correspondence',
71
+ value: 'correspondence'
72
+ },
73
+ {
74
+ label: 'FOI/EIR release',
75
+ value: 'foi-eir-release'
76
+ },
77
+ {
78
+ label: 'Factsheet',
79
+ value: 'factsheet'
80
+ },
81
+ {
82
+ label: 'Form',
83
+ value: 'form'
84
+ },
85
+ {
86
+ label: 'Impact assessment',
87
+ value: 'impact-assessment'
88
+ },
89
+ {
90
+ label: 'Independent report',
91
+ value: 'independent-report'
92
+ },
93
+ {
94
+ label: 'Map',
95
+ value: 'map'
96
+ },
97
+ {
98
+ label: 'Minutes',
99
+ value: 'minutes'
100
+ },
101
+ {
102
+ label: 'Progress report',
103
+ value: 'progress-report'
104
+ },
105
+ {
106
+ label: 'Regulation/directive/order',
107
+ value: 'regulation-directive-order'
108
+ },
109
+ {
110
+ label: 'Research and analysis',
111
+ value: 'research-and-analysis'
112
+ },
113
+ {
114
+ label: 'Speech/statement',
115
+ value: 'speech-statement'
116
+ },
117
+ {
118
+ label: 'Statistics',
119
+ value: 'statistics'
120
+ },
121
+ {
122
+ label: 'Strategy/plan',
123
+ value: 'strategy-plan'
124
+ },
125
+ {
126
+ label: 'Transparency data',
127
+ value: 'transparency-data'
128
+ }
129
+ ];
130
+
131
+ const CONTENT_TYPES_WITH_SELECTED = JSON.parse(JSON.stringify(CONTENT_TYPES));
132
+ CONTENT_TYPES_WITH_SELECTED[1].checked = true;
133
+ CONTENT_TYPES_WITH_SELECTED[4].checked = true;
134
+ CONTENT_TYPES_WITH_SELECTED[7].checked = true;
135
+
136
+ export const Default: Story = {
137
+ render: (args: any) => (
138
+ <Filters.Panel
139
+ heading="Filter by date"
140
+ legend="Filter by date"
141
+ >
142
+ <DatePicker
143
+ hintText="For example, 21/01/2022"
144
+ id="date-from"
145
+ label="Updated after"
146
+ />
147
+
148
+ <DatePicker
149
+ hintText="For example, 21/01/2022"
150
+ id="date-to"
151
+ label="Updated before"
152
+ />
153
+ </Filters.Panel>
154
+ )
155
+ };
156
+
157
+ export const Scrollable: Story = {
158
+ render: (args: any) => (
159
+ <Filters.Panel
160
+ heading="Content type"
161
+ isScrollable
162
+ legend="Select which publication types you would like to see"
163
+ {...args}
164
+ >
165
+ <Filters.CheckboxGroup>
166
+ {CONTENT_TYPES.map((type) => (
167
+ <Checkbox
168
+ key={type.value}
169
+ label={type.label}
170
+ value={type.value}
171
+ id={type.value}
172
+ />
173
+ ))}
174
+ </Filters.CheckboxGroup>
175
+ </Filters.Panel>
176
+ )
177
+ };
178
+
179
+ export const WithActiveFilterCount: Story = {
180
+ render: (args: any) => (
181
+ <Filters.Panel
182
+ activeFilterCount={CONTENT_TYPES_WITH_SELECTED.filter((item: any) => item.checked).length}
183
+ heading="Content type"
184
+ isScrollable
185
+ legend="Select which publication types you would like to see"
186
+ {...args}
187
+ >
188
+ <Filters.CheckboxGroup>
189
+ {CONTENT_TYPES_WITH_SELECTED.map((type: any) => (
190
+ <Checkbox
191
+ checked={type.checked || false}
192
+ key={type.value}
193
+ label={type.label}
194
+ value={type.value}
195
+ id={type.value}
196
+ />
197
+ ))}
198
+ </Filters.CheckboxGroup>
199
+ </Filters.Panel>
200
+ )
201
+ };
@@ -0,0 +1,137 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import argTypes from '../../../.storybook/sgdsArgTypes';
3
+
4
+ import Filters from './SearchFilters';
5
+ import DatePicker from '../DatePicker/DatePicker';
6
+ import Checkbox from '../Checkbox/Checkbox';
7
+
8
+
9
+ const meta = {
10
+ title: 'Components/Search results/Filters',
11
+ component: Filters,
12
+ argTypes: {
13
+ children: argTypes.children()
14
+ },
15
+ args: {
16
+ title: 'Content type'
17
+ }
18
+ } satisfies Meta<typeof Filters>;
19
+
20
+ export default meta;
21
+ type Story = StoryObj<typeof meta>;
22
+
23
+ const CONTENT_TYPES = [
24
+ {
25
+ label: 'Advice and guidance',
26
+ value: 'advice-and-guidance'
27
+ },
28
+ {
29
+ label: 'Agreement',
30
+ value: 'agreement'
31
+ },
32
+ {
33
+ label: 'Consultation analysis',
34
+ value: 'consultation-analysis'
35
+ },
36
+ {
37
+ label: 'Consultation paper',
38
+ value: 'consultation-paper'
39
+ },
40
+ {
41
+ label: 'Corporate report',
42
+ value: 'corporate-report'
43
+ },
44
+ {
45
+ label: 'Correspondence',
46
+ value: 'correspondence'
47
+ },
48
+ {
49
+ label: 'FOI/EIR release',
50
+ value: 'foi-eir-release'
51
+ },
52
+ {
53
+ label: 'Factsheet',
54
+ value: 'factsheet'
55
+ },
56
+ {
57
+ label: 'Form',
58
+ value: 'form'
59
+ },
60
+ {
61
+ label: 'Impact assessment',
62
+ value: 'impact-assessment'
63
+ },
64
+ {
65
+ label: 'Independent report',
66
+ value: 'independent-report'
67
+ },
68
+ {
69
+ label: 'Map',
70
+ value: 'map'
71
+ },
72
+ {
73
+ label: 'Minutes',
74
+ value: 'minutes'
75
+ },
76
+ {
77
+ label: 'Progress report',
78
+ value: 'progress-report'
79
+ },
80
+ {
81
+ label: 'Regulation/directive/order',
82
+ value: 'regulation-directive-order'
83
+ },
84
+ {
85
+ label: 'Research and analysis',
86
+ value: 'research-and-analysis'
87
+ },
88
+ {
89
+ label: 'Speech/statement',
90
+ value: 'speech-statement'
91
+ },
92
+ {
93
+ label: 'Statistics',
94
+ value: 'statistics'
95
+ },
96
+ {
97
+ label: 'Strategy/plan',
98
+ value: 'strategy-plan'
99
+ },
100
+ {
101
+ label: 'Transparency data',
102
+ value: 'transparency-data'
103
+ }
104
+ ];
105
+
106
+ export const Default: Story = {
107
+ render: (args: any) => (
108
+ <Filters {...args}>
109
+ <Filters.Panel legend="Select which publication types you would like to see" heading="Content type">
110
+ <Filters.CheckboxGroup>
111
+ {CONTENT_TYPES.map((type) => (
112
+ <Checkbox
113
+ key={type.value}
114
+ label={type.label}
115
+ value={type.value}
116
+ id={type.value}
117
+ />
118
+ ))}
119
+ </Filters.CheckboxGroup>
120
+ </Filters.Panel>
121
+
122
+ <Filters.Panel heading="Filter by date" legend="Filter by date">
123
+ <DatePicker
124
+ hintText="For example, 21/01/2022"
125
+ id="date-from"
126
+ label="Updated after"
127
+ />
128
+
129
+ <DatePicker
130
+ hintText="For example, 21/01/2022"
131
+ id="date-to"
132
+ label="Updated before"
133
+ />
134
+ </Filters.Panel>
135
+ </Filters>
136
+ )
137
+ };
@@ -0,0 +1,161 @@
1
+ import { test, expect } from 'vitest';
2
+ import { render, screen, within } from '@testing-library/react';
3
+ import Filters from './SearchFilters';
4
+ import Checkbox from '../Checkbox/Checkbox';
5
+
6
+ test('search filters boilerplate renders correctly', () => {
7
+ render(
8
+ <Filters data-testid="searchfilters"/>
9
+ );
10
+
11
+ const filters = screen.getByTestId('searchfilters');
12
+
13
+ expect(filters).toHaveClass('ds_search-filters');
14
+ expect(filters.tagName).toEqual('DIV');
15
+
16
+ const details = filters.querySelector('.ds_details');
17
+ expect(details).toBeInTheDocument();
18
+ expect(details).toHaveClass('ds_no-margin')
19
+ expect(details?.tagName).toEqual('DIV');
20
+ expect(details).toHaveAttribute('data-module', 'ds-details');
21
+ expect(details?.parentElement).toEqual(filters);
22
+
23
+ const detailsToggle = filters.querySelector('.ds_details__toggle');
24
+ expect(detailsToggle).toBeInTheDocument();
25
+ expect(detailsToggle).toHaveClass('visually-hidden');
26
+ expect(detailsToggle?.tagName).toEqual('INPUT');
27
+ expect(detailsToggle).toHaveAttribute('type', 'checkbox');
28
+ expect(detailsToggle).toHaveAttribute('id', 'filters-toggle');
29
+ expect(detailsToggle?.parentElement).toEqual(details);
30
+
31
+ const detailsSummary = filters.querySelector('.ds_details__summary');
32
+ expect(detailsSummary).toBeInTheDocument();
33
+ expect(detailsSummary?.tagName).toEqual('LABEL');
34
+ expect(detailsSummary).toHaveAttribute('for', 'filters-toggle');
35
+ expect(detailsSummary).toHaveTextContent('Search filters');
36
+ expect(detailsSummary?.parentElement).toEqual(details);
37
+ expect(detailsSummary?.previousElementSibling).toEqual(detailsToggle);
38
+
39
+ const skipLinks = filters.querySelector('.ds_skip-links');
40
+
41
+ const detailsText = filters.querySelector('.ds_details__text');
42
+ expect(detailsText).toBeInTheDocument();
43
+ expect(detailsText?.tagName).toEqual('DIV');
44
+ expect(detailsText?.parentElement).toEqual(details);
45
+ expect(detailsText?.previousElementSibling).toEqual(skipLinks);
46
+
47
+ const form = filters.querySelector('#filters');
48
+ expect(form).toBeInTheDocument();
49
+ expect(form?.tagName).toEqual('FORM');
50
+
51
+ const formTitle = within(form).getByRole('heading');
52
+ expect(formTitle).toBeInTheDocument();
53
+ expect(formTitle.tagName).toEqual('H2');
54
+ expect(formTitle).toHaveTextContent('Filter by');
55
+
56
+ const accordion = form?.querySelector('.ds_accordion');;
57
+ expect(accordion).toBeInTheDocument();
58
+ expect(accordion).toHaveClass('js-initialised', 'ds_accordion--small', 'ds_!_margin-top--0');
59
+ expect(accordion?.parentElement).toEqual(form);
60
+ expect(accordion?.tagName).toEqual('DIV');
61
+ expect(accordion?.previousElementSibling).toEqual(formTitle);
62
+
63
+ const applyButton = within(filters).getByRole('button');
64
+ expect(applyButton).toBeInTheDocument();
65
+ expect(applyButton).toHaveClass('ds_button', 'ds_button--max', 'ds_button--small', 'ds_no-margin');
66
+ expect(applyButton).toHaveTextContent('Apply filter');
67
+ expect(applyButton?.tagName).toEqual('BUTTON');
68
+ expect(applyButton?.parentElement).toEqual(form);
69
+ expect(applyButton?.previousElementSibling).toEqual(accordion);
70
+ });
71
+
72
+ test('search filter panel boilerplate renders correctly', () => {
73
+ render(
74
+ <Filters.Panel data-testid="searchfilter" />
75
+ );
76
+
77
+ const filterPanel = screen.getByTestId('searchfilter');
78
+ expect(filterPanel).toHaveClass('ds_accordion-item');
79
+ expect(filterPanel.tagName).toEqual('DIV');
80
+
81
+ const filterPanelBody = filterPanel.querySelector('.ds_accordion-item__body');
82
+ expect(filterPanelBody).toBeInTheDocument();
83
+ expect(filterPanelBody?.parentElement).toEqual(filterPanel);
84
+
85
+ const fieldset = filterPanelBody?.querySelector('fieldset');
86
+ expect(fieldset).toBeInTheDocument();
87
+ expect(fieldset?.tagName).toEqual('FIELDSET');
88
+ expect(fieldset?.parentElement).toEqual(filterPanelBody);
89
+
90
+ const legend = fieldset?.querySelector('legend');
91
+ expect(legend).toBeInTheDocument();
92
+ expect(legend).toHaveClass('visually-hidden');
93
+ expect(legend?.tagName).toEqual('LEGEND');
94
+ expect(legend?.parentElement).toEqual(fieldset);
95
+ });
96
+
97
+ test('search filter panel with scrollable content', () => {
98
+ render(
99
+ <Filters.Panel isScrollable data-testid="searchfilter"/>
100
+ );
101
+
102
+ const filterPanel = screen.getByTestId('searchfilter');
103
+ const filterPanelBody = filterPanel.querySelector('.ds_accordion-item__body');
104
+ const fieldset = filterPanelBody?.querySelector('fieldset');
105
+ const scrollableDiv = fieldset?.querySelector('.ds_search-filters__scrollable');
106
+
107
+ expect(scrollableDiv).toBeInTheDocument();
108
+ expect(scrollableDiv?.parentElement).toEqual(fieldset);
109
+ });
110
+
111
+ test('search filter panel with an active filter count', () => {
112
+ render(
113
+ <Filters.Panel activeFilterCount={3} heading="Content type" data-testid="searchfilter"/>
114
+ );
115
+
116
+ const filterPanel = screen.getByTestId('searchfilter');
117
+
118
+ const accordionTitle = filterPanel.querySelector('.ds_accordion-item__title');
119
+ expect(accordionTitle).toBeInTheDocument();
120
+ expect(accordionTitle).toHaveTextContent('Content type');
121
+ expect(accordionTitle).toHaveTextContent('(3 selected)');
122
+
123
+ const filterCountDiv = accordionTitle?.querySelector('.ds_search-filters__filter-count');
124
+ expect(filterCountDiv).toBeInTheDocument();
125
+ expect(filterCountDiv).toHaveTextContent('(3 selected)');
126
+ expect(filterCountDiv?.parentElement).toEqual(accordionTitle);
127
+ });
128
+
129
+ test('search filter checkbox group renders correctly', () => {
130
+ render(
131
+ <Filters.CheckboxGroup data-testid="searchfiltercheckboxgroup">
132
+ <Checkbox label="Pension Credit" id="pensioncredit" />
133
+ </Filters.CheckboxGroup>
134
+ );
135
+
136
+ const checkboxGroup = screen.getByTestId('searchfiltercheckboxgroup');
137
+ expect(checkboxGroup).toHaveClass('ds_search-filters__checkboxes');
138
+
139
+ const checkbox = within(checkboxGroup).getByRole('checkbox');
140
+ const checkboxWrapper = checkbox.parentElement;
141
+ expect(checkboxWrapper).toBeInTheDocument();
142
+ expect(checkboxWrapper).toHaveClass('ds_checkbox', 'ds_checkbox--small');
143
+ });
144
+
145
+ test('passing additional props', () => {
146
+ render(
147
+ <Filters data-test="foo" data-testid="searchfilters"/>
148
+ );
149
+
150
+ const searchFilters = screen.getByTestId('searchfilters');
151
+ expect(searchFilters?.dataset.test).toEqual('foo');
152
+ });
153
+
154
+ test('passing additional CSS classes', () => {
155
+ render(
156
+ <Filters className="foo" data-testid="searchfilters"/>
157
+ );
158
+
159
+ const searchFilters = screen.getByTestId('searchfilters');
160
+ expect(searchFilters).toHaveClass('foo');
161
+ });
@@ -0,0 +1,89 @@
1
+ import Accordion from "../Accordion/Accordion";
2
+ import Button from "../Button/Button";
3
+ import SkipLinks from "../SkipLinks/SkipLinks";
4
+ import ConditionalWrapper from "../../common/ConditionalWrapper";
5
+ import CheckboxGroup from "../Checkbox/CheckboxGroup";
6
+
7
+ export const FilterPanel = ({
8
+ activeFilterCount = 0,
9
+ children,
10
+ isScrollable = false,
11
+ legend,
12
+ heading = 'Filter',
13
+ ...props
14
+ }: SGDS.Component.SearchFilters.Panel) => {
15
+ const headingWithCount = <>
16
+ {heading}
17
+ {activeFilterCount > 0 && <div className="ds_search-filters__filter-count">({activeFilterCount} selected)</div>}
18
+ </>;
19
+
20
+ return (
21
+ <Accordion.Item heading={headingWithCount} {...props}>
22
+ <fieldset>
23
+ <legend className="visually-hidden">{legend}</legend>
24
+
25
+ <ConditionalWrapper
26
+ condition={isScrollable}
27
+ wrapper={(children: React.JSX.Element) => <div className="ds_search-filters__scrollable">{children}</div>}
28
+ >
29
+ {children}
30
+ </ConditionalWrapper>
31
+ </fieldset>
32
+ </Accordion.Item>
33
+ );
34
+ };
35
+
36
+ const FilterCheckboxGroup = ({
37
+ children,
38
+ ...props
39
+ }: any) => {
40
+ return (
41
+ <CheckboxGroup
42
+ className="ds_search-filters__checkboxes"
43
+ isSmall
44
+ {...props}
45
+ >
46
+ {children}
47
+ </CheckboxGroup>
48
+ );
49
+ };
50
+
51
+ const Filters = ({
52
+ children,
53
+ searchResultsContainerId = 'search-results',
54
+ ...props
55
+ }: SGDS.Component.SearchFilters) => {
56
+ return (
57
+ <div className="ds_search-filters" {...props}>
58
+ <div className="ds_details ds_no-margin" data-module="ds-details">
59
+ <input id="filters-toggle" type="checkbox" className="ds_details__toggle visually-hidden"/>
60
+ <label htmlFor="filters-toggle" className="ds_details__summary">
61
+ Search filters
62
+ </label>
63
+
64
+ <SkipLinks isStatic>
65
+ <SkipLinks.Link fragmentId={searchResultsContainerId}>Skip to results</SkipLinks.Link>
66
+ </SkipLinks>
67
+
68
+ <div className="ds_details__text">
69
+ <form id="filters">
70
+ <h2 className="ds_search-filters__title ds_h4">Filter by</h2>
71
+
72
+ <Accordion className="ds_!_margin-top--0" isSmall hideOpenAll>
73
+ {children}
74
+ </Accordion>
75
+
76
+ <Button isSmall width="max" className="ds_no-margin">Apply filter</Button>
77
+ </form>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ )
82
+ }
83
+
84
+ Filters.Panel = FilterPanel;
85
+ Filters.CheckboxGroup = FilterCheckboxGroup;
86
+ FilterCheckboxGroup.displayName = 'Filters.CheckboxGroup';
87
+ FilterPanel.displayName = 'Filters.Panel';
88
+
89
+ export default Filters;
@@ -0,0 +1,111 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import argTypes from '../../../.storybook/sgdsArgTypes';
3
+
4
+ import SearchResult from './SearchResult';
5
+
6
+ //@ts-ignore
7
+ import coo from '../../../static/images/highland-cow.jpg';
8
+
9
+ const meta = {
10
+ title: 'Components/Search results/Result',
11
+ component: SearchResult,
12
+ argTypes: {
13
+ children: argTypes.children()
14
+ },
15
+ args: {
16
+ title: 'Application incomplete'
17
+ }
18
+ } satisfies Meta<typeof SearchResult>;
19
+
20
+ export default meta;
21
+ type Story = StoryObj<typeof meta>;
22
+
23
+ export const Default: Story = {
24
+ render: (args) => (
25
+ <SearchResult href="#foo" title="Greenhouse gas statistics 1990-2022" {...args}>
26
+ <SearchResult.Content>
27
+ Official statistics showing emissions of greenhouse gases in Scotland over the period 1990 to 2022.
28
+ </SearchResult.Content>
29
+ </SearchResult>
30
+ )
31
+ };
32
+
33
+ export const Metadata: Story = {
34
+ render: (args) => (
35
+ <SearchResult href="#foo" title="Greenhouse gas statistics 1990-2022" {...args}>
36
+ <SearchResult.Content>
37
+ Official statistics showing emissions of greenhouse gases in Scotland over the period 1990 to 2022.
38
+ </SearchResult.Content>
39
+ <SearchResult.Meta>
40
+ <SearchResult.MetaItem name="Publication type">
41
+ Statistics
42
+ </SearchResult.MetaItem>
43
+ <SearchResult.MetaItem name="Date">
44
+ 18 June 2024
45
+ </SearchResult.MetaItem>
46
+ </SearchResult.Meta>
47
+ </SearchResult>
48
+ )
49
+ };
50
+
51
+ export const Context: Story = {
52
+ render: (args) => (
53
+ <SearchResult href="#foo" title="Greenhouse gas statistics 1990-2022" {...args}>
54
+ <SearchResult.Content>
55
+ Official statistics showing emissions of greenhouse gases in Scotland over the period 1990 to 2022.
56
+ </SearchResult.Content>
57
+ <SearchResult.Context title="Part of">
58
+ <SearchResult.ContextItem><a href="#">Environment statistics</a></SearchResult.ContextItem>
59
+ <SearchResult.ContextItem><a href="#">Energy statistics</a></SearchResult.ContextItem>
60
+ </SearchResult.Context>
61
+ </SearchResult>
62
+ )
63
+ };
64
+
65
+ export const Media: Story = {
66
+ render: (args) => (
67
+ <SearchResult href="#foo" title="Application incomplete" {...args}>
68
+ <SearchResult.Content>
69
+ <SearchResult.Media>
70
+ <img src={coo} alt="" />
71
+ </SearchResult.Media>
72
+ Official statistics showing emissions of greenhouse gases in Scotland over the period 1990 to 2022.
73
+ </SearchResult.Content>
74
+ </SearchResult>
75
+ )
76
+ };
77
+
78
+ export const Promoted: Story = {
79
+ render: (args) => (
80
+ <SearchResult isPromoted href="#foo" title="Greenhouse gas statistics 1990-2022" {...args}>
81
+ <SearchResult.Content>
82
+ Official statistics showing emissions of greenhouse gases in Scotland over the period 1990 to 2022.
83
+ </SearchResult.Content>
84
+ </SearchResult>
85
+ )
86
+ };
87
+
88
+ export const KitchenSink: Story = {
89
+ render: (args) => (
90
+ <SearchResult isPromoted href="#foo" title="Application incomplete" {...args}>
91
+ <SearchResult.Content>
92
+ <SearchResult.Media>
93
+ <img src={coo} alt="" />
94
+ </SearchResult.Media>
95
+ Official statistics showing emissions of greenhouse gases in Scotland over the period 1990 to 2022.
96
+ </SearchResult.Content>
97
+ <SearchResult.Meta>
98
+ <SearchResult.MetaItem name="Publication type">
99
+ Statistics
100
+ </SearchResult.MetaItem>
101
+ <SearchResult.MetaItem name="Date">
102
+ 18 June 2024
103
+ </SearchResult.MetaItem>
104
+ </SearchResult.Meta>
105
+ <SearchResult.Context title="Part of">
106
+ <SearchResult.ContextItem><a href="#">Environment statistics</a></SearchResult.ContextItem>
107
+ <SearchResult.ContextItem><a href="#">Energy statistics</a></SearchResult.ContextItem>
108
+ </SearchResult.Context>
109
+ </SearchResult>
110
+ )
111
+ };