@scottish-government/designsystem-react 0.10.2 → 0.12.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 (175) hide show
  1. package/@types/components/Accordion.d.ts +3 -2
  2. package/@types/components/ButtonGroup.d.ts +5 -0
  3. package/@types/components/PageHeader.d.ts +2 -1
  4. package/@types/components/RadioButton.d.ts +2 -2
  5. package/@types/components/SearchFacets.d.ts +18 -0
  6. package/@types/components/SearchFilters.d.ts +14 -0
  7. package/@types/components/SearchResult.d.ts +30 -0
  8. package/@types/components/SearchSort.d.ts +9 -0
  9. package/@types/components/SideNavigation.d.ts +1 -1
  10. package/CHANGELOG.md +39 -5
  11. package/dist/common/AbstractNotificationBanner.d.ts +9 -0
  12. package/dist/common/ActionLink.d.ts +5 -0
  13. package/dist/common/ConditionalWrapper.d.ts +8 -0
  14. package/dist/common/FileIcon.d.ts +6 -0
  15. package/dist/common/HintText.d.ts +5 -0
  16. package/dist/common/Icon.d.ts +6 -0
  17. package/dist/common/ScreenReaderText.d.ts +5 -0
  18. package/dist/common/WrapperTag.d.ts +8 -0
  19. package/dist/components/Accordion/Accordion.d.ts +10 -0
  20. package/dist/components/Accordion/Accordion.jsx +8 -3
  21. package/dist/components/AspectBox/AspectBox.d.ts +6 -0
  22. package/dist/components/BackToTop/BackToTop.d.ts +5 -0
  23. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +9 -0
  24. package/dist/components/Button/Button.d.ts +5 -0
  25. package/dist/components/ButtonGroup/ButtonGroup.d.ts +5 -0
  26. package/dist/components/ButtonGroup/ButtonGroup.jsx +13 -0
  27. package/dist/components/CategoryItem/CategoryItem.d.ts +5 -0
  28. package/dist/components/CategoryList/CategoryList.d.ts +6 -0
  29. package/dist/components/Checkbox/Checkbox.d.ts +5 -0
  30. package/dist/components/Checkbox/CheckboxGroup.d.ts +6 -0
  31. package/dist/components/ConfirmationMessage/ConfirmationMessage.d.ts +5 -0
  32. package/dist/components/ContentsNav/ContentsNav.d.ts +10 -0
  33. package/dist/components/CookieBanner/CookieBanner.d.ts +9 -0
  34. package/dist/components/DatePicker/DatePicker.d.ts +5 -0
  35. package/dist/components/Details/Details.d.ts +5 -0
  36. package/dist/components/ErrorMessage/ErrorMessage.d.ts +5 -0
  37. package/dist/components/ErrorSummary/ErrorSummary.d.ts +9 -0
  38. package/dist/components/FileDownload/FileDownload.d.ts +5 -0
  39. package/dist/components/HideThisPage/HideThisPage.d.ts +6 -0
  40. package/dist/components/InsetText/InsetText.d.ts +5 -0
  41. package/dist/components/NotificationBanner/NotificationBanner.d.ts +9 -0
  42. package/dist/components/NotificationPanel/NotificationPanel.d.ts +5 -0
  43. package/dist/components/PageHeader/PageHeader.d.ts +5 -0
  44. package/dist/components/PageHeader/PageHeader.jsx +2 -2
  45. package/dist/components/PageMetadata/PageMetadata.d.ts +9 -0
  46. package/dist/components/Pagination/Pagination.d.ts +7 -0
  47. package/dist/components/PhaseBanner/PhaseBanner.d.ts +5 -0
  48. package/dist/components/Question/Question.d.ts +5 -0
  49. package/dist/components/RadioButton/RadioButton.d.ts +5 -0
  50. package/dist/components/RadioButton/RadioGroup.d.ts +6 -0
  51. package/dist/components/RadioButton/RadioGroup.jsx +1 -1
  52. package/dist/components/SearchFacets/SearchFacets.d.ts +14 -0
  53. package/dist/components/SearchFacets/SearchFacets.jsx +101 -0
  54. package/dist/components/SearchFilters/SearchFilters.d.ts +16 -0
  55. package/dist/components/SearchFilters/SearchFilters.jsx +63 -0
  56. package/dist/components/SearchResult/SearchResult.d.ts +28 -0
  57. package/dist/components/SearchResult/SearchResult.jsx +93 -0
  58. package/dist/components/SearchSort/SearchSort.d.ts +10 -0
  59. package/dist/components/SearchSort/SearchSort.jsx +28 -0
  60. package/dist/components/Select/Select.d.ts +5 -0
  61. package/dist/components/SequentialNavigation/SequentialNavigation.d.ts +13 -0
  62. package/dist/components/SequentialNavigation/SequentialNavigation.jsx +0 -1
  63. package/dist/components/SideNavigation/SideNavigation.d.ts +13 -0
  64. package/dist/components/SideNavigation/SideNavigation.jsx +2 -2
  65. package/dist/components/SiteFooter/SiteFooter.d.ts +22 -0
  66. package/dist/components/SiteHeader/SiteHeader.d.ts +22 -0
  67. package/dist/components/SiteHeader/SiteHeader.jsx +0 -1
  68. package/dist/components/SiteNavigation/SiteNavigation.d.ts +9 -0
  69. package/dist/components/SiteSearch/SiteSearch.d.ts +5 -0
  70. package/dist/components/SkipLinks/SkipLinks.d.ts +10 -0
  71. package/dist/components/SkipLinks/SkipLinks.jsx +42 -0
  72. package/dist/components/SummaryCard/SummaryCard.d.ts +10 -0
  73. package/dist/components/SummaryList/SummaryList.d.ts +18 -0
  74. package/dist/components/Table/Table.d.ts +5 -0
  75. package/dist/components/Tabs/Tabs.d.ts +10 -0
  76. package/dist/components/Tag/Tag.d.ts +5 -0
  77. package/dist/components/TaskList/TaskList.d.ts +13 -0
  78. package/dist/components/TextInput/TextInput.d.ts +5 -0
  79. package/dist/components/Textarea/Textarea.d.ts +5 -0
  80. package/dist/components/WarningText/WarningText.d.ts +5 -0
  81. package/dist/hooks/useTracking.d.ts +1 -0
  82. package/dist/images/documents/audio.d.ts +4 -0
  83. package/dist/images/documents/csv.d.ts +4 -0
  84. package/dist/images/documents/excel.d.ts +4 -0
  85. package/dist/images/documents/file.d.ts +4 -0
  86. package/dist/images/documents/generic.d.ts +4 -0
  87. package/dist/images/documents/geodata.d.ts +4 -0
  88. package/dist/images/documents/ical.d.ts +4 -0
  89. package/dist/images/documents/ico.d.ts +4 -0
  90. package/dist/images/documents/image.d.ts +4 -0
  91. package/dist/images/documents/index.d.ts +22 -0
  92. package/dist/images/documents/odf.d.ts +4 -0
  93. package/dist/images/documents/odg.d.ts +4 -0
  94. package/dist/images/documents/odp.d.ts +4 -0
  95. package/dist/images/documents/ods.d.ts +4 -0
  96. package/dist/images/documents/odt.d.ts +4 -0
  97. package/dist/images/documents/pdf.d.ts +4 -0
  98. package/dist/images/documents/ppt.d.ts +4 -0
  99. package/dist/images/documents/rtf.d.ts +4 -0
  100. package/dist/images/documents/text.d.ts +4 -0
  101. package/dist/images/documents/video.d.ts +4 -0
  102. package/dist/images/documents/word.d.ts +4 -0
  103. package/dist/images/documents/xml.d.ts +4 -0
  104. package/dist/images/documents/zip.d.ts +4 -0
  105. package/dist/images/icons/arrow_upward.d.ts +4 -0
  106. package/dist/images/icons/calendar_today.d.ts +4 -0
  107. package/dist/images/icons/cancel.d.ts +4 -0
  108. package/dist/images/icons/check_circle.d.ts +4 -0
  109. package/dist/images/icons/chevron_left.d.ts +4 -0
  110. package/dist/images/icons/chevron_right.d.ts +4 -0
  111. package/dist/images/icons/close.d.ts +4 -0
  112. package/dist/images/icons/description.d.ts +4 -0
  113. package/dist/images/icons/double_chevron_left.d.ts +4 -0
  114. package/dist/images/icons/double_chevron_right.d.ts +4 -0
  115. package/dist/images/icons/error.d.ts +4 -0
  116. package/dist/images/icons/expand_less.d.ts +4 -0
  117. package/dist/images/icons/expand_more.d.ts +4 -0
  118. package/dist/images/icons/index.d.ts +17 -0
  119. package/dist/images/icons/list.d.ts +4 -0
  120. package/dist/images/icons/menu.d.ts +4 -0
  121. package/dist/images/icons/priority_high.d.ts +4 -0
  122. package/dist/images/icons/search.d.ts +4 -0
  123. package/dist/tsconfig.tsbuildinfo +1 -1
  124. package/dist/utils/context.d.ts +4 -0
  125. package/package.json +6 -6
  126. package/src/components/Accordion/Accordion.Item.stories.tsx +10 -9
  127. package/src/components/Accordion/Accordion.stories.tsx +4 -4
  128. package/src/components/Accordion/Accordion.test.tsx +48 -14
  129. package/src/components/Accordion/Accordion.tsx +12 -1
  130. package/src/components/Breadcrumbs/Breadcrumbs.Item.stories.tsx +8 -1
  131. package/src/components/Button/Button.stories.tsx +1 -1
  132. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +41 -0
  133. package/src/components/ButtonGroup/ButtonGroup.test.tsx +45 -0
  134. package/src/components/ButtonGroup/ButtonGroup.tsx +20 -0
  135. package/src/components/ContentsNav/ContentsNav.Item.stories.tsx +8 -0
  136. package/src/components/ErrorSummary/ErrorSummary.Item.stories.tsx +7 -0
  137. package/src/components/PageHeader/PageHeader.tsx +2 -1
  138. package/src/components/PageMetadata/PageMetadata.Item.stories.tsx +9 -0
  139. package/src/components/RadioButton/RadioGroup.tsx +2 -2
  140. package/src/components/SearchFacets/SearchFacets.Group.stories.tsx +56 -0
  141. package/src/components/SearchFacets/SearchFacets.Item.stories.tsx +53 -0
  142. package/src/components/SearchFacets/SearchFacets.stories.tsx +38 -0
  143. package/src/components/SearchFacets/SearchFacets.test.tsx +214 -0
  144. package/src/components/SearchFacets/SearchFacets.tsx +99 -0
  145. package/src/components/SearchFilters/SearchFilters.Panel.stories.tsx +201 -0
  146. package/src/components/SearchFilters/SearchFilters.stories.tsx +137 -0
  147. package/src/components/SearchFilters/SearchFilters.test.tsx +161 -0
  148. package/src/components/SearchFilters/SearchFilters.tsx +89 -0
  149. package/src/components/SearchResult/SearchResult.stories.tsx +111 -0
  150. package/src/components/SearchResult/SearchResult.test.tsx +215 -0
  151. package/src/components/SearchResult/SearchResult.tsx +137 -0
  152. package/src/components/SearchSort/SearchSort.stories.tsx +32 -0
  153. package/src/components/SearchSort/SearchSort.test.tsx +129 -0
  154. package/src/components/SearchSort/SearchSort.tsx +45 -0
  155. package/src/components/SequentialNavigation/SequentialNavigation.Next.stories.tsx +1 -1
  156. package/src/components/SequentialNavigation/SequentialNavigation.Previous.stories.tsx +1 -1
  157. package/src/components/SequentialNavigation/SequentialNavigation.tsx +0 -1
  158. package/src/components/SideNavigation/SideNavigation.Item.stories.tsx +9 -0
  159. package/src/components/SideNavigation/SideNavigation.List.stories.tsx +7 -0
  160. package/src/components/SideNavigation/SideNavigation.tsx +2 -1
  161. package/src/components/SiteFooter/SiteFooter.License.stories.tsx +7 -0
  162. package/src/components/SiteFooter/SiteFooter.Link.stories.tsx +9 -0
  163. package/src/components/SiteFooter/SiteFooter.Org.stories.tsx +7 -0
  164. package/src/components/SiteHeader/SiteHeader.tsx +0 -2
  165. package/src/components/SiteNavigation/SiteNavigation.Item.stories.tsx +10 -0
  166. package/src/components/SkipLinks/SkipLinks.Item.stories.tsx +11 -1
  167. package/src/components/SkipLinks/SkipLinks.tsx +10 -0
  168. package/src/components/SummaryCard/SummaryCard.Action.stories.tsx +7 -0
  169. package/src/components/SummaryCard/SummaryCard.stories.tsx +7 -0
  170. package/src/components/SummaryList/SummaryList.Item.stories.tsx +15 -0
  171. package/src/components/SummaryList/SummaryList.Value.stories.tsx +5 -2
  172. package/src/components/Tabs/Tabs.Item.stories.tsx +4 -1
  173. package/src/components/TaskList/TaskList.Group.stories.tsx +9 -0
  174. package/src/components/TaskList/TaskList.Item.stories.tsx +7 -0
  175. package/tsconfig.json +14 -14
@@ -0,0 +1,137 @@
1
+ import { Children, createContext, useContext } from 'react';
2
+ import ConditionalWrapper from '../../common/ConditionalWrapper';
3
+ import AspectBox from '../AspectBox/AspectBox';
4
+ import Metadata from '../PageMetadata/PageMetadata';
5
+
6
+ const SearchResultLinkHrefContext = createContext('');
7
+
8
+ const SearchResultContent = ({
9
+ children
10
+ }: SGDS.Component.SearchResult.Content) => {
11
+ const otherChildren: any[] = [];
12
+ let imageChild: React.ReactNode = null;
13
+
14
+ // assign to slots
15
+ Children.forEach(children, (child: React.ReactNode) => {
16
+ const thisChild = child as React.ReactElement<any>;
17
+ if (thisChild && thisChild.type === SearchResultMedia) {
18
+ imageChild = thisChild;
19
+ } else {
20
+ otherChildren.push(thisChild);
21
+ }
22
+ });
23
+
24
+ return (
25
+ imageChild ?
26
+ (<div className="ds_search-result__has-media">
27
+ {imageChild}
28
+ <div className="ds_search-result__summary">{otherChildren}</div>
29
+ </div>)
30
+ :
31
+ (<div className="ds_search-result__summary">
32
+ {otherChildren}
33
+ </div>)
34
+ )
35
+ };
36
+
37
+ const SearchResultContext = ({
38
+ children,
39
+ title = 'Part of'
40
+ }: SGDS.Component.SearchResult.Context) => {
41
+ return (
42
+ <dl className="ds_search-result__context">
43
+ <dt className="ds_search-result__context-key">{title}:</dt>
44
+ {children}
45
+ </dl>
46
+ )
47
+ };
48
+
49
+ const SearchResultContextItem = ({
50
+ children
51
+ }: SGDS.Component.SearchResult.ContextItem) => {
52
+ return (
53
+ <dd className="ds_search-result__context-value">
54
+ {children}
55
+ </dd>
56
+ )
57
+ };
58
+
59
+ const SearchResultMedia = ({
60
+ children
61
+ }: SGDS.Component.SearchResult.Media) => {
62
+ return (
63
+ <div className="ds_search-result__media-wrapper">
64
+ <a className="ds_search-result__media-link" href={useContext(SearchResultLinkHrefContext)} tabIndex={-1} aria-hidden="true">
65
+ <AspectBox className="ds_search-result__media" ratio="1:1">
66
+ {children}
67
+ </AspectBox>
68
+ </a>
69
+ </div>
70
+ )
71
+ };
72
+
73
+ const SearchResultMeta = ({
74
+ children
75
+ }: SGDS.Component.SearchResult.Meta) => {
76
+ return (
77
+ <Metadata className="ds_search-result__metadata" isInline>
78
+ {children}
79
+ </Metadata>
80
+ )
81
+ };
82
+
83
+ const SearchResult = ({
84
+ children,
85
+ href,
86
+ isPromoted,
87
+ linkComponent,
88
+ promotedTitle = 'Recommended',
89
+ title,
90
+ ...props
91
+ }: SGDS.Component.SearchResult) => {
92
+ const LINK_CLASS = 'ds_search-result__link';
93
+
94
+ return (
95
+ <div className={[
96
+ 'ds_search-result',
97
+ isPromoted ? 'ds_search-result--promoted' : ''
98
+ ].join(' ')}
99
+ {...props}
100
+ >
101
+ <ConditionalWrapper
102
+ condition={!!isPromoted}
103
+ wrapper={(children: React.JSX.Element) => <div className="ds_search-result--promoted-content">
104
+ <header className="ds_search-result--promoted-title">{promotedTitle}</header>
105
+ {children}
106
+ </div>}
107
+ >
108
+ <SearchResultLinkHrefContext value={href}>
109
+ <h3 className="ds_search-result__title">
110
+ {linkComponent ?
111
+ linkComponent({ className: LINK_CLASS, children: title, href }) :
112
+ <a className={LINK_CLASS} href={href}>{title}</a>
113
+ }
114
+ </h3>
115
+
116
+ {children}
117
+ </SearchResultLinkHrefContext>
118
+ </ConditionalWrapper>
119
+ </div>
120
+ );
121
+ };
122
+
123
+ SearchResult.Content = SearchResultContent;
124
+ SearchResult.Context = SearchResultContext;
125
+ SearchResult.ContextItem = SearchResultContextItem;
126
+ SearchResult.Media = SearchResultMedia;
127
+ SearchResult.Meta = SearchResultMeta;
128
+ SearchResult.MetaItem = Metadata.Item;
129
+
130
+ SearchResultContent.displayName = 'SearchResult.Content';
131
+ SearchResultContext.displayName = 'SearchResult.Context';
132
+ SearchResultContextItem.displayName = 'SearchResult.ContextItem';
133
+ SearchResultMedia.displayName = 'SearchResult.Media';
134
+ SearchResultMeta.displayName = 'SearchResult.Meta';
135
+ SearchResult.MetaItem.displayName = 'SearchResult.MetaItem';
136
+
137
+ export default SearchResult;
@@ -0,0 +1,32 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import argTypes from '../../../.storybook/sgdsArgTypes';
3
+
4
+ import SearchSort from './SearchSort';
5
+
6
+ const meta = {
7
+ title: 'Components/Search results/Sort',
8
+ component: SearchSort,
9
+ argTypes: {
10
+ children: argTypes.children(),
11
+ id: argTypes.id(),
12
+ label: argTypes.label({ defaultValue: 'Sort by' }),
13
+ onApply: argTypes.onClick({ description: 'Callback function to be called when the Apply sort button is clicked' }),
14
+ },
15
+ args: {
16
+ id: 'sort-by',
17
+ label: 'Sort by'
18
+ }
19
+ } satisfies Meta<typeof SearchSort>;
20
+
21
+ export default meta;
22
+ type Story = StoryObj<typeof meta>;
23
+
24
+ export const Default: Story = {
25
+ render: (args) => (
26
+ <SearchSort {...args}>
27
+ <SearchSort.Option value="relevance">Most relevant</SearchSort.Option>
28
+ <SearchSort.Option value="date">Updated (newest)</SearchSort.Option>
29
+ <SearchSort.Option value="adate">Updated (oldest)</SearchSort.Option>
30
+ </SearchSort>
31
+ )
32
+ };
@@ -0,0 +1,129 @@
1
+ import { test, expect, vi } from 'vitest';
2
+ import { render, screen, within } from '@testing-library/react';
3
+ import SearchSort from './SearchSort';
4
+
5
+ const SELECT_ID = 'sort-by';
6
+ const LABEL_TEXT = 'Sort by';
7
+
8
+ test('renders correctly', () => {
9
+ render(
10
+ <SearchSort data-testid="search-sort">
11
+ </SearchSort>
12
+ );
13
+
14
+ const searchSort = screen.getByTestId('search-sort');
15
+ const select = screen.getByRole('combobox');
16
+ const selectWrapper = select.parentElement;
17
+ const label = selectWrapper?.previousElementSibling;
18
+ const selectArrow = select.nextElementSibling;
19
+ const button = within(searchSort).getByRole('button');
20
+
21
+ expect(select).toHaveClass('ds_select');
22
+ expect(select.id).toEqual(SELECT_ID);
23
+ expect(select).toHaveAttribute('name', SELECT_ID);
24
+
25
+ expect(selectWrapper).toHaveClass('ds_select-wrapper');
26
+ expect(selectWrapper?.tagName).toEqual('DIV');
27
+
28
+ expect(label).toHaveClass('ds_label');
29
+ expect(label).toHaveAttribute('for', SELECT_ID);
30
+ expect(label).toHaveTextContent(LABEL_TEXT);
31
+
32
+ expect(selectArrow).toHaveClass('ds_select-arrow');
33
+ expect(selectArrow).toHaveAttribute('aria-hidden');
34
+ expect(selectArrow?.textContent).toEqual('');
35
+
36
+ expect(searchSort).toBeInTheDocument();
37
+ expect(searchSort).toHaveClass('ds_sort-options');
38
+ expect(searchSort.tagName).toEqual('DIV');
39
+
40
+ expect(button).toHaveClass('ds_button', 'ds_button--small', 'ds_button--secondary');
41
+ expect(button).toHaveTextContent('Apply sort');
42
+ expect(button).toHaveAttribute('type', 'submit');
43
+ expect(button.previousElementSibling).toEqual(selectWrapper);
44
+ });
45
+
46
+ test('custom id and label', () => {
47
+ const CUSTOM_ID = 'custom-sort-by';
48
+ const CUSTOM_LABEL = 'Custom sort by';
49
+
50
+ render(
51
+ <SearchSort id={CUSTOM_ID} label={CUSTOM_LABEL} data-testid="search-sort">
52
+ </SearchSort>
53
+ );
54
+
55
+ const select = screen.getByRole('combobox');
56
+ const selectWrapper = select.parentElement;
57
+ const label = selectWrapper?.previousElementSibling;
58
+
59
+ expect(select.id).toEqual(CUSTOM_ID);
60
+ expect(select).toHaveAttribute('name', CUSTOM_ID);
61
+ expect(label).toHaveAttribute('for', CUSTOM_ID);
62
+ expect(label).toHaveTextContent(CUSTOM_LABEL);
63
+ });
64
+
65
+ test('event handler onApply', async () => {
66
+ const onApply = vi.fn();
67
+ render(
68
+ <SearchSort onApply={onApply} data-testid="search-sort">
69
+ </SearchSort>
70
+ );
71
+
72
+ const button = within(screen.getByTestId('search-sort')).getByRole('button');
73
+ await button.click();
74
+ expect(onApply).toHaveBeenCalled();
75
+ });
76
+
77
+ test('passing additional props', () => {
78
+ render(
79
+ <SearchSort data-test="foo" data-testid="search-sort">
80
+ </SearchSort>
81
+ );
82
+
83
+ const searchSort = screen.getByTestId('search-sort');
84
+ expect(searchSort.dataset.test).toEqual('foo');
85
+ });
86
+
87
+ test('passing additional CSS classes', () => {
88
+ render(
89
+ <SearchSort className="foo" data-testid="search-sort">
90
+ </SearchSort>
91
+ );
92
+
93
+ const searchSort = screen.getByTestId('search-sort');
94
+ expect(searchSort).toHaveClass('foo', 'ds_sort-options');
95
+ });
96
+
97
+ test('renders options correctly', () => {
98
+ const OPTION_VALUE_1 = 'relevance';
99
+ const OPTION_TEXT_1 = 'Most relevant';
100
+ const OPTION_VALUE_2 = 'date';
101
+ const OPTION_TEXT_2 = 'Updated (newest)';
102
+ const OPTION_VALUE_3 = 'adate';
103
+ const OPTION_TEXT_3 = 'Updated (oldest)';
104
+
105
+ render(
106
+ <SearchSort data-testid="search-sort">
107
+ <SearchSort.Option value={OPTION_VALUE_1}>{OPTION_TEXT_1}</SearchSort.Option>
108
+ <SearchSort.Option value={OPTION_VALUE_2}>{OPTION_TEXT_2}</SearchSort.Option>
109
+ <SearchSort.Option value={OPTION_VALUE_3}>{OPTION_TEXT_3}</SearchSort.Option>
110
+ </SearchSort>
111
+ );
112
+
113
+ const select = screen.getByRole('combobox');
114
+ const options = within(select).getAllByRole('option');
115
+
116
+ expect(options).toHaveLength(4);
117
+
118
+ expect(options[0]).toHaveTextContent('');
119
+ expect(options[0]).toHaveAttribute('value', '');
120
+
121
+ expect(options[1]).toHaveTextContent(OPTION_TEXT_1);
122
+ expect(options[1]).toHaveAttribute('value', OPTION_VALUE_1);
123
+
124
+ expect(options[2]).toHaveTextContent(OPTION_TEXT_2);
125
+ expect(options[2]).toHaveAttribute('value', OPTION_VALUE_2);
126
+
127
+ expect(options[3]).toHaveTextContent(OPTION_TEXT_3);
128
+ expect(options[3]).toHaveAttribute('value', OPTION_VALUE_3);
129
+ });
@@ -0,0 +1,45 @@
1
+ import { AllHTMLAttributes } from "react";
2
+ import Button from "../Button/Button";
3
+ import Select from "../Select/Select";
4
+
5
+ const Option = ({
6
+ children,
7
+ value
8
+ }: AllHTMLAttributes<HTMLOptionElement>) => {
9
+ return (
10
+ <option value={value}>
11
+ {children}
12
+ </option>
13
+ );
14
+ }
15
+
16
+ const SearchSort = ({
17
+ children,
18
+ className,
19
+ id = 'sort-by',
20
+ label = 'Sort by',
21
+ onApply,
22
+ ...props
23
+ }: SGDS.Component.SearchSort) => {
24
+ return (
25
+ <div
26
+ className={[
27
+ 'ds_sort-options',
28
+ className
29
+ ].join(' ')}
30
+ {...props}
31
+ >
32
+ <Select id={id} label={label}>
33
+ {children}
34
+ </Select>
35
+
36
+ <Button onClick={onApply} isSmall buttonStyle="secondary" type="submit">Apply sort</Button>
37
+ </div>
38
+ );
39
+ };
40
+
41
+ SearchSort.displayName = 'SearchSort';
42
+ Option.displayName = 'SearchSort.Option';
43
+ SearchSort.Option = Option;
44
+
45
+ export default SearchSort;
@@ -7,7 +7,7 @@ const meta = {
7
7
  title: 'Components/SequentialNavigation/SequentialNavigation.Next',
8
8
  component: SequentialNavigation.Next,
9
9
  argTypes: {
10
- href: argTypes.href(),
10
+ href: argTypes.href({type: {name: 'string', required: true}}),
11
11
  linkComponent: argTypes.linkComponent(),
12
12
  textLabel: {
13
13
  description: 'String to use for the label that precedes the link text',
@@ -7,7 +7,7 @@ const meta = {
7
7
  title: 'Components/SequentialNavigation/SequentialNavigation.Previous',
8
8
  component: SequentialNavigation.Previous,
9
9
  argTypes: {
10
- href: argTypes.href(),
10
+ href: argTypes.href({type: {name: 'string', required: true}}),
11
11
  linkComponent: argTypes.linkComponent(),
12
12
  textLabel: {
13
13
  description: 'String to use for the label that precedes the link text',
@@ -5,7 +5,6 @@ const SeqNavLink = ({
5
5
  linkComponent,
6
6
  textLabel
7
7
  }: SGDS.Component.SequentialNavigation.Link) => {
8
- console.log(textLabel)
9
8
  const LINK_CLASSES = [
10
9
  'ds_sequential-nav__button',
11
10
  isPrev ? 'ds_sequential-nav__button--left' : 'ds_sequential-nav__button--right'
@@ -6,6 +6,15 @@ import SideNavigation from './SideNavigation';
6
6
  const meta = {
7
7
  title: 'Components/SideNavigation/SideNavigation.Item',
8
8
  component: SideNavigation.Item,
9
+ decorators: [
10
+ Story => (
11
+ <nav className="ds_side-navigation">
12
+ <ul className="ds_side-navigation__list">
13
+ <Story />
14
+ </ul>
15
+ </nav>
16
+ )
17
+ ],
9
18
  argTypes: {
10
19
  href: argTypes.href(),
11
20
  isCurrent: argTypes.isCurrent(),
@@ -6,6 +6,13 @@ import SideNavigation from './SideNavigation';
6
6
  const meta = {
7
7
  title: 'Components/SideNavigation/SideNavigation.List',
8
8
  component: SideNavigation.List,
9
+ decorators: [
10
+ Story => (
11
+ <nav className="ds_side-navigation">
12
+ <Story />
13
+ </nav>
14
+ )
15
+ ],
9
16
  argTypes: {
10
17
  isRoot: {
11
18
  description: 'Indicates that this is the root list in a nested structure. Required for mobile navigation.',
@@ -43,6 +43,7 @@ const SideNavigationItem = function ({
43
43
  };
44
44
 
45
45
  const SideNavigation = function ({
46
+ ariaLabel = 'Sections',
46
47
  children,
47
48
  className,
48
49
  ...props
@@ -57,7 +58,7 @@ const SideNavigation = function ({
57
58
 
58
59
  return (
59
60
  <nav
60
- aria-label="Sections"
61
+ aria-label={ariaLabel}
61
62
  className={[
62
63
  'ds_side-navigation',
63
64
  className
@@ -6,6 +6,13 @@ import SiteFooter from './SiteFooter';
6
6
  const meta = {
7
7
  title: 'Components/SiteFooter/SiteFooter.License',
8
8
  component: SiteFooter.License,
9
+ decorators: [
10
+ Story => (
11
+ <nav className="ds_site-footer" style={{borderTop: 0}}>
12
+ <Story />
13
+ </nav>
14
+ )
15
+ ],
9
16
  argTypes: {
10
17
  children: argTypes.children()
11
18
  },
@@ -6,6 +6,15 @@ import SiteFooter from './SiteFooter';
6
6
  const meta = {
7
7
  title: 'Components/SiteFooter/SiteFooter.Link',
8
8
  component: SiteFooter.Link,
9
+ decorators: [
10
+ Story => (
11
+ <div className="ds_site-footer__site-items" style={{ borderBottom: 0 }}>
12
+ <ul>
13
+ <Story />
14
+ </ul>
15
+ </div>
16
+ )
17
+ ],
9
18
  argTypes: {
10
19
  href: argTypes.href(),
11
20
  linkComponent: argTypes.linkComponent(),
@@ -6,6 +6,13 @@ import SiteFooter from './SiteFooter';
6
6
  const meta = {
7
7
  title: 'Components/SiteFooter/SiteFooter.Org',
8
8
  component: SiteFooter.Org,
9
+ decorators: [
10
+ Story => (
11
+ <div className="ds_site-footer__content" style={{borderTop: 0}}>
12
+ <Story />
13
+ </div>
14
+ )
15
+ ],
9
16
  argTypes: {
10
17
  href: argTypes.href(),
11
18
  title: {
@@ -1,5 +1,3 @@
1
- 'use client';
2
-
3
1
  import React, { Children, useEffect, useRef } from 'react';
4
2
  import Icon from '../../common/Icon';
5
3
  import SiteNavigation from '../SiteNavigation/SiteNavigation';
@@ -6,7 +6,17 @@ import SiteNavigation from './SiteNavigation';
6
6
  const meta = {
7
7
  title: 'Components/SiteNavigation/SiteNavigation.Item',
8
8
  component: SiteNavigation.Item,
9
+ decorators: [
10
+ Story => (
11
+ <nav className="ds_site-navigation">
12
+ <ul className="ds_site-navigation__list">
13
+ <Story />
14
+ </ul>
15
+ </nav>
16
+ )
17
+ ],
9
18
  argTypes: {
19
+ href: argTypes.href({type: {name: 'string', required: true}}),
10
20
  linkComponent: argTypes.linkComponent(),
11
21
  children: argTypes.children()
12
22
  },
@@ -6,10 +6,20 @@ import SkipLinks from './SkipLinks';
6
6
  const meta = {
7
7
  title: 'Components/SkipLinks/SkipLinks.Link',
8
8
  component: SkipLinks.Link,
9
+ decorators: [
10
+ Story => (
11
+ <ul className="ds_skip-links">
12
+ <Story />
13
+ </ul>
14
+ )
15
+ ],
9
16
  argTypes: {
10
17
  fragmentId: {
11
18
  description: 'ID of the destination element',
12
- type: 'string'
19
+ type: {
20
+ name: 'string',
21
+ required: true
22
+ }
13
23
  },
14
24
  children: argTypes.children()
15
25
  },
@@ -1,3 +1,8 @@
1
+ import React, { useEffect } from 'react';
2
+
3
+ // @ts-ignore
4
+ import dsSkipLinks from '@scottish-government/design-system/src/components/skip-links/skip-links';
5
+
1
6
  const Link = ({
2
7
  children,
3
8
  fragmentId
@@ -18,6 +23,11 @@ const SkipLinks = ({
18
23
  isStatic,
19
24
  ...props
20
25
  }: SGDS.Component.SkipLinks) => {
26
+
27
+ useEffect(() => {
28
+ dsSkipLinks.init();
29
+ });
30
+
21
31
  return (
22
32
  <div
23
33
  className={[
@@ -6,6 +6,13 @@ import SummaryCard from './SummaryCard';
6
6
  const meta = {
7
7
  title: 'Components/SummaryCard/SummaryCard.Action',
8
8
  component: SummaryCard.Action,
9
+ decorators: [
10
+ Story => (
11
+ <div className="ds_summary-card__header">
12
+ <Story />
13
+ </div>
14
+ )
15
+ ],
9
16
  argTypes: {
10
17
  href: argTypes.href(),
11
18
  onClick: argTypes.onClick(),
@@ -9,6 +9,13 @@ const meta = {
9
9
  component: SummaryCard,
10
10
  argTypes: {
11
11
  headingLevel: argTypes.headingLevel(),
12
+ title: {
13
+ description: 'The title of the summary card',
14
+ type: {
15
+ name: 'string',
16
+ required: true
17
+ }
18
+ },
12
19
  children: argTypes.children()
13
20
  },
14
21
  args: {
@@ -6,10 +6,25 @@ import SummaryList from './SummaryList';
6
6
  const meta = {
7
7
  title: 'Components/SummaryList/SummaryList.Item',
8
8
  component: SummaryList.Item,
9
+ decorators: [
10
+ Story => (
11
+ <ul>
12
+ <Story />
13
+ </ul>
14
+ )
15
+ ],
9
16
  argTypes: {
17
+ Title: {
18
+ description: 'Title of the summary list item.',
19
+ type: {
20
+ name: 'string',
21
+ required: true
22
+ }
23
+ },
10
24
  children: argTypes.children()
11
25
  },
12
26
  args: {
27
+ title: 'Contact details',
13
28
  children: [
14
29
  <SummaryList.Value name="Email">
15
30
  email@gov.scot
@@ -8,8 +8,11 @@ const meta = {
8
8
  component: SummaryList.Value,
9
9
  argTypes: {
10
10
  name: {
11
- description: 'Title of the summary list item',
12
- type: 'string'
11
+ description: 'Name of the summary value',
12
+ type: {
13
+ name: 'string',
14
+ required: true
15
+ }
13
16
  },
14
17
  children: argTypes.children()
15
18
  },
@@ -9,7 +9,10 @@ const meta = {
9
9
  argTypes: {
10
10
  tabLabel: {
11
11
  description: '',
12
- type: 'string'
12
+ type: {
13
+ name: 'string',
14
+ required: true
15
+ }
13
16
  },
14
17
  children: argTypes.children()
15
18
  },
@@ -6,6 +6,15 @@ import TaskList from './TaskList';
6
6
  const meta = {
7
7
  title: 'Components/TaskList/TaskList.Group',
8
8
  component: TaskList.Group,
9
+ decorators: [
10
+ Story => (
11
+ <div className="ds_task-list">
12
+ <ul>
13
+ <Story />
14
+ </ul>
15
+ </div>
16
+ )
17
+ ],
9
18
  argTypes: {
10
19
  headingId: {
11
20
  description: 'ID of the task list\'s heading element',
@@ -6,6 +6,13 @@ import TaskList from './TaskList';
6
6
  const meta = {
7
7
  title: 'Components/TaskList/TaskList.Item',
8
8
  component: TaskList.Item,
9
+ decorators: [
10
+ Story => (
11
+ <ul>
12
+ <Story />
13
+ </ul>
14
+ )
15
+ ],
9
16
  argTypes: {
10
17
  href: argTypes.href(),
11
18
  id: {