@scottish-government/designsystem-react 0.3.0 → 0.5.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 (62) hide show
  1. package/@types/common/AbstractNotificationBanner.d.ts +17 -0
  2. package/@types/components/Table.d.ts +8 -0
  3. package/@types/components/Tabs.d.ts +22 -0
  4. package/@types/components/TaskList.d.ts +1 -1
  5. package/dist/common/abstract-notification-banner.jsx +63 -0
  6. package/dist/components/accordion/accordion.jsx +2 -3
  7. package/dist/components/breadcrumbs/breadcrumbs.jsx +2 -2
  8. package/dist/components/cookie-banner/cookie-banner.jsx +21 -0
  9. package/dist/components/notification-banner/notification-banner.jsx +7 -34
  10. package/dist/components/page-metadata/page-metadata.jsx +2 -3
  11. package/dist/components/table/table.jsx +24 -0
  12. package/dist/components/tabs/tabs.jsx +99 -0
  13. package/dist/components/task-list/task-list.jsx +6 -7
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/dist/utils/slugify.js +15 -0
  16. package/package.json +1 -1
  17. package/src/common/abstract-notification-banner.test.tsx +126 -0
  18. package/src/common/abstract-notification-banner.tsx +87 -0
  19. package/src/common/conditional-wrapper.test.tsx +1 -1
  20. package/src/common/screen-reader-text.test.tsx +1 -1
  21. package/src/common/wrapper-tag.test.tsx +3 -3
  22. package/src/components/accordion/accordion.test.tsx +40 -40
  23. package/src/components/accordion/accordion.tsx +4 -2
  24. package/src/components/aspect-box/aspect-box.test.tsx +7 -7
  25. package/src/components/breadcrumbs/breadcrumbs.tsx +0 -2
  26. package/src/components/checkbox/checkbox.test.tsx +8 -8
  27. package/src/components/confirmation-message/confirmation-message.test.tsx +1 -1
  28. package/src/components/cookie-banner/cookie-banner.test.tsx +25 -0
  29. package/src/components/cookie-banner/cookie-banner.tsx +36 -0
  30. package/src/components/details/details.test.tsx +7 -7
  31. package/src/components/inset-text/inset-text.test.tsx +1 -1
  32. package/src/components/notification-banner/notification-banner.test.tsx +13 -73
  33. package/src/components/notification-banner/notification-banner.tsx +13 -41
  34. package/src/components/notification-panel/notification-panel.test.tsx +6 -6
  35. package/src/components/page-header/page-header.test.tsx +2 -2
  36. package/src/components/page-metadata/page-metadata.test.tsx +12 -12
  37. package/src/components/page-metadata/page-metadata.tsx +4 -2
  38. package/src/components/pagination/pagination.test.tsx +28 -30
  39. package/src/components/phase-banner/phase-banner.test.tsx +8 -8
  40. package/src/components/question/question.test.tsx +3 -3
  41. package/src/components/radio-button/radio-button.test.tsx +7 -7
  42. package/src/components/select/select.test.tsx +9 -9
  43. package/src/components/sequential-navigation/sequential-navigation.test.tsx +4 -4
  44. package/src/components/side-navigation/side-navigation.test.tsx +1 -1
  45. package/src/components/site-header/site-header.test.tsx +22 -22
  46. package/src/components/site-search/site-search.test.tsx +9 -9
  47. package/src/components/skip-links/skip-links.test.tsx +3 -3
  48. package/src/components/summary-card/summary-card.test.tsx +2 -2
  49. package/src/components/summary-list/summary-list.test.tsx +35 -16
  50. package/src/components/table/table.test.tsx +77 -0
  51. package/src/components/table/table.tsx +36 -0
  52. package/src/components/tabs/tabs.test.tsx +258 -0
  53. package/src/components/tabs/tabs.tsx +110 -0
  54. package/src/components/task-list/task-list.test.tsx +81 -83
  55. package/src/components/task-list/task-list.tsx +9 -5
  56. package/src/components/text-input/text-input.test.tsx +6 -6
  57. package/src/components/textarea/textarea.test.tsx +2 -2
  58. package/src/components/warning-text/warning-text.test.tsx +1 -1
  59. package/src/utils/slugify.ts +13 -0
  60. package/vite.config.ts +6 -1
  61. package/vitest-setup.ts +1 -1
  62. package/@types/components/NotificationBanner.d.ts +0 -9
@@ -37,33 +37,33 @@ test('site header renders correctly (maximal, testing markup structure)', () =>
37
37
  const siteHeaderNavigationMobile = within(siteHeader).getAllByRole('navigation')[0];
38
38
  const siteHeaderNavigationDesktop = within(siteHeader).getAllByRole('navigation')[1];
39
39
  const siteHeaderPhaseBanner = siteHeader.querySelector('.ds_phase-banner');
40
- const siteHeaderSearch = within(siteHeader).getByRole('search').parentNode;
40
+ const siteHeaderSearch = within(siteHeader).getByRole('search').parentElement;
41
41
 
42
42
  expect(siteHeader).toHaveClass('ds_site-header');
43
43
  expect(siteHeaderContentWrapper).toHaveClass('ds_wrapper');
44
44
 
45
- expect(siteHeaderBranding?.parentNode).toEqual(siteHeaderContent);
46
- expect(siteHeaderControls?.parentNode).toEqual(siteHeaderContent);
47
- expect(siteHeaderNavToggle?.parentNode).toEqual(siteHeaderContent);
48
- expect(siteHeaderNavigationMobile.parentNode).toEqual(siteHeaderContent);
49
- expect(siteHeaderSearch?.parentNode).toEqual(siteHeaderContent);
45
+ expect(siteHeaderBranding?.parentElement).toEqual(siteHeaderContent);
46
+ expect(siteHeaderControls?.parentElement).toEqual(siteHeaderContent);
47
+ expect(siteHeaderNavToggle?.parentElement).toEqual(siteHeaderContent);
48
+ expect(siteHeaderNavigationMobile.parentElement).toEqual(siteHeaderContent);
49
+ expect(siteHeaderSearch?.parentElement).toEqual(siteHeaderContent);
50
50
 
51
- expect(siteHeaderBranding?.previousSibling).toBeNull();
52
- expect(siteHeaderControls?.previousSibling).toEqual(siteHeaderBranding);
53
- expect(siteHeaderNavToggle?.previousSibling).toEqual(siteHeaderControls);
54
- expect(siteHeaderNavigationMobile.previousSibling).toEqual(siteHeaderNavToggle);
55
- expect(siteHeaderSearch?.previousSibling).toEqual(siteHeaderNavigationMobile);
51
+ expect(siteHeaderBranding?.previousElementSibling).toBeNull();
52
+ expect(siteHeaderControls?.previousElementSibling).toEqual(siteHeaderBranding);
53
+ expect(siteHeaderNavToggle?.previousElementSibling).toEqual(siteHeaderControls);
54
+ expect(siteHeaderNavigationMobile.previousElementSibling).toEqual(siteHeaderNavToggle);
55
+ expect(siteHeaderSearch?.previousElementSibling).toEqual(siteHeaderNavigationMobile);
56
56
 
57
- expect(siteHeaderNavigationDesktop.parentNode).toHaveClass('ds_wrapper');
58
- expect(siteHeaderNavigationDesktop?.parentNode?.parentNode).toHaveClass('ds_site-header__navigation');
57
+ expect(siteHeaderNavigationDesktop.parentElement).toHaveClass('ds_wrapper');
58
+ expect(siteHeaderNavigationDesktop?.parentElement?.parentElement).toHaveClass('ds_site-header__navigation');
59
59
 
60
- expect(siteHeaderNavigationDesktop?.parentNode?.parentNode?.previousSibling).toEqual(siteHeaderPhaseBanner);
60
+ expect(siteHeaderNavigationDesktop?.parentElement?.parentElement?.previousElementSibling).toEqual(siteHeaderPhaseBanner);
61
61
 
62
- expect(siteHeaderPhaseBanner?.previousSibling).toEqual(siteHeaderContentWrapper);
62
+ expect(siteHeaderPhaseBanner?.previousElementSibling).toEqual(siteHeaderContentWrapper);
63
63
 
64
- expect(siteHeaderPhaseBanner?.parentNode).toEqual(siteHeader);
65
- expect(siteHeaderNavigationDesktop?.parentNode?.parentNode?.parentNode).toEqual(siteHeader);
66
- expect(siteHeaderContentWrapper?.parentNode).toEqual(siteHeader);
64
+ expect(siteHeaderPhaseBanner?.parentElement).toEqual(siteHeader);
65
+ expect(siteHeaderNavigationDesktop?.parentElement?.parentElement?.parentElement).toEqual(siteHeader);
66
+ expect(siteHeaderContentWrapper?.parentElement).toEqual(siteHeader);
67
67
  });
68
68
 
69
69
  test('site header branding: logo only, default URL', () => {
@@ -90,8 +90,8 @@ test('site header branding: logo only, default URL', () => {
90
90
  expect(siteHeaderLogoImg).toHaveAttribute('src', logo.src);
91
91
  expect(siteHeaderLogoImg).toHaveAttribute('alt', logo.alt);
92
92
 
93
- expect(siteHeaderLogoImg.parentNode).toEqual(siteHeaderLogoLink);
94
- expect(siteHeaderLogoLink.parentNode).toEqual(siteHeaderBranding);
93
+ expect(siteHeaderLogoImg.parentElement).toEqual(siteHeaderLogoLink);
94
+ expect(siteHeaderLogoLink.parentElement).toEqual(siteHeaderBranding);
95
95
  });
96
96
 
97
97
  test('site header branding: logo and site title', () => {
@@ -115,8 +115,8 @@ test('site header branding: logo and site title', () => {
115
115
 
116
116
  expect(siteTitle?.tagName).toEqual('DIV');
117
117
  expect(siteTitle?.textContent).toEqual(siteTitleContent);
118
- expect(siteTitle?.parentNode).toEqual(siteHeaderBranding);
119
- expect(siteTitle?.previousSibling).toEqual(siteHeaderLogoLink);
118
+ expect(siteTitle?.parentElement).toEqual(siteHeaderBranding);
119
+ expect(siteTitle?.previousElementSibling).toEqual(siteHeaderLogoLink);
120
120
  });
121
121
 
122
122
  test('site header branding: custom link URL', () => {
@@ -12,27 +12,27 @@ test('site search renders correctly', () => {
12
12
  );
13
13
 
14
14
  const searchForm = screen.getByRole('search');
15
- const searchFormContainer = searchForm.parentNode;
15
+ const searchFormContainer = searchForm.parentElement;
16
16
  const searchLabel = document.querySelector('label');
17
17
  const searchInput = within(searchForm).getByRole('searchbox');
18
- const inputWrapper = searchInput.parentNode;
18
+ const inputWrapper = searchInput.parentElement;
19
19
  const searchButton = within(searchForm).getByRole('button');
20
20
 
21
21
  expect(searchFormContainer).toHaveClass('ds_site-search');
22
- expect(searchFormContainer.tagName).toEqual('DIV');
22
+ expect(searchFormContainer?.tagName).toEqual('DIV');
23
23
  expect(searchFormContainer).not.toHaveAttribute('id', 'site-search-autocomplete');
24
24
 
25
25
  expect(searchForm).toHaveClass('ds_site-search__form');
26
26
  expect(searchForm).toHaveAttribute('method', 'GET');
27
27
  expect(searchForm).toHaveAttribute('action', '/search');
28
28
 
29
- expect(searchLabel.textContent).toEqual(labelText);
29
+ expect(searchLabel?.textContent).toEqual(labelText);
30
30
  expect(searchLabel).toHaveAttribute('for', id);
31
31
  expect(searchLabel).toHaveAttribute('id', `${id}-label`);
32
32
  expect(searchLabel).toHaveClass('ds_label', 'visually-hidden');
33
33
 
34
34
  expect(inputWrapper).toHaveClass('ds_input__wrapper ds_input__wrapper--has-icon');
35
- expect(inputWrapper.tagName).toEqual('DIV');
35
+ expect(inputWrapper?.tagName).toEqual('DIV');
36
36
 
37
37
  expect(searchInput).toHaveClass('ds_input', 'ds_site-search__input');
38
38
  expect(searchInput).toHaveAttribute('id', id);
@@ -113,7 +113,7 @@ test('autocomplete', () => {
113
113
  )
114
114
 
115
115
  const searchForm = screen.getByRole('search');
116
- const searchFormContainer = searchForm.parentNode;
116
+ const searchFormContainer = searchForm.parentElement;
117
117
  const searchInput = within(searchForm).getByRole('searchbox');
118
118
  const autocompleteStatus = within(searchForm).getByRole('status');
119
119
  const suggestionsContainer = document.querySelector('.ds_autocomplete__suggestions');
@@ -134,7 +134,7 @@ test('autocomplete', () => {
134
134
  expect(searchInput).toHaveClass('js-autocomplete-input');
135
135
 
136
136
  expect(suggestionsContainer).toHaveAttribute('id', suggestionsId);
137
- expect(suggestionsContainer.tagName).toEqual('DIV');
137
+ expect(suggestionsContainer?.tagName).toEqual('DIV');
138
138
 
139
139
  expect(suggestionsList).toHaveClass('ds_autocomplete__suggestions-list');
140
140
  expect(suggestionsList).toHaveAttribute('aria-labelledby', `${id}-label`);
@@ -148,7 +148,7 @@ test('passing additional props', () => {
148
148
  );
149
149
 
150
150
  const searchForm = screen.getByRole('search');
151
- const searchFormContainer = searchForm.parentNode;
151
+ const searchFormContainer = searchForm.parentElement;
152
152
  expect(searchFormContainer?.dataset.test).toEqual('foo');
153
153
  });
154
154
 
@@ -158,6 +158,6 @@ test('passing additional CSS classes', () => {
158
158
  );
159
159
 
160
160
  const searchForm = screen.getByRole('search');
161
- const searchFormContainer = searchForm.parentNode;
161
+ const searchFormContainer = searchForm.parentElement;
162
162
  expect(searchFormContainer).toHaveClass('foo', 'ds_site-search');
163
163
  });
@@ -11,12 +11,12 @@ test('skip links renders correctly', () => {
11
11
  );
12
12
 
13
13
  const skipLinksList = screen.getByRole('list');
14
- const skipLinksContainer = skipLinksList.parentNode;
14
+ const skipLinksContainer = skipLinksList.parentElement;
15
15
  const skipLinksListItem = within(skipLinksList).getByRole('listitem');
16
16
  const skipLinksLink = within(skipLinksList).getByRole('link');
17
17
 
18
18
  expect(skipLinksContainer).toHaveClass('ds_skip-links');
19
- expect(skipLinksContainer.tagName).toEqual('DIV');
19
+ expect(skipLinksContainer?.tagName).toEqual('DIV');
20
20
 
21
21
  expect(skipLinksList).toHaveClass('ds_skip-links__list');
22
22
  expect(skipLinksList.tagName).toEqual('UL');
@@ -78,7 +78,7 @@ test('passing additional props', () => {
78
78
  );
79
79
 
80
80
  const skipLinksList = screen.getByRole('list');
81
- const skipLinksContainer = skipLinksList.parentNode;
81
+ const skipLinksContainer = skipLinksList.parentElement;
82
82
 
83
83
  expect(skipLinksContainer?.dataset.test).toEqual('foo');
84
84
  });
@@ -45,8 +45,8 @@ test('summary card renders correctly', () => {
45
45
 
46
46
  const summaryCard = screen.getByTestId('foo');
47
47
  const title = within(summaryCard).getByRole('heading');
48
- const header = title.parentElement;
49
- const content = header?.nextSibling;
48
+ const header = title.parentElement as HTMLElement;
49
+ const content = header?.nextElementSibling as HTMLElement;
50
50
  const actionsList = within(header).getByRole('list')
51
51
 
52
52
  const thisList = within(content).getByRole('list');
@@ -116,7 +116,7 @@ test('summary list item with multiple values', () => {
116
116
  const item = screen.getAllByRole('listitem')[0];
117
117
  const value = item.querySelector('.ds_summary-list__value');
118
118
  const valueList = value?.children[0];
119
- const valueListItems = valueList?.children;
119
+ const valueListItems = valueList?.children as HTMLCollection;
120
120
 
121
121
  expect(valueList).toHaveClass('ds_no-bullets');
122
122
  expect(valueList?.tagName).toEqual('UL');
@@ -143,31 +143,47 @@ test('summary list item with no value', () => {
143
143
  });
144
144
 
145
145
  test('summary list item with multiple actions', () => {
146
+ const title = 'Name';
147
+ const value = 'Jane Smith';
148
+ const actions = [
149
+ {
150
+ title: 'Change',
151
+ href: '#foo'
152
+ },
153
+ {
154
+ title: 'Delete',
155
+ onclick: onClickFn
156
+ }
157
+ ];
158
+
146
159
  render(
147
160
  <Item
148
161
  actions={items[0].actions}
149
- title={items[0].title}
150
- value={items[0].value}
162
+ title={title}
163
+ value={value}
151
164
  />
152
165
  );
153
166
 
154
167
  const item = screen.getAllByRole('listitem')[0];
155
- const actions = item.querySelector('.ds_summary-list__actions');
168
+ const actionsElement = item.querySelector('.ds_summary-list__actions');
156
169
 
157
- expect(actions.children.length).toEqual(items[0].actions.length);
158
- expect(actions.children[0].textContent).toEqual(items[0].actions[0].title);
159
- expect(actions.children[1].textContent).toEqual(items[0].actions[1].title);
170
+ expect(actionsElement?.children.length).toEqual(actions.length);
171
+ expect(actionsElement?.children[0].textContent).toEqual(actions[0].title);
172
+ expect(actionsElement?.children[1].textContent).toEqual(actions[1].title);
160
173
  });
161
174
 
162
175
  test('button action', () => {
163
176
  const describedById = 'q1-name';
177
+ const title = 'Name';
178
+ const href = undefined;
179
+ const onClick = onClickFn;
164
180
 
165
181
  render(
166
182
  <Action
167
183
  describedby={describedById}
168
- href={items[0].actions[1].href}
169
- onclick={items[0].actions[1].onclick}
170
- title={items[0].actions[1].title}
184
+ href={href}
185
+ onclick={onClick}
186
+ title={title}
171
187
  />
172
188
  );
173
189
 
@@ -178,7 +194,7 @@ test('button action', () => {
178
194
  expect(action).toHaveAttribute('type', 'button');
179
195
  expect(action).not.toHaveAttribute('href');
180
196
  expect(action.tagName).toEqual('BUTTON');
181
- expect(action.textContent).toEqual('Delete');
197
+ expect(action.textContent).toEqual(title);
182
198
 
183
199
  fireEvent.click(action);
184
200
 
@@ -187,13 +203,16 @@ test('button action', () => {
187
203
 
188
204
  test('link action', () => {
189
205
  const describedById = 'q1-name';
206
+ const title = 'Name';
207
+ const href = "#foo"
208
+ const onClick = onClickFn;
190
209
 
191
210
  render(
192
211
  <Action
193
212
  describedby={describedById}
194
- href={items[0].actions[0].href}
195
- onclick={items[0].actions[0].onclick}
196
- title={items[0].actions[0].title}
213
+ href={href}
214
+ onclick={onClick}
215
+ title={title}
197
216
  />
198
217
  );
199
218
 
@@ -201,10 +220,10 @@ test('link action', () => {
201
220
 
202
221
  expect(action).toHaveClass('ds_link');
203
222
  expect(action).toHaveAttribute('aria-describedby', describedById);
204
- expect(action).toHaveAttribute('href', items[0].actions[0].href);
223
+ expect(action).toHaveAttribute('href', href);
205
224
  expect(action).not.toHaveAttribute('type');
206
225
  expect(action.tagName).toEqual('A');
207
- expect(action.textContent).toEqual('Change');
226
+ expect(action.textContent).toEqual(title);
208
227
  });
209
228
 
210
229
  test('multiline answer', () => {
@@ -0,0 +1,77 @@
1
+ import { test, expect } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import Table from './table';
4
+
5
+ test('table renders correctly', () => {
6
+ render(
7
+ <Table>
8
+ <caption>Public holidays in 2020</caption>
9
+ <thead>
10
+ <tr>
11
+ <th scope="col">Date</th>
12
+ <th scope="col">Day</th>
13
+ <th scope="col">Holiday</th>
14
+ </tr>
15
+ </thead>
16
+ <tbody>
17
+ <tr>
18
+ <td>10 April</td>
19
+ <td>Friday</td>
20
+ <td>Good Friday</td>
21
+ </tr>
22
+ </tbody>
23
+ </Table>
24
+ );
25
+
26
+ const table = screen.getByRole('table');
27
+
28
+ expect(table).toHaveClass('ds_table');
29
+ expect(table.nodeName).toEqual('TABLE');
30
+ });
31
+
32
+ test('table with smallscreen behaviour', () => {
33
+ const behaviour = 'scrolling';
34
+
35
+ render(
36
+ <Table smallscreen={behaviour}>
37
+ <caption>Public holidays in 2020</caption>
38
+ <thead>
39
+ <tr>
40
+ <th scope="col">Date</th>
41
+ <th scope="col">Day</th>
42
+ <th scope="col">Holiday</th>
43
+ </tr>
44
+ </thead>
45
+ <tbody>
46
+ <tr>
47
+ <td>10 April</td>
48
+ <td>Friday</td>
49
+ <td>Good Friday</td>
50
+ </tr>
51
+ </tbody>
52
+ </Table>
53
+ );
54
+
55
+ const table = screen.getByRole('table');
56
+
57
+ expect(table).toHaveAttribute('data-smallscreen', behaviour);
58
+ expect(table.nodeName).toEqual('TABLE');
59
+ });
60
+
61
+ test('passing additional props', () => {
62
+ render(
63
+ <Table data-test="foo"/>
64
+ );
65
+
66
+ const table = screen.getByRole('table');
67
+ expect(table?.dataset.test).toEqual('foo');
68
+ });
69
+
70
+ test('table with additional CSS class', () => {
71
+ render(
72
+ <Table className="foo"/>
73
+ );
74
+
75
+ const table = screen.getByRole('table');
76
+ expect(table).toHaveClass('foo', 'ds_table');
77
+ });
@@ -0,0 +1,36 @@
1
+ import { useEffect, useRef } from 'react';
2
+ // @ts-ignore
3
+ import DSTable from '@scottish-government/design-system/src/components/table/table';
4
+
5
+ const Table: React.FC<SGDS.Component.Table> = ({
6
+ children,
7
+ className,
8
+ smallscreen,
9
+ ...props
10
+ }) => {
11
+ const ref = useRef(null);
12
+
13
+ useEffect(() => {
14
+ if (ref.current) {
15
+ new DSTable().init();
16
+ }
17
+ }, [ref]);
18
+
19
+ return (
20
+ <table
21
+ className={[
22
+ 'ds_table',
23
+ className
24
+ ].join(' ')}
25
+ data-smallscreen={smallscreen}
26
+ ref={ref}
27
+ {...props}
28
+ >
29
+ {children}
30
+ </table>
31
+ );
32
+ };
33
+
34
+ Table.displayName = 'Table';
35
+
36
+ export default Table;
@@ -0,0 +1,258 @@
1
+ import { test, expect } from 'vitest';
2
+ import { render, screen, within } from '@testing-library/react';
3
+ import Tabs from './tabs';
4
+ import slugify from '../../utils/slugify';
5
+
6
+ test('tab container renders correctly', () => {
7
+ render(
8
+ <Tabs data-testid="tabcontainer">
9
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1">
10
+ <div data-testid="tabpanel1content">Content one</div>
11
+ </Tabs.Item>
12
+ <Tabs.Item tabLabel="Tab 2" data-testid="tabpanel2">
13
+ Content two
14
+ </Tabs.Item>
15
+ </Tabs>
16
+ );
17
+
18
+ const tabContainer = screen.getByTestId('tabcontainer');
19
+ const tabHeading = within(tabContainer).getByRole('heading');
20
+ const tabList = within(tabContainer).getByRole('tablist');
21
+ const tabListItems = within(tabList).getAllByRole('presentation');
22
+ const tabListLinkOne = within(tabListItems[0]).getByRole('tab');
23
+ const tabListLinkTwo = within(tabListItems[1]).getByRole('tab');
24
+ const tabPanels = document.querySelectorAll('.ds_tabs__content');
25
+ const tabPanelOne = screen.getByTestId('tabpanel1');
26
+ const tabPanelTwo = screen.getByTestId('tabpanel2');
27
+
28
+ expect(tabContainer).toHaveClass('ds_tabs');
29
+ expect(tabContainer.tagName).toEqual('DIV');
30
+
31
+ expect(tabHeading).toHaveClass('ds_tabs__title');
32
+ // default header level
33
+ expect(tabHeading.tagName).toEqual('H2');
34
+ expect(tabHeading.parentElement).toEqual(tabContainer);
35
+ // default ID-heading
36
+ expect(tabHeading).toHaveAttribute('id', 'tabs-heading');
37
+
38
+ expect(tabList).toHaveClass('ds_tabs__list');
39
+ expect(tabList.tagName).toEqual('UL');
40
+ expect(tabList.parentElement).toEqual(tabContainer);
41
+ expect(tabList.previousElementSibling).toEqual(tabHeading);
42
+
43
+ // number of items in the provided markup
44
+ expect(tabListItems.length).toEqual(2);
45
+
46
+ expect(tabListItems[0]).toHaveClass('ds_tabs__tab');
47
+ expect(tabListItems[0].tagName).toEqual('LI');
48
+
49
+ expect(tabListLinkOne).toHaveClass('ds_tabs__tab-link');
50
+ expect(tabListLinkOne).toHaveAttribute('href', `#${tabPanelOne.id}`);
51
+ expect(tabListLinkTwo).toHaveAttribute('href', `#${tabPanelTwo.id}`);
52
+ // text content in provided markup
53
+ expect(tabListLinkOne.textContent).toEqual('Tab 1');
54
+ expect(tabListLinkTwo.textContent).toEqual('Tab 2');
55
+
56
+ // number of items in the provided markup
57
+ expect(tabPanels.length).toEqual(2)
58
+
59
+ expect(tabPanelOne).toHaveClass('ds_tabs__content', 'ds_tabs__content--bordered');
60
+
61
+ // todo: this will be true after the tab script has been updated in the core DS
62
+ //expect(tabPanelOne.tabIndex).toEqual(0);
63
+
64
+ // tab panels have ID
65
+ expect(tabPanelOne).toHaveAttribute('id');
66
+ expect(tabPanelTwo).toHaveAttribute('id');
67
+ });
68
+
69
+ test('non-bordered tabs', () => {
70
+ render(
71
+ <Tabs data-testid="tabcontainer" bordered={false}>
72
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1">
73
+ <div data-testid="tabpanel1content">Content one</div>
74
+ </Tabs.Item>
75
+ <Tabs.Item tabLabel="Tab 2" data-testid="tabpanel2">
76
+ Content two
77
+ </Tabs.Item>
78
+ </Tabs>
79
+ );
80
+
81
+ const tabPanelOne = screen.getByTestId('tabpanel1');
82
+ expect(tabPanelOne).not.toHaveClass('ds_tabs__content--bordered');
83
+ });
84
+
85
+ test('custom title', () => {
86
+ const title = 'My title';
87
+
88
+ render(
89
+ <Tabs data-testid="tabcontainer" title={title}>
90
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1">
91
+ <div data-testid="tabpanel1content">Content one</div>
92
+ </Tabs.Item>
93
+ <Tabs.Item tabLabel="Tab 2" data-testid="tabpanel2">
94
+ Content two
95
+ </Tabs.Item>
96
+ </Tabs>
97
+ );
98
+
99
+ const tabContainer = screen.getByTestId('tabcontainer');
100
+ const tabList = within(tabContainer).getByRole('tablist');
101
+ const tabHeading = within(tabContainer).getByRole('heading');
102
+
103
+ expect(tabList).toHaveAttribute('aria-labelledby', tabHeading.id);
104
+ expect(tabHeading.textContent).toEqual(title)
105
+ });
106
+
107
+ test('custom base ID', () => {
108
+ const baseId = 'foo';
109
+
110
+ render(
111
+ <Tabs data-testid="tabcontainer" baseId={baseId}>
112
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1">
113
+ <div data-testid="tabpanel1content">Content one</div>
114
+ </Tabs.Item>
115
+ <Tabs.Item tabLabel="Tab 2" data-testid="tabpanel2">
116
+ Content two
117
+ </Tabs.Item>
118
+ </Tabs>
119
+ );
120
+
121
+ const tabContainer = screen.getByTestId('tabcontainer');
122
+ const tabHeading = within(tabContainer).getByRole('heading');
123
+
124
+ expect(tabHeading).toHaveAttribute('id', `${baseId}-heading`);
125
+ });
126
+
127
+ test('custom header level', () => {
128
+ const headerLevel = 'h3';
129
+
130
+ render(
131
+ <Tabs data-testid="tabcontainer" headerLevel={headerLevel}>
132
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1">
133
+ <div data-testid="tabpanel1content">Content one</div>
134
+ </Tabs.Item>
135
+ <Tabs.Item tabLabel="Tab 2" data-testid="tabpanel2">
136
+ Content two
137
+ </Tabs.Item>
138
+ </Tabs>
139
+ );
140
+
141
+ const tabContainer = screen.getByTestId('tabcontainer');
142
+ const tabHeading = within(tabContainer).getByRole('heading');
143
+
144
+ expect(tabHeading.tagName).toEqual(headerLevel.toUpperCase());
145
+ });
146
+
147
+ test('custom baseID', () => {
148
+ const baseId = 'myId';
149
+
150
+ render(
151
+ <Tabs data-testid="tabcontainer" baseId={baseId}>
152
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1">
153
+ <div data-testid="tabpanel1content">Content one</div>
154
+ </Tabs.Item>
155
+ <Tabs.Item tabLabel="Tab 2" data-testid="tabpanel2">
156
+ Content two
157
+ </Tabs.Item>
158
+ </Tabs>
159
+ );
160
+
161
+ const tabContainer = screen.getByTestId('tabcontainer');
162
+ const tabHeading = within(tabContainer).getByRole('heading');
163
+ const tabPanelOne = screen.getByTestId('tabpanel1');
164
+ const tabPanelTwo = screen.getByTestId('tabpanel2');
165
+
166
+ // default title slugified to part of the ID
167
+ expect(tabHeading).toHaveAttribute('id', `${baseId}-heading`);
168
+ // generated IDs using the slug of the tab title
169
+ expect(tabPanelOne).toHaveAttribute('id', `${baseId}-tab-1`);
170
+ expect(tabPanelTwo).toHaveAttribute('id', `${baseId}-tab-2`);
171
+ });
172
+
173
+ test('tab with and without specific ID attribute', () => {
174
+ const baseId = 'foo'
175
+ const tabLabel = 'Tab 1';
176
+ const id = 'bar';
177
+
178
+ render(
179
+ <Tabs baseId={baseId}>
180
+ <Tabs.Item tabLabel={tabLabel} data-testid="tabpanel1">
181
+ <div data-testid="tabpanel1content">Content one</div>
182
+ </Tabs.Item>
183
+ <Tabs.Item tabLabel="Tab 2" id={id} data-testid="tabpanel2">
184
+ <div data-testid="tabpanel1content">Content one</div>
185
+ </Tabs.Item>
186
+ </Tabs>
187
+ );
188
+
189
+ const tabPanelOne = screen.getByTestId('tabpanel1');
190
+ const tabPanelTwo = screen.getByTestId('tabpanel2');
191
+
192
+ expect(tabPanelOne).toHaveAttribute('id', `${baseId}-${slugify(tabLabel)}`);
193
+ expect(tabPanelTwo).toHaveAttribute('id', id);
194
+ });
195
+
196
+ test('with manual activation', () => {
197
+ render(
198
+ <Tabs data-testid="tabcontainer" manual>
199
+ <Tabs.Item title="Tab 1" data-testid="tabpanel1">
200
+ <div data-testid="tabpanel1content">Content one</div>
201
+ </Tabs.Item>
202
+ <Tabs.Item title="Tab 2" data-testid="tabpanel2">
203
+ Content two
204
+ </Tabs.Item>
205
+ </Tabs>
206
+ );
207
+
208
+ const tabContainer = screen.getByTestId('tabcontainer');
209
+ expect(tabContainer).toHaveClass('ds_tabs--manual');
210
+ });
211
+
212
+ test('passing additional props to tab container', () => {
213
+ render(
214
+ <Tabs data-testid="tabcontainer" data-test="foo">
215
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1">
216
+ <div data-testid="tabpanel1content">Content one</div>
217
+ </Tabs.Item>
218
+ </Tabs>
219
+ );
220
+
221
+ const tabContainer = screen.getByTestId('tabcontainer');
222
+ expect(tabContainer.dataset.test).toEqual('foo');
223
+ });
224
+
225
+ test('passing additional CSS classes tab container', () => {
226
+ render(
227
+ <Tabs data-testid="tabcontainer" className="foo">
228
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1">
229
+ <div data-testid="tabpanel1content">Content one</div>
230
+ </Tabs.Item>
231
+ </Tabs>
232
+ );
233
+
234
+ const tabContainer = screen.getByTestId('tabcontainer');
235
+ expect(tabContainer).toHaveClass('foo');
236
+ });
237
+
238
+ test('passing additional props to tab panel', () => {
239
+ render(
240
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1" data-test="foo">
241
+ <div data-testid="tabpanel1content">Content one</div>
242
+ </Tabs.Item>
243
+ );
244
+
245
+ const tabItem = screen.getByTestId('tabpanel1');
246
+ expect(tabItem.dataset.test).toEqual('foo');
247
+ });
248
+
249
+ test('passing additional CSS classes tab panel', () => {
250
+ render(
251
+ <Tabs.Item tabLabel="Tab 1" data-testid="tabpanel1" className="foo">
252
+ <div data-testid="tabpanel1content">Content one</div>
253
+ </Tabs.Item>
254
+ );
255
+
256
+ const tabItem = screen.getByTestId('tabpanel1');
257
+ expect(tabItem).toHaveClass('foo');
258
+ });