@scottish-government/designsystem-react 0.7.1 → 0.8.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 (140) hide show
  1. package/@types/common/AbstractNotificationBanner.d.ts +2 -2
  2. package/@types/common/ActionLink.d.ts +8 -0
  3. package/@types/common/FileIcon.d.ts +1 -1
  4. package/@types/common/Icon.d.ts +1 -1
  5. package/@types/components/Breadcrumbs.d.ts +2 -5
  6. package/@types/components/Checkbox.d.ts +0 -2
  7. package/@types/components/ConfirmationMessage.d.ts +1 -1
  8. package/@types/components/ContentsNav.d.ts +4 -6
  9. package/@types/components/DatePicker.d.ts +1 -1
  10. package/@types/components/ErrorSummary.d.ts +3 -4
  11. package/@types/components/NotificationPanel.d.ts +1 -1
  12. package/@types/components/Pagination.d.ts +5 -4
  13. package/@types/components/PhaseBanner.d.ts +0 -1
  14. package/@types/components/Question.d.ts +1 -1
  15. package/@types/components/RadioButton.d.ts +0 -1
  16. package/@types/components/Select.d.ts +0 -7
  17. package/@types/components/SequentialNavigation.d.ts +4 -4
  18. package/@types/components/SideNavigation.d.ts +4 -5
  19. package/@types/components/SiteFooter.d.ts +25 -0
  20. package/@types/components/SiteHeader.d.ts +10 -3
  21. package/@types/components/SiteNavigation.d.ts +2 -3
  22. package/@types/components/SkipLinks.d.ts +3 -4
  23. package/@types/components/SummaryCard.d.ts +0 -2
  24. package/@types/components/SummaryList.d.ts +0 -13
  25. package/@types/components/Tabs.d.ts +0 -1
  26. package/@types/components/Tag.d.ts +1 -3
  27. package/@types/components/TaskList.d.ts +1 -0
  28. package/@types/sgds.d.ts +13 -2
  29. package/CHANGELOG.md +63 -1
  30. package/dist/common/AbstractNotificationBanner.jsx +8 -6
  31. package/dist/common/ActionLink.jsx +19 -0
  32. package/dist/common/FileIcon.jsx +2 -7
  33. package/dist/common/Icon.jsx +3 -9
  34. package/dist/components/Accordion/Accordion.jsx +2 -2
  35. package/dist/components/Breadcrumbs/Breadcrumbs.jsx +20 -15
  36. package/dist/components/Checkbox/Checkbox.jsx +2 -30
  37. package/dist/components/Checkbox/CheckboxGroup.jsx +69 -0
  38. package/dist/components/ContentsNav/ContentsNav.jsx +27 -16
  39. package/dist/components/CookieBanner/CookieBanner.jsx +1 -0
  40. package/dist/components/DatePicker/DatePicker.jsx +5 -5
  41. package/dist/components/ErrorSummary/ErrorSummary.jsx +28 -18
  42. package/dist/components/NotificationBanner/NotificationBanner.jsx +2 -2
  43. package/dist/components/Pagination/Pagination.jsx +42 -22
  44. package/dist/components/PhaseBanner/PhaseBanner.jsx +3 -3
  45. package/dist/components/Question/Question.jsx +3 -3
  46. package/dist/components/RadioButton/RadioButton.jsx +3 -17
  47. package/dist/components/RadioButton/RadioGroup.jsx +61 -0
  48. package/dist/components/Select/Select.jsx +4 -7
  49. package/dist/components/SequentialNavigation/SequentialNavigation.jsx +31 -18
  50. package/dist/components/SideNavigation/SideNavigation.jsx +17 -16
  51. package/dist/components/SiteFooter/SiteFooter.jsx +104 -0
  52. package/dist/components/SiteHeader/SiteHeader.jsx +113 -32
  53. package/dist/components/SiteNavigation/SiteNavigation.jsx +20 -7
  54. package/dist/components/SkipLinks/SkipLinks.jsx +10 -10
  55. package/dist/components/SummaryCard/SummaryCard.jsx +25 -14
  56. package/dist/components/SummaryList/SummaryList.jsx +65 -47
  57. package/dist/components/Tabs/Tabs.jsx +6 -6
  58. package/dist/components/Tag/Tag.jsx +2 -2
  59. package/dist/components/TaskList/TaskList.jsx +14 -3
  60. package/dist/components/TextInput/TextInput.jsx +3 -3
  61. package/dist/components/Textarea/Textarea.jsx +3 -3
  62. package/dist/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +1 -1
  64. package/src/common/AbstractNotificationBanner.test.tsx +1 -1
  65. package/src/common/AbstractNotificationBanner.tsx +14 -13
  66. package/src/common/ActionLink.test.tsx +80 -0
  67. package/src/common/ActionLink.tsx +27 -0
  68. package/src/common/ConditionalWrapper.tsx +1 -1
  69. package/src/common/FileIcon.tsx +7 -11
  70. package/src/common/HintText.tsx +2 -2
  71. package/src/common/Icon.tsx +13 -17
  72. package/src/common/ScreenReaderText.tsx +2 -2
  73. package/src/common/WrapperTag.tsx +2 -2
  74. package/src/components/Accordion/Accordion.test.tsx +1 -1
  75. package/src/components/Accordion/Accordion.tsx +6 -7
  76. package/src/components/AspectBox/AspectBox.tsx +2 -2
  77. package/src/components/BackToTop/BackToTop.tsx +2 -2
  78. package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +79 -47
  79. package/src/components/Breadcrumbs/Breadcrumbs.tsx +31 -31
  80. package/src/components/Button/Button.tsx +2 -2
  81. package/src/components/Checkbox/Checkbox.test.tsx +1 -96
  82. package/src/components/Checkbox/Checkbox.tsx +3 -55
  83. package/src/components/Checkbox/CheckboxGroup.test.tsx +37 -0
  84. package/src/components/Checkbox/CheckboxGroup.tsx +46 -0
  85. package/src/components/ConfirmationMessage/ConfirmationMessage.tsx +2 -2
  86. package/src/components/ContentsNav/ContentsNav.test.tsx +40 -51
  87. package/src/components/ContentsNav/ContentsNav.tsx +32 -25
  88. package/src/components/CookieBanner/CookieBanner.tsx +3 -3
  89. package/src/components/DatePicker/DatePicker.test.tsx +1 -1
  90. package/src/components/DatePicker/DatePicker.tsx +7 -7
  91. package/src/components/Details/Details.tsx +2 -2
  92. package/src/components/ErrorMessage/ErrorMessage.tsx +2 -2
  93. package/src/components/ErrorSummary/ErrorSummary.test.tsx +40 -34
  94. package/src/components/ErrorSummary/ErrorSummary.tsx +40 -32
  95. package/src/components/FileDownload/FileDownload.tsx +2 -2
  96. package/src/components/HideThisPage/HideThisPage.tsx +2 -2
  97. package/src/components/InsetText/InsetText.tsx +2 -2
  98. package/src/components/NotificationBanner/NotificationBanner.tsx +6 -7
  99. package/src/components/NotificationPanel/NotificationPanel.tsx +2 -2
  100. package/src/components/PageHeader/PageHeader.tsx +2 -2
  101. package/src/components/PageMetadata/PageMetadata.tsx +4 -5
  102. package/src/components/Pagination/Pagination.test.tsx +26 -7
  103. package/src/components/Pagination/Pagination.tsx +70 -36
  104. package/src/components/PhaseBanner/PhaseBanner.tsx +4 -5
  105. package/src/components/Question/Question.test.tsx +1 -1
  106. package/src/components/Question/Question.tsx +5 -5
  107. package/src/components/RadioButton/RadioButton.test.tsx +7 -126
  108. package/src/components/RadioButton/RadioButton.tsx +4 -41
  109. package/src/components/RadioButton/RadioGroup.test.tsx +65 -0
  110. package/src/components/RadioButton/RadioGroup.tsx +38 -0
  111. package/src/components/Select/Select.test.tsx +39 -37
  112. package/src/components/Select/Select.tsx +7 -22
  113. package/src/components/SequentialNavigation/SequentialNavigation.test.tsx +32 -21
  114. package/src/components/SequentialNavigation/SequentialNavigation.tsx +52 -30
  115. package/src/components/SideNavigation/SideNavigation.test.tsx +39 -85
  116. package/src/components/SideNavigation/SideNavigation.tsx +27 -29
  117. package/src/components/SiteFooter/SiteFooter.test.tsx +153 -0
  118. package/src/components/SiteFooter/SiteFooter.tsx +107 -0
  119. package/src/components/SiteHeader/SiteHeader.test.tsx +87 -79
  120. package/src/components/SiteHeader/SiteHeader.tsx +103 -40
  121. package/src/components/SiteNavigation/SiteNavigation.test.tsx +42 -23
  122. package/src/components/SiteNavigation/SiteNavigation.tsx +28 -16
  123. package/src/components/SiteSearch/SiteSearch.tsx +2 -2
  124. package/src/components/SkipLinks/SkipLinks.test.tsx +22 -10
  125. package/src/components/SkipLinks/SkipLinks.tsx +16 -15
  126. package/src/components/SummaryCard/SummaryCard.test.tsx +31 -35
  127. package/src/components/SummaryCard/SummaryCard.tsx +39 -28
  128. package/src/components/SummaryList/SummaryList.test.tsx +49 -148
  129. package/src/components/SummaryList/SummaryList.tsx +54 -92
  130. package/src/components/Table/Table.tsx +2 -2
  131. package/src/components/Tabs/Tabs.tsx +14 -15
  132. package/src/components/Tag/Tag.test.tsx +4 -4
  133. package/src/components/Tag/Tag.tsx +4 -4
  134. package/src/components/TaskList/TaskList.test.tsx +26 -0
  135. package/src/components/TaskList/TaskList.tsx +21 -11
  136. package/src/components/TextInput/TextInput.test.tsx +1 -1
  137. package/src/components/TextInput/TextInput.tsx +5 -5
  138. package/src/components/Textarea/Textarea.test.tsx +1 -1
  139. package/src/components/Textarea/Textarea.tsx +5 -5
  140. package/src/components/WarningText/WarningText.tsx +2 -2
@@ -1,75 +1,6 @@
1
1
  import { test, expect, vi } from 'vitest';
2
2
  import { render, screen, fireEvent } from '@testing-library/react';
3
- import CheckboxGroup, { Checkbox } from './Checkbox';
4
-
5
- test('checkbox group renders correct children', () => {
6
- const ITEMS = [
7
- {
8
- id: 'universal-credit',
9
- label: 'Universal Credit',
10
- checked: true
11
- },
12
- {
13
- id: 'pensioncredit',
14
- label: 'Pension Credit'
15
- },
16
- {
17
- id: 'jsa',
18
- label: 'Income-based Job Seeker\'s Allowance',
19
- },
20
- {
21
- exclusive: true,
22
- id: 'none',
23
- label: 'No, I do not receive any of these benefits',
24
- }
25
- ];
26
-
27
- render(
28
- <CheckboxGroup items={ITEMS} />
29
- );
30
-
31
- const checkboxes = screen.getAllByRole('checkbox');
32
- const groupContainer = checkboxes[0].parentElement?.parentElement;
33
- expect(checkboxes.length).toEqual(ITEMS.length);
34
- expect(groupContainer).toHaveClass('ds_checkboxes', 'ds_field-group');
35
- });
36
-
37
- test('checkbox group passes all expected item params', () => {
38
- const ONBLUR_FUNCTION = vi.fn();
39
- const ONCHANGE_FUNCTION = vi.fn();
40
-
41
- render(
42
- <CheckboxGroup small items={[
43
- {
44
- checked: true,
45
- exclusive: true,
46
- hintText: 'hint text',
47
- id: 'myid',
48
- label: 'label text',
49
- onBlur: {ONBLUR_FUNCTION},
50
- onChange: {ONCHANGE_FUNCTION},
51
- small: true
52
- }
53
- ]}/>
54
- );
55
-
56
- const checkbox = screen.getByRole('checkbox');
57
- const checkboxContainer = checkbox.parentElement;
58
- const hintText = screen.getByText('hint text');
59
-
60
- expect(checkbox).toHaveAttribute('data-behaviour', 'exclusive');
61
- expect(checkbox).toHaveAttribute('checked');
62
- expect(checkbox.id).toEqual('myid');
63
- expect(checkboxContainer).toHaveClass('ds_checkbox--small');
64
- expect(hintText).toBeInTheDocument();
65
- expect(checkbox).toHaveAttribute('aria-describedby', hintText.id);
66
-
67
- // fireEvent.blur(checkbox);
68
- // expect(ONBLUR_FUNCTION).toHaveBeenCalled();
69
-
70
- // fireEvent.click(checkbox);
71
- // expect(ONCHANGE_FUNCTION).toHaveBeenCalled();
72
- });
3
+ import Checkbox from './Checkbox';
73
4
 
74
5
  test('individual checkbox renders correctly', () => {
75
6
  render(
@@ -165,29 +96,3 @@ test('small checkbox', () => {
165
96
 
166
97
  expect(checkboxContainer).toHaveClass('ds_checkbox--small');
167
98
  });
168
-
169
- test('passing additional props', () => {
170
- render(
171
- <CheckboxGroup data-test="foo" items={[{
172
- id: 'universal-credit',
173
- label: 'Universal Credit'
174
- }]} />
175
- );
176
-
177
- const checkboxes = screen.getAllByRole('checkbox');
178
- const groupContainer = checkboxes[0]?.parentElement?.parentElement;
179
- expect(groupContainer?.dataset.test).toEqual('foo');
180
- });
181
-
182
- test('passing additional CSS classes', () => {
183
- render(
184
- <CheckboxGroup className="foo" items={[{
185
- id: 'universal-credit',
186
- label: 'Universal Credit'
187
- }]} />
188
- );
189
-
190
- const checkboxes = screen.getAllByRole('checkbox');
191
- const groupContainer = checkboxes[0]?.parentElement?.parentElement;
192
- expect(groupContainer).toHaveClass('foo', 'ds_checkboxes');
193
- });
@@ -1,9 +1,6 @@
1
- import { useEffect, useRef } from 'react';
2
- // @ts-ignore
3
- import DSCheckboxes from '@scottish-government/design-system/src/forms/checkbox/checkboxes'
4
1
  import HintText from '../../common/HintText';
5
2
 
6
- export const Checkbox: React.FC<SGDS.Component.Checkbox> = ({
3
+ const Checkbox = ({
7
4
  checked,
8
5
  hintText,
9
6
  id,
@@ -13,7 +10,7 @@ export const Checkbox: React.FC<SGDS.Component.Checkbox> = ({
13
10
  onBlur,
14
11
  onChange,
15
12
  small
16
- }) => {
13
+ }: SGDS.Component.Checkbox) => {
17
14
  const hintTextId = `hint-text-${id}`;
18
15
  const behaviour = exclusive && 'exclusive';
19
16
 
@@ -58,55 +55,6 @@ export const Checkbox: React.FC<SGDS.Component.Checkbox> = ({
58
55
  );
59
56
  };
60
57
 
61
- /**
62
- * @param {Object} props - Properties for the element
63
- * @param {Array} items - Checkboxes
64
- * @param {boolean} small - Use the small display style for all checkboxes
65
- * @returns {JSX.Element} - The element
66
- */
67
- export const CheckboxGroup: React.FC<SGDS.Component.Checkbox.Group> = ({
68
- className,
69
- items,
70
- small,
71
- ...props
72
- }) => {
73
- const ref = useRef(null);
74
-
75
- useEffect(() => {
76
- if (ref.current) {
77
- new DSCheckboxes(ref.current).init();
78
- }
79
- }, [ref]);
80
-
81
- return (
82
- <div
83
- className={[
84
- 'ds_checkboxes',
85
- 'ds_field-group',
86
- className
87
- ].join(' ')}
88
- data-module="ds-checkboxes"
89
- ref={ref}
90
- {...props}
91
- >
92
- {items && items.map((item, index: number) => (
93
- <Checkbox
94
- exclusive={item.exclusive}
95
- checked={item.checked}
96
- hintText={item.hintText}
97
- id={item.id}
98
- key={'checkbox' + index}
99
- label={item.label}
100
- onBlur={item.onBlur}
101
- onChange={item.onChange}
102
- small={small || item.small}
103
- />
104
- ))}
105
- </div>
106
- )
107
- };
108
-
109
58
  Checkbox.displayName = 'Checkbox';
110
- CheckboxGroup.displayName = 'CheckboxGroup';
111
59
 
112
- export default CheckboxGroup;
60
+ export default Checkbox;
@@ -0,0 +1,37 @@
1
+ import { test, expect } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import Checkbox from './Checkbox';
4
+ import CheckboxGroup from './CheckboxGroup';
5
+
6
+ test('checkbox group renders correctly', () => {
7
+ render(
8
+ <CheckboxGroup data-testid="checkboxgroup">
9
+ <Checkbox id="banana" label="Banana" />
10
+ Foo
11
+ </CheckboxGroup>
12
+ );
13
+ const checkboxGroup = screen.getByTestId('checkboxgroup');
14
+ expect(checkboxGroup).toHaveClass('ds_checkboxes', 'ds_field-group');
15
+
16
+ const checkbox = screen.getByRole('checkbox');
17
+ const checkboxContainer = checkbox.parentElement;
18
+ expect(checkboxContainer).not.toHaveClass('ds_checkbox--small');
19
+ });
20
+
21
+ test('passing additional props', () => {
22
+ render(
23
+ <CheckboxGroup data-testid="checkboxgroup" data-test="foo" />
24
+ );
25
+
26
+ const checkboxGroup = screen.getByTestId('checkboxgroup');
27
+ expect(checkboxGroup?.dataset.test).toEqual('foo');
28
+ });
29
+
30
+ test('passing additional CSS classes', () => {
31
+ render(
32
+ <CheckboxGroup data-testid="checkboxgroup" className="foo" />
33
+ );
34
+
35
+ const checkboxGroup = screen.getByTestId('checkboxgroup');
36
+ expect(checkboxGroup).toHaveClass('foo', 'ds_checkboxes');
37
+ });
@@ -0,0 +1,46 @@
1
+ import React, { Children, useEffect, useRef } from 'react';
2
+ import Checkbox from './Checkbox';
3
+ // @ts-ignore
4
+ import DSCheckboxes from '@scottish-government/design-system/src/forms/checkbox/checkboxes'
5
+
6
+ export const CheckboxGroup = ({
7
+ children,
8
+ className,
9
+ small,
10
+ ...props
11
+ }: SGDS.Component.Checkbox.Group) => {
12
+ function processChild(child: any) {
13
+ if (child && child.type === Checkbox) {
14
+ return React.cloneElement(child as React.ReactElement<SGDS.Component.Checkbox>, { small: small });
15
+ } else {
16
+ return child;
17
+ }
18
+ }
19
+
20
+ const ref = useRef(null);
21
+
22
+ useEffect(() => {
23
+ if (ref.current) {
24
+ new DSCheckboxes(ref.current).init();
25
+ }
26
+ }, [ref]);
27
+
28
+ return (
29
+ <div
30
+ className={[
31
+ 'ds_checkboxes',
32
+ 'ds_field-group',
33
+ className
34
+ ].join(' ')}
35
+ data-module="ds-checkboxes"
36
+ ref={ref}
37
+ {...props}
38
+ >
39
+ {Children.map(children, child => processChild(child))}
40
+ </div>
41
+ )
42
+ };
43
+
44
+ CheckboxGroup.displayName = 'CheckboxGroup';
45
+
46
+ export default CheckboxGroup;
@@ -1,14 +1,14 @@
1
1
  import Icon from '../../common/Icon';
2
2
  import WrapperTag from '../../common/WrapperTag';
3
3
 
4
- const ConfirmationMessage: React.FC<SGDS.Component.ConfirmationMessage> = ({
4
+ const ConfirmationMessage = ({
5
5
  ariaLive = 'polite',
6
6
  children,
7
7
  className,
8
8
  headingLevel = 'h3',
9
9
  title,
10
10
  ...props
11
- }) => {
11
+ }: SGDS.Component.ConfirmationMessage) => {
12
12
  return (
13
13
  <div
14
14
  aria-live={ariaLive}
@@ -1,38 +1,20 @@
1
1
  import { test, expect } from 'vitest';
2
2
  import { render, screen, within } from '@testing-library/react';
3
- import ContentsNav, {Link} from './ContentsNav';
3
+ import ContentsNav from './ContentsNav';
4
4
 
5
5
  const ITEM_HREF = '#foo';
6
6
  const ITEM_TITLE = 'My content';
7
+ const LABEL_TEXT = 'Pages in this guide';
7
8
 
8
9
  test('contents nav renders correctly', () => {
9
- const ITEMS = [
10
- {
11
- title: 'Apply for Blue Badge',
12
- current: true
13
- },
14
- {
15
- title: 'Eligibility',
16
- href: '#2'
17
- },
18
- {
19
- title: 'Using your Blue Badge',
20
- href: '#3'
21
- },
22
- {
23
- title: 'Report a lost, stolen or misuesd Blue Badge',
24
- href: '#4'
25
- },
26
- {
27
- title: 'Changing or handing back a Blue Badge',
28
- href: '#5'
29
- }
30
- ];
31
-
32
- const LABEL_TEXT = 'Pages in this guide';
33
-
34
10
  render(
35
- <ContentsNav label={LABEL_TEXT} items={ITEMS} />
11
+ <ContentsNav ariaLabel={LABEL_TEXT}>
12
+ <ContentsNav.Item>Apply for Blue Badge</ContentsNav.Item>
13
+ <ContentsNav.Item href="#1">Eligibility</ContentsNav.Item>
14
+ <ContentsNav.Item href="#2">Using your Blue Badge</ContentsNav.Item>
15
+ <ContentsNav.Item href="#3">Report a lost, stolen or misuesd Blue Badge</ContentsNav.Item>
16
+ <ContentsNav.Item href="#4">Changing or handing back a Blue Badge</ContentsNav.Item>
17
+ </ContentsNav>
36
18
  )
37
19
 
38
20
  const contentsNav = screen.getByRole('navigation');
@@ -46,14 +28,13 @@ test('contents nav renders correctly', () => {
46
28
  expect(contentsNavTitle.textContent).toEqual('Contents');
47
29
  expect(contentsNavList).toHaveClass('ds_contents-nav__list');
48
30
  expect(contentsNavList.tagName).toEqual('UL');
49
- expect(contentsNavList.children.length).toEqual(ITEMS.length);
50
31
  });
51
32
 
52
33
  test('contents nav with custom title', () => {
53
34
  const TITLE_TEXT = 'My title';
54
35
 
55
36
  render(
56
- <ContentsNav title={TITLE_TEXT} items={[]} />
37
+ <ContentsNav title={TITLE_TEXT} />
57
38
  );
58
39
 
59
40
  const contentsNav = screen.getByRole('navigation');
@@ -63,7 +44,7 @@ test('contents nav with custom title', () => {
63
44
 
64
45
  test('contents nav item', () => {
65
46
  render(
66
- <Link href={ITEM_HREF} title={ITEM_TITLE} />
47
+ <ContentsNav.Item href={ITEM_HREF}>{ITEM_TITLE}</ContentsNav.Item>
67
48
  );
68
49
 
69
50
  const listItem = screen.getByRole('listitem');
@@ -77,51 +58,63 @@ test('contents nav item', () => {
77
58
  expect(link).toHaveAttribute('href', ITEM_HREF);
78
59
  });
79
60
 
80
- test('contents nav current item with href', () => {
61
+ test('contents nav item without href', () => {
81
62
  render(
82
- <Link current href={ITEM_HREF} title={ITEM_TITLE} />
63
+ <ContentsNav.Item>{ITEM_TITLE}</ContentsNav.Item>
83
64
  );
84
65
 
85
66
  const listItem = screen.getByRole('listitem');
86
67
  const link = within(listItem).getByText(ITEM_TITLE);
87
68
 
88
- expect(listItem.ariaCurrent).toEqual('page');
89
69
  expect(link.tagName).toEqual('SPAN');
90
- expect(link).toHaveClass('ds_current');
70
+ expect(link).not.toHaveClass('ds_current');
91
71
  });
92
72
 
93
- test('contents nav current item without href', () => {
73
+ test('current contents nav item with href', () => {
94
74
  render(
95
- <Link current title={ITEM_TITLE} />
75
+ <ContentsNav.Item current href={ITEM_HREF}>{ITEM_TITLE}</ContentsNav.Item>
96
76
  );
97
77
 
98
78
  const listItem = screen.getByRole('listitem');
99
79
  const link = within(listItem).getByText(ITEM_TITLE);
100
80
 
101
- expect(listItem.ariaCurrent).toEqual('page');
102
- expect(link.tagName).toEqual('SPAN');
81
+ expect(link.tagName).toEqual('A');
103
82
  expect(link).toHaveClass('ds_current');
104
83
  });
105
84
 
106
- test('contents nav item without href', () => {
85
+ test('current contents nav item without href', () => {
107
86
  render(
108
- <Link title={ITEM_TITLE} />
87
+ <ContentsNav.Item current>{ITEM_TITLE}</ContentsNav.Item>
109
88
  );
110
89
 
111
90
  const listItem = screen.getByRole('listitem');
112
91
  const link = within(listItem).getByText(ITEM_TITLE);
113
92
 
114
93
  expect(link.tagName).toEqual('SPAN');
115
- expect(link).not.toHaveClass('ds_current');
94
+ expect(link).toHaveClass('ds_current');
95
+ });
96
+
97
+ test('contents nav item with custom element', () => {
98
+ render(
99
+ <ContentsNav.Item href="category" linkComponent={
100
+ ({ className, ...props }) => (
101
+ <strong role="link" className={className} {...props}/>
102
+ )}>
103
+ {ITEM_TITLE}
104
+ </ContentsNav.Item>
105
+ );
106
+
107
+
108
+ const item = screen.getByRole('listitem');
109
+ const link = within(item).queryByRole('link');
110
+
111
+ expect(link?.tagName).toEqual('STRONG');
112
+ expect(link?.textContent).toEqual(ITEM_TITLE);
116
113
  });
117
114
 
118
115
  test('passing additional props', () => {
119
116
  render(
120
- <ContentsNav data-test="foo" items={[
121
- {
122
- title: 'Apply for Blue Badge',
123
- }
124
- ]} />
117
+ <ContentsNav data-test="foo"/>
125
118
  )
126
119
 
127
120
  const contentsNav = screen.getByRole('navigation');
@@ -130,11 +123,7 @@ test('passing additional props', () => {
130
123
 
131
124
  test('passing additional CSS classes', () => {
132
125
  render(
133
- <ContentsNav className="foo" items={[
134
- {
135
- title: 'Apply for Blue Badge',
136
- }
137
- ]} />
126
+ <ContentsNav className="foo"/>
138
127
  )
139
128
 
140
129
  const contentsNav = screen.getByRole('navigation');
@@ -1,42 +1,48 @@
1
- import WrapperTag from '../../common/WrapperTag';
1
+ import React from 'react';
2
2
 
3
- export const Link: React.FC<SGDS.Component.ContentsNav.Link> = ({
3
+ const ContentsNavItem = ({
4
+ children,
4
5
  current,
5
6
  href,
6
- title
7
- }) => {
8
- // determine which HTML tag to use
9
- const tagName = href && !current ? 'a' : 'span';
7
+ linkComponent
8
+ }: SGDS.Component.ContentsNav.ContentsNavItem) => {
9
+ const classNames = ['ds_contents-nav__link'];
10
+ let ariaCurrent: React.AriaAttributes["aria-current"];
11
+
12
+ if (current) {
13
+ classNames.push('ds_current');
14
+ ariaCurrent = 'page';
15
+ }
16
+
17
+ function processChildren(children: React.ReactNode) {
18
+ if (linkComponent) {
19
+ return linkComponent({ className: classNames.join(' '), href, children });
20
+ } else if (href) {
21
+ return <a href={href} aria-current={ariaCurrent ? ariaCurrent : undefined} className={classNames.join(' ')}>{children}</a>;
22
+ } else {
23
+ return <span aria-current={ariaCurrent ? ariaCurrent : undefined} className={classNames.join(' ')}>{children}</span>;
24
+ }
25
+ }
10
26
 
11
27
  return (
12
28
  <li
13
- aria-current={current && 'page' || undefined}
14
29
  className="ds_contents-nav__item"
15
30
  >
16
- <WrapperTag
17
- tagName={tagName}
18
- className={[
19
- 'ds_contents-nav__link',
20
- current && 'ds_current'
21
- ].join(' ')}
22
- href={!current ? href : undefined}
23
- >
24
- {title}
25
- </WrapperTag>
31
+ {processChildren(children)}
26
32
  </li>
27
33
  );
28
34
  };
29
35
 
30
- const ContentsNav: React.FC<SGDS.Component.ContentsNav> = function ({
36
+ const ContentsNav = ({
37
+ ariaLabel = 'Pages in this section',
38
+ children,
31
39
  className,
32
- items,
33
- label = 'Pages in this section',
34
40
  title = 'Contents',
35
41
  ...props
36
- }) {
42
+ }: SGDS.Component.ContentsNav) => {
37
43
  return (
38
44
  <nav
39
- aria-label={label}
45
+ aria-label={ariaLabel}
40
46
  className={[
41
47
  'ds_contents-nav',
42
48
  className
@@ -44,15 +50,16 @@ const ContentsNav: React.FC<SGDS.Component.ContentsNav> = function ({
44
50
  {...props}
45
51
  >
46
52
  <h2 className="ds_contents-nav__title">{title}</h2>
53
+
47
54
  <ul className="ds_contents-nav__list">
48
- {items && items.map((item, index: number) => (
49
- <Link current={item.current} href={item.href} title={item.title} key={'link' + index} />
50
- ))}
55
+ {children}
51
56
  </ul>
52
57
  </nav>
53
58
  );
54
59
  };
55
60
 
56
61
  ContentsNav.displayName = 'ContentsNav';
62
+ ContentsNavItem.displayName = 'ContentsNav.Item';
63
+ ContentsNav.Item = ContentsNavItem;
57
64
 
58
65
  export default ContentsNav;
@@ -3,13 +3,12 @@ import AbstractNotificationBanner from '../../common/AbstractNotificationBanner'
3
3
  // @ts-ignore
4
4
  import DSCookieBanner from '@scottish-government/design-system/src/components/cookie-notification/cookie-notification.js';
5
5
 
6
- const CookieBanner: React.FC<SGDS.Common.AbstractNotificationBanner>
7
- & { Buttons?: React.FC<SGDS.Common.AbstractNotificationBanner.Buttons> } = ({
6
+ const CookieBanner = ({
8
7
  children,
9
8
  className,
10
9
  title,
11
10
  ...props
12
- }) => {
11
+ }: SGDS.Common.AbstractNotificationBanner) => {
13
12
 
14
13
  return (
15
14
  <>
@@ -32,5 +31,6 @@ const CookieBanner: React.FC<SGDS.Common.AbstractNotificationBanner>
32
31
 
33
32
  CookieBanner.displayName = 'CookieBanner';
34
33
  CookieBanner.Buttons = AbstractNotificationBanner.Buttons;
34
+ CookieBanner.Buttons.displayName = 'CookieBanner.Buttons';
35
35
 
36
36
  export default CookieBanner;
@@ -211,8 +211,8 @@ test('date picker with error message', () => {
211
211
  <DatePicker
212
212
  id={DATE_PICKER_ID}
213
213
  label={LABEL_TEXT}
214
- error
215
214
  errorMessage={ERROR_MESSAGE_TEXT}
215
+ hasError
216
216
  />
217
217
  );
218
218
 
@@ -4,12 +4,12 @@ import DSDatePicker from '@scottish-government/design-system/src/components/date
4
4
  import ErrorMessage from '../ErrorMessage/ErrorMessage';
5
5
  import TextInput from '../TextInput/TextInput';
6
6
 
7
- const DatePicker: React.FC<SGDS.Component.DatePicker> = ({
7
+ const DatePicker = ({
8
8
  className,
9
9
  dateSelectCallback,
10
10
  disabledDates,
11
- error,
12
11
  errorMessage,
12
+ hasError,
13
13
  hintText,
14
14
  id,
15
15
  iconPath = './',
@@ -23,7 +23,7 @@ const DatePicker: React.FC<SGDS.Component.DatePicker> = ({
23
23
  value,
24
24
  width = 'fixed-10',
25
25
  ...props
26
- }) => {
26
+ }: SGDS.Component.DatePicker) => {
27
27
  const ref = useRef(null);
28
28
 
29
29
  useEffect(() => {
@@ -68,7 +68,7 @@ const DatePicker: React.FC<SGDS.Component.DatePicker> = ({
68
68
  <div>
69
69
  <TextInput
70
70
  className="js-datepicker-date"
71
- error={!!error}
71
+ hasError={!!hasError}
72
72
  id={id + "-day"}
73
73
  hintText={hintText}
74
74
  label="Day"
@@ -83,7 +83,7 @@ const DatePicker: React.FC<SGDS.Component.DatePicker> = ({
83
83
  <div>
84
84
  <TextInput
85
85
  className="js-datepicker-month"
86
- error={!!error}
86
+ hasError={!!hasError}
87
87
  id={id + "-month"}
88
88
  hintText={hintText}
89
89
  label="Month"
@@ -98,7 +98,7 @@ const DatePicker: React.FC<SGDS.Component.DatePicker> = ({
98
98
  <div>
99
99
  <TextInput
100
100
  className="js-datepicker-year"
101
- error={!!error}
101
+ hasError={!!hasError}
102
102
  id={id + "-year"}
103
103
  hintText={hintText}
104
104
  label="Year"
@@ -112,7 +112,7 @@ const DatePicker: React.FC<SGDS.Component.DatePicker> = ({
112
112
  </fieldset>
113
113
  ) : (
114
114
  <TextInput
115
- error={!!error}
115
+ hasError={!!hasError}
116
116
  errorMessage={errorMessage}
117
117
  id={id}
118
118
  hasButton
@@ -1,9 +1,9 @@
1
- const Details: React.FC<SGDS.Component.Details> = ({
1
+ const Details = ({
2
2
  children,
3
3
  className,
4
4
  summary,
5
5
  ...props
6
- }) => {
6
+ }: SGDS.Component.Details) => {
7
7
  return (
8
8
  <details
9
9
  className={[
@@ -1,10 +1,10 @@
1
- const ErrorMessage: React.FC<SGDS.Component.ErrorMessage> = ({
1
+ const ErrorMessage = ({
2
2
  children,
3
3
  className,
4
4
  id,
5
5
  text,
6
6
  ...props
7
- }) => {
7
+ }: SGDS.Component.ErrorMessage) => {
8
8
  return (
9
9
  <p
10
10
  className={[