@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,69 +1,39 @@
1
1
  import { test, expect, vi } from 'vitest';
2
- import { render, screen, fireEvent } from '@testing-library/react';
3
- import SummaryList, { Item, Action, Answer } from './SummaryList';
2
+ import { within, render, screen, fireEvent } from '@testing-library/react';
3
+ import SummaryList from './SummaryList';
4
4
 
5
5
  const ONCLICK_FUNCTION = vi.fn();
6
+ const TITLE = 'Name';
7
+ const VALUE_1 = 'Jane Smith';
8
+ const VALUE_2 = 'Tony Brown';
6
9
 
7
- const SUMMARY_ITEMS = [
8
- {
9
- title: 'Name',
10
- value: 'Jane Smith',
11
- actions: [
12
- {
13
- title: 'Change',
14
- href: '#foo'
15
- },
16
- {
17
- title: 'Delete',
18
- onclick: ONCLICK_FUNCTION
19
- }
20
- ]
21
- },
22
- {
23
- title: 'Date of birth',
24
- value: '13 April 2001',
25
- actions: [
26
- {
27
- title: 'Change',
28
- href: '#bar'
29
- }
30
- ]
31
- },
32
- {
33
- title: 'Address',
34
- value: `Scottish Government\nSt Andrew's House\nRegent Road\nEdinburgh\nEH1 3DG`,
35
- actions: [
36
- {
37
- title: 'Change',
38
- href: '#baz'
39
- }
40
- ]
41
- },
42
- {
43
- title: 'Contact details',
44
- value: [
45
- 'email@gov.scot',
46
- '0123 456 7890',
47
- ]
48
- }
49
- ];
10
+ const ACTION_HREF = "#foo"
11
+ const ACTION_ONCLICK = ONCLICK_FUNCTION;
12
+ const ACTION_TEXT = 'Name';
13
+ const DESCRIBEDBY_ID = 'q1-name';
50
14
 
51
15
  test('summary list renders correctly', () => {
52
16
  render(
53
- <SummaryList items={SUMMARY_ITEMS} />
17
+ <SummaryList data-testid="summarylist">
18
+ <SummaryList.Item title="key">
19
+ <SummaryList.Value>value</SummaryList.Value>
20
+ <SummaryList.Value>value2</SummaryList.Value>
21
+ <SummaryList.Value>value3</SummaryList.Value>
22
+ <SummaryList.Action href="foo">foo</SummaryList.Action>
23
+ <SummaryList.Action onclick={() => true}>bar</SummaryList.Action>
24
+ </SummaryList.Item>
25
+ </SummaryList>
54
26
  );
55
27
 
56
- const summaryList = screen.getAllByRole('list')[0];
57
- const summaryListItems = summaryList.children;
28
+ const summaryList = screen.getByTestId('summarylist');
58
29
 
59
30
  expect(summaryList).toHaveClass('ds_summary-list');
60
31
  expect(summaryList.tagName).toEqual('OL');
61
- expect(summaryListItems.length).toEqual(SUMMARY_ITEMS.length);
62
32
  });
63
33
 
64
34
  test('summary list without border', () => {
65
35
  render(
66
- <SummaryList noBorder items={SUMMARY_ITEMS} />
36
+ <SummaryList noBorder/>
67
37
  );
68
38
 
69
39
  const summaryList = screen.getAllByRole('list')[0];
@@ -72,14 +42,11 @@ test('summary list without border', () => {
72
42
  });
73
43
 
74
44
  test('summary list item renders correctly', () => {
75
- const DESCRIBEDBY_ID = 'q2-date-of-birth';
76
-
77
45
  render(
78
- <Item
79
- actions={SUMMARY_ITEMS[1].actions}
80
- title={SUMMARY_ITEMS[1].title}
81
- value={SUMMARY_ITEMS[1].value}
82
- />
46
+ <SummaryList.Item title={TITLE}>
47
+ <SummaryList.Value>{VALUE_1}</SummaryList.Value>
48
+ <SummaryList.Action></SummaryList.Action>
49
+ </SummaryList.Item>
83
50
  );
84
51
 
85
52
  const item = screen.getAllByRole('listitem')[0];
@@ -91,8 +58,7 @@ test('summary list item renders correctly', () => {
91
58
  expect(item).toHaveClass('ds_summary-list__item');
92
59
 
93
60
  expect(key?.tagName).toEqual('SPAN');
94
- expect(key?.textContent).toEqual('Date of birth');
95
- expect(key).toHaveAttribute('id', DESCRIBEDBY_ID);
61
+ expect(key?.textContent).toEqual(TITLE);
96
62
 
97
63
  expect(value?.tagName).toEqual('SPAN');
98
64
 
@@ -101,16 +67,15 @@ test('summary list item renders correctly', () => {
101
67
 
102
68
  expect(actions?.tagName).toEqual('DIV');
103
69
 
104
- expect(actions?.querySelector('.ds_link')).toHaveAttribute('aria-describedby', DESCRIBEDBY_ID);
70
+ expect(actions?.querySelector('.ds_link')).toHaveAttribute('aria-describedby', key?.id);
105
71
  });
106
72
 
107
73
  test('summary list item with multiple values', () => {
108
74
  render(
109
- <Item
110
- actions={SUMMARY_ITEMS[3].actions}
111
- title={SUMMARY_ITEMS[3].title}
112
- value={SUMMARY_ITEMS[3].value}
113
- />
75
+ <SummaryList.Item title={TITLE}>
76
+ <SummaryList.Value>{VALUE_1}</SummaryList.Value>
77
+ <SummaryList.Value>{VALUE_2}</SummaryList.Value>
78
+ </SummaryList.Item>
114
79
  );
115
80
 
116
81
  const item = screen.getAllByRole('listitem')[0];
@@ -121,30 +86,23 @@ test('summary list item with multiple values', () => {
121
86
  expect(valueList).toHaveClass('ds_no-bullets');
122
87
  expect(valueList?.tagName).toEqual('UL');
123
88
 
124
- expect(valueListItems?.length).toEqual(SUMMARY_ITEMS[3].value.length);
125
-
126
89
  expect(valueListItems[0].tagName).toEqual('LI');
127
- expect(valueListItems[0].innerHTML).toEqual(`<q class="ds_summary-list__answer">${SUMMARY_ITEMS[3].value[0]}</q>`);
90
+ expect(valueListItems[0].innerHTML).toEqual(`<q class="ds_summary-list__answer">${VALUE_1}</q>`);
128
91
  });
129
92
 
130
93
  test('summary list item with no value', () => {
131
94
  render(
132
- <Item
133
- actions={SUMMARY_ITEMS[3].actions}
134
- title={SUMMARY_ITEMS[3].title}
135
- />
95
+ <SummaryList.Item title={TITLE}>
96
+ </SummaryList.Item>
136
97
  );
137
98
 
138
99
  const item = screen.getAllByRole('listitem')[0];
139
100
  const value = item.querySelector('.ds_summary-list__value');
140
- const answer = value?.children[0];
141
101
 
142
- expect(answer?.textContent).toEqual('');
102
+ expect(value?.textContent).toEqual('');
143
103
  });
144
104
 
145
105
  test('summary list item with multiple actions', () => {
146
- const ITEM_TITLE = 'Name';
147
- const ITEM_VALUE = 'Jane Smith';
148
106
  const ITEM_ACTIONS = [
149
107
  {
150
108
  title: 'Change',
@@ -157,87 +115,30 @@ test('summary list item with multiple actions', () => {
157
115
  ];
158
116
 
159
117
  render(
160
- <Item
161
- actions={SUMMARY_ITEMS[0].actions}
162
- title={ITEM_TITLE}
163
- value={ITEM_VALUE}
164
- />
118
+ <SummaryList.Item title={TITLE}>
119
+ <SummaryList.Value>{VALUE_1}</SummaryList.Value>
120
+ <SummaryList.Action href={ITEM_ACTIONS[0].href}>{ITEM_ACTIONS[0].title}</SummaryList.Action>
121
+ <SummaryList.Action onclick={ITEM_ACTIONS[0].onclick}>{ITEM_ACTIONS[1].title}</SummaryList.Action>
122
+ </SummaryList.Item>
165
123
  );
166
124
 
167
125
  const item = screen.getAllByRole('listitem')[0];
168
126
  const actionsElement = item.querySelector('.ds_summary-list__actions');
127
+ const actionsList = within(actionsElement as HTMLElement).getByRole('list')
169
128
 
170
- expect(actionsElement?.children.length).toEqual(ITEM_ACTIONS.length);
171
- expect(actionsElement?.children[0].textContent).toEqual(ITEM_ACTIONS[0].title);
172
- expect(actionsElement?.children[1].textContent).toEqual(ITEM_ACTIONS[1].title);
173
- });
174
-
175
- test('button action', () => {
176
- const DESCRIBEDBY_ID = 'q1-name';
177
- const ACTION_TITLE = 'Name';
178
- const ACTION_ONCLICK = ONCLICK_FUNCTION;
179
-
180
- render(
181
- <Action
182
- describedby={DESCRIBEDBY_ID}
183
- href={undefined}
184
- onclick={ACTION_ONCLICK}
185
- title={ACTION_TITLE}
186
- />
187
- );
188
-
189
- const action = screen.getByRole('button');
190
-
191
- expect(action).toHaveClass('ds_link');
192
- expect(action).toHaveAttribute('aria-describedby', DESCRIBEDBY_ID);
193
- expect(action).toHaveAttribute('type', 'button');
194
- expect(action).not.toHaveAttribute('href');
195
- expect(action.tagName).toEqual('BUTTON');
196
- expect(action.textContent).toEqual(ACTION_TITLE);
197
-
198
- fireEvent.click(action);
199
-
200
- expect(ONCLICK_FUNCTION).toHaveBeenCalled();
201
- });
202
-
203
- test('link action', () => {
204
- const DESCRIBEDBY_ID = 'q1-name';
205
- const ACTION_TITLE = 'Name';
206
- const ACTION_HREF = "#foo"
207
- const ACTION_ONCLICK = ONCLICK_FUNCTION;
208
-
209
- render(
210
- <Action
211
- describedby={DESCRIBEDBY_ID}
212
- href={ACTION_HREF}
213
- onclick={ACTION_ONCLICK}
214
- title={ACTION_TITLE}
215
- />
216
- );
217
-
218
- const action = screen.getByRole('link');
219
-
220
- expect(action).toHaveClass('ds_link');
221
- expect(action).toHaveAttribute('aria-describedby', DESCRIBEDBY_ID);
222
- expect(action).toHaveAttribute('href', ACTION_HREF);
223
- expect(action).not.toHaveAttribute('type');
224
- expect(action.tagName).toEqual('A');
225
- expect(action.textContent).toEqual(ACTION_TITLE);
226
- });
227
-
228
- test('multiline answer', () => {
229
- render(
230
- <Answer value={SUMMARY_ITEMS[2].value} />
231
- );
232
-
233
- const answer = document.querySelector('.ds_summary-list__answer');
129
+ expect(actionsList).toHaveClass('ds_summary-list__actions-list');
130
+ expect(actionsList.tagName).toEqual('UL');
234
131
 
235
- expect(answer?.outerHTML).toEqual('<q class="ds_summary-list__answer">Scottish Government<br>St Andrew\'s House<br>Regent Road<br>Edinburgh<br>EH1 3DG</q>');
132
+ expect(actionsList?.children.length).toEqual(2);
133
+ expect(actionsList?.children[0]).toHaveClass('ds_summary-list__actions-list-item');
134
+ expect(actionsList?.children[0].tagName).toEqual('LI');
135
+ expect(actionsList?.children[0].textContent).toEqual(ITEM_ACTIONS[0].title);
136
+ expect(actionsList?.children[1].textContent).toEqual(ITEM_ACTIONS[1].title);
236
137
  });
237
138
 
238
139
  test('passing additional props', () => {
239
140
  render(
240
- <SummaryList data-test="foo" items={SUMMARY_ITEMS} />
141
+ <SummaryList data-test="foo" />
241
142
  );
242
143
 
243
144
  const summaryList = screen.getAllByRole('list')[0];
@@ -246,7 +147,7 @@ test('passing additional props', () => {
246
147
 
247
148
  test('passing additional CSS classes', () => {
248
149
  render(
249
- <SummaryList className="foo" items={SUMMARY_ITEMS} />
150
+ <SummaryList className="foo" />
250
151
  );
251
152
 
252
153
  const summaryList = screen.getAllByRole('list')[0];
@@ -1,71 +1,25 @@
1
- import ConditionalWrapper from '../../common/ConditionalWrapper';
2
- import WrapperTag from '../../common/WrapperTag';
3
-
4
- function escapedNewLineToLineBreakTag (string: string) {
5
- if (typeof string === 'string') {
6
- // @ts-ignore
7
- return string.split('\n').map((item, index) => {
8
- return (index === 0) ? item : [<br key={index} />, item]
9
- });
10
- }
11
- }
12
-
13
- function convertToSlug(string: string) {
14
- return string.toLowerCase()
15
- .replace(/[^\w ]+/g, "")
16
- .replace(/ +/g, "-");
17
- }
1
+ import React, { Children, useId } from 'react';
18
2
 
19
- export const Answer: React.FC<SGDS.Component.SummaryList.Answer> = ({
20
- value
21
- }) => {
22
- const processedValue = escapedNewLineToLineBreakTag(value.toString());
23
-
24
- return (
25
- <q className="ds_summary-list__answer">{processedValue}</q>
26
- );
27
- };
28
-
29
- export const Action: React.FC<SGDS.Component.SummaryList.Action> = ({
30
- describedby,
31
- href,
32
- onclick,
33
- title,
34
- }) => {
35
- let tagName = 'button';
36
- if (href) {
37
- tagName = 'a';
38
- }
39
-
40
- return (
41
- <WrapperTag
42
- aria-describedby={describedby}
43
- className="ds_link"
44
- href={href}
45
- onClick={onclick}
46
- tagName={tagName}
47
- type={tagName === 'button' ? 'button' : undefined}
48
- >
49
- {title}
50
- </WrapperTag>
51
- );
52
- };
3
+ import ActionLink from '../../common/ActionLink';
4
+ import ConditionalWrapper from '../../common/ConditionalWrapper';
53
5
 
54
- export const Item: React.FC<SGDS.Component.SummaryList.Item> = ({
55
- actions,
56
- index = 1,
57
- title,
58
- value
59
- }) => {
60
- let values: string[] = [];
6
+ const Item = ({
7
+ children,
8
+ title
9
+ }: SGDS.Component.SummaryList.Item) => {
10
+ let values: any[] = [];
11
+ let actions: any[] = [];
61
12
 
62
- if (Array.isArray(value)) {
63
- values = value;
64
- } else {
65
- values = [value || ''];
66
- }
13
+ const describedById = useId();
67
14
 
68
- const describedById = `q${index+1}-${convertToSlug(title)}`;
15
+ Children.forEach(children, (child: React.ReactNode) => {
16
+ const thisChild = child as React.ReactElement<SGDS.Common.ActionLink>;
17
+ if (thisChild && thisChild.type === Value) {
18
+ values.push(thisChild);
19
+ } else if (thisChild && thisChild.type === ActionLink) {
20
+ actions.push(React.cloneElement(thisChild, { describedby: describedById }));
21
+ }
22
+ });
69
23
 
70
24
  return (
71
25
  <li className="ds_summary-list__item">
@@ -75,42 +29,53 @@ export const Item: React.FC<SGDS.Component.SummaryList.Item> = ({
75
29
  condition={values.length > 1}
76
30
  wrapper={(children: React.JSX.Element) => <ul className="ds_no-bullets">{children}</ul>}
77
31
  >
78
- {values && values.map((value, index: number) => (
32
+ {values && values.map((value, index: number) => (
79
33
  <ConditionalWrapper
80
34
  condition={values.length > 1}
81
35
  wrapper={(children: React.JSX.Element) => <li>{children}</li>}
82
36
  key={'answer' + index}
83
37
  >
84
- <Answer
85
- value={value}
86
- />
38
+ {value}
87
39
  </ConditionalWrapper>
88
40
  ))}
89
41
  </ConditionalWrapper>
90
42
  </span>
91
43
  {actions &&
92
44
  <div className="ds_summary-list__actions">
93
- {actions && actions.map((action, index: number) => (
94
- <Action
95
- describedby={describedById}
96
- href={action.href}
97
- onclick={action.onclick}
98
- title={action.title}
99
- key={'summarylistaction' + index}
100
- />
101
- ))}
45
+ <ConditionalWrapper
46
+ condition={actions.length > 1}
47
+ wrapper={(children: React.JSX.Element) => <ul className="ds_summary-list__actions-list">{children}</ul>}
48
+ >
49
+ {actions && actions.map((action, index: number) => (
50
+ <ConditionalWrapper
51
+ condition={actions.length > 1}
52
+ wrapper={(children: React.JSX.Element) => <li className="ds_summary-list__actions-list-item">{children}</li>}
53
+ key={'action' + index}
54
+ >
55
+ {action}
56
+ </ConditionalWrapper>
57
+ ))}
58
+ </ConditionalWrapper>
102
59
  </div>
103
60
  }
104
61
  </li>
105
62
  );
106
63
  };
107
64
 
108
- const SummaryList: React.FC<SGDS.Component.SummaryList> = ({
65
+ const Value = ({
66
+ children
67
+ }: SGDS.Component.SummaryList.Answer) => {
68
+ return (
69
+ <q className="ds_summary-list__answer">{children}</q>
70
+ );
71
+ };
72
+
73
+ const SummaryList = ({
74
+ children,
109
75
  className,
110
- items,
111
76
  noBorder,
112
77
  ...props
113
- }) => {
78
+ }: SGDS.Component.SummaryList) => {
114
79
  return (
115
80
  <ol
116
81
  className={[
@@ -120,22 +85,19 @@ const SummaryList: React.FC<SGDS.Component.SummaryList> = ({
120
85
  ].join(' ')}
121
86
  {...props}
122
87
  >
123
- {items && items.map((item, index: number) => (
124
- <Item
125
- actions={item.actions}
126
- title={item.title}
127
- value={item.value}
128
- index={index}
129
- key={'summarylistitem' + index}
130
- />
131
- ))}
88
+ {children}
132
89
  </ol>
133
90
  );
134
91
  };
135
92
 
93
+
94
+ SummaryList.Action = ActionLink;
95
+ SummaryList.Item = Item;
96
+ SummaryList.Value = Value;
97
+
136
98
  SummaryList.displayName = 'SummaryList';
137
- Action.displayName = 'SummaryListAction';
138
- Answer.displayName = 'SumaryListAnswer';
139
- Item.displayName = 'SummaryListItem';
99
+ SummaryList.Action.displayName = 'SummaryList.Action';
100
+ Item.displayName = 'SummaryList.Item';
101
+ Value.displayName = 'SumaryList.Value';
140
102
 
141
103
  export default SummaryList;
@@ -2,12 +2,12 @@ import { useEffect, useRef } from 'react';
2
2
  // @ts-ignore
3
3
  import DSTable from '@scottish-government/design-system/src/components/table/table';
4
4
 
5
- const Table: React.FC<SGDS.Component.Table> = ({
5
+ const Table = ({
6
6
  children,
7
7
  className,
8
8
  smallscreen,
9
9
  ...props
10
- }) => {
10
+ }: SGDS.Component.Table) => {
11
11
  const ref = useRef(null);
12
12
 
13
13
  useEffect(() => {
@@ -1,17 +1,17 @@
1
- import React, { isValidElement, Children, useEffect, useRef } from 'react';
1
+ import React, { Children, useEffect, useRef } from 'react';
2
2
  import WrapperTag from '../../common/WrapperTag';
3
3
  // @ts-ignore
4
4
  import DSTabs from '@scottish-government/design-system/src/components/tabs/tabs';
5
5
  import slugify from '../../utils/slugify';
6
6
 
7
- const TabItem: React.FC<SGDS.Component.Tabs.Item> = ({
7
+ const TabItem = ({
8
8
  bordered,
9
9
  children,
10
10
  className,
11
11
  id,
12
12
  tabLabel,
13
13
  ...props
14
- }) => {
14
+ }: SGDS.Component.Tabs.Item) => {
15
15
  return (
16
16
  <div
17
17
  className={[
@@ -27,19 +27,18 @@ const TabItem: React.FC<SGDS.Component.Tabs.Item> = ({
27
27
  );
28
28
  };
29
29
 
30
- const TabListItem: React.FC<SGDS.Component.Tabs.TabListItem> = ({
31
- title,
30
+ const TabListItem = ({
31
+ children,
32
32
  href
33
- }) => {
33
+ }: SGDS.Component.Tabs.TabListItem) => {
34
34
  return (
35
35
  <li className="ds_tabs__tab">
36
- <a className="ds_tabs__tab-link" href={href}>{title}</a>
36
+ <a className="ds_tabs__tab-link" href={href}>{children}</a>
37
37
  </li>
38
38
  );
39
39
  }
40
40
 
41
- const Tabs: React.FC<SGDS.Component.Tabs>
42
- & { Item: React.FC<SGDS.Component.Tabs.Item> } = ({
41
+ const Tabs = ({
43
42
  baseId = 'tabs',
44
43
  bordered = true,
45
44
  children,
@@ -48,7 +47,7 @@ const Tabs: React.FC<SGDS.Component.Tabs>
48
47
  manual = false,
49
48
  title = 'Contents',
50
49
  ...props
51
- }) => {
50
+ }: SGDS.Component.Tabs) => {
52
51
  const ref = useRef(null);
53
52
 
54
53
  const headingId = `${baseId}-heading`;
@@ -60,10 +59,10 @@ const Tabs: React.FC<SGDS.Component.Tabs>
60
59
  }, [ref]);
61
60
 
62
61
  const processedItems = Children.map(children, child => {
63
- if (isValidElement(child) && child.type === TabItem) {
64
- let thisChild = child as React.ReactElement<SGDS.Component.Tabs.Item>;
62
+ const thisChild = child as React.ReactElement<SGDS.Component.Tabs.Item>;
65
63
 
66
- return React.cloneElement(thisChild as React.ReactElement<SGDS.Component.Tabs.Item>, {
64
+ if (thisChild && thisChild.type === TabItem) {
65
+ return React.cloneElement(thisChild, {
67
66
  bordered: bordered,
68
67
  id: thisChild.props.id || `${baseId}-${slugify(thisChild.props.tabLabel)}`,
69
68
  });
@@ -71,8 +70,8 @@ const Tabs: React.FC<SGDS.Component.Tabs>
71
70
  });
72
71
 
73
72
  const tabListItems = Children.map(processedItems, child => {
74
- if (isValidElement(child) && child.type === TabItem) {
75
- return React.createElement(TabListItem, { title: child.props.tabLabel, href: `#${child.props.id}` });
73
+ if (child && child.type === TabItem) {
74
+ return <TabListItem href={`#${child.props.id}`}>{child.props.tabLabel}</TabListItem>
76
75
  }
77
76
  });
78
77
 
@@ -6,7 +6,7 @@ const TAG_TEXT = 'Beta';
6
6
 
7
7
  test('tag renders correctly', () => {
8
8
  render(
9
- <Tag title={TAG_TEXT}/>
9
+ <Tag>{TAG_TEXT}</Tag>
10
10
  );
11
11
 
12
12
  const tag = screen.getByText(TAG_TEXT);
@@ -17,7 +17,7 @@ test('tag renders correctly', () => {
17
17
 
18
18
  test('tag with custom colour', () => {
19
19
  render(
20
- <Tag colour="red" title={TAG_TEXT}/>
20
+ <Tag colour="red">{TAG_TEXT}</Tag>
21
21
  );
22
22
 
23
23
  const tag = screen.getByText(TAG_TEXT);
@@ -27,7 +27,7 @@ test('tag with custom colour', () => {
27
27
 
28
28
  test('passing additional props', () => {
29
29
  render(
30
- <Tag data-test="foo" title={TAG_TEXT}/>
30
+ <Tag data-test="foo">{TAG_TEXT}</Tag>
31
31
  );
32
32
 
33
33
  const tag = screen.getByText(TAG_TEXT);
@@ -36,7 +36,7 @@ test('passing additional props', () => {
36
36
 
37
37
  test('tag with additional CSS class', () => {
38
38
  render(
39
- <Tag className="foo" title={TAG_TEXT}/>
39
+ <Tag className="foo">{TAG_TEXT}</Tag>
40
40
  );
41
41
 
42
42
  const tag = screen.getByText(TAG_TEXT);
@@ -1,9 +1,9 @@
1
- const Tag: React.FC<SGDS.Component.Tag> = ({
1
+ const Tag = ({
2
+ children,
2
3
  className,
3
4
  colour,
4
- title,
5
5
  ...props
6
- }) => {
6
+ }: SGDS.Component.Tag) => {
7
7
  return (
8
8
  <span
9
9
  className={[
@@ -13,7 +13,7 @@ const Tag: React.FC<SGDS.Component.Tag> = ({
13
13
  ].join(' ')}
14
14
  {...props}
15
15
  >
16
- {title}
16
+ {children}
17
17
  </span>
18
18
  );
19
19
  };
@@ -192,6 +192,32 @@ test('task with link', () => {
192
192
  expect(link.textContent).toEqual(taskHeading.textContent);
193
193
  });
194
194
 
195
+ test('task with custom link element', () => {
196
+ const TASK_HREF = '#foo';
197
+
198
+ render(
199
+ <TaskList.Item
200
+ id={TASK_ITEM.id}
201
+ statusText={TASK_ITEM.statusText}
202
+ title={TASK_ITEM.title}
203
+ href={TASK_HREF}
204
+ linkComponent={
205
+ ({ className, ...props }) => (
206
+ <strong role="link" className={className} {...props}/>
207
+ )}
208
+ >
209
+ {TASK_SUMMARY_CONTENT}
210
+ </TaskList.Item>
211
+ );
212
+
213
+ const task = screen.getByRole('listitem');
214
+ const taskHeading = within(task).getByRole('heading');
215
+ const link = within(task).getByRole('link');
216
+
217
+ expect(link?.tagName).toEqual('STRONG');
218
+ expect(link?.textContent).toEqual(taskHeading.textContent);
219
+ });
220
+
195
221
  test('completed task has green tag', () => {
196
222
  render(
197
223
  <TaskList.Item