@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.
- package/@types/common/AbstractNotificationBanner.d.ts +2 -2
- package/@types/common/ActionLink.d.ts +8 -0
- package/@types/common/FileIcon.d.ts +1 -1
- package/@types/common/Icon.d.ts +1 -1
- package/@types/components/Breadcrumbs.d.ts +2 -5
- package/@types/components/Checkbox.d.ts +0 -2
- package/@types/components/ConfirmationMessage.d.ts +1 -1
- package/@types/components/ContentsNav.d.ts +4 -6
- package/@types/components/DatePicker.d.ts +1 -1
- package/@types/components/ErrorSummary.d.ts +3 -4
- package/@types/components/NotificationPanel.d.ts +1 -1
- package/@types/components/Pagination.d.ts +5 -4
- package/@types/components/PhaseBanner.d.ts +0 -1
- package/@types/components/Question.d.ts +1 -1
- package/@types/components/RadioButton.d.ts +0 -1
- package/@types/components/Select.d.ts +0 -7
- package/@types/components/SequentialNavigation.d.ts +4 -4
- package/@types/components/SideNavigation.d.ts +4 -5
- package/@types/components/SiteFooter.d.ts +25 -0
- package/@types/components/SiteHeader.d.ts +10 -3
- package/@types/components/SiteNavigation.d.ts +2 -3
- package/@types/components/SkipLinks.d.ts +3 -4
- package/@types/components/SummaryCard.d.ts +0 -2
- package/@types/components/SummaryList.d.ts +0 -13
- package/@types/components/Tabs.d.ts +0 -1
- package/@types/components/Tag.d.ts +1 -3
- package/@types/components/TaskList.d.ts +1 -0
- package/@types/sgds.d.ts +13 -2
- package/CHANGELOG.md +63 -1
- package/dist/common/AbstractNotificationBanner.jsx +8 -6
- package/dist/common/ActionLink.jsx +19 -0
- package/dist/common/FileIcon.jsx +2 -7
- package/dist/common/Icon.jsx +3 -9
- package/dist/components/Accordion/Accordion.jsx +2 -2
- package/dist/components/Breadcrumbs/Breadcrumbs.jsx +20 -15
- package/dist/components/Checkbox/Checkbox.jsx +2 -30
- package/dist/components/Checkbox/CheckboxGroup.jsx +69 -0
- package/dist/components/ContentsNav/ContentsNav.jsx +27 -16
- package/dist/components/CookieBanner/CookieBanner.jsx +1 -0
- package/dist/components/DatePicker/DatePicker.jsx +5 -5
- package/dist/components/ErrorSummary/ErrorSummary.jsx +28 -18
- package/dist/components/NotificationBanner/NotificationBanner.jsx +2 -2
- package/dist/components/Pagination/Pagination.jsx +42 -22
- package/dist/components/PhaseBanner/PhaseBanner.jsx +3 -3
- package/dist/components/Question/Question.jsx +3 -3
- package/dist/components/RadioButton/RadioButton.jsx +3 -17
- package/dist/components/RadioButton/RadioGroup.jsx +61 -0
- package/dist/components/Select/Select.jsx +4 -7
- package/dist/components/SequentialNavigation/SequentialNavigation.jsx +31 -18
- package/dist/components/SideNavigation/SideNavigation.jsx +17 -16
- package/dist/components/SiteFooter/SiteFooter.jsx +104 -0
- package/dist/components/SiteHeader/SiteHeader.jsx +113 -32
- package/dist/components/SiteNavigation/SiteNavigation.jsx +20 -7
- package/dist/components/SkipLinks/SkipLinks.jsx +10 -10
- package/dist/components/SummaryCard/SummaryCard.jsx +25 -14
- package/dist/components/SummaryList/SummaryList.jsx +65 -47
- package/dist/components/Tabs/Tabs.jsx +6 -6
- package/dist/components/Tag/Tag.jsx +2 -2
- package/dist/components/TaskList/TaskList.jsx +14 -3
- package/dist/components/TextInput/TextInput.jsx +3 -3
- package/dist/components/Textarea/Textarea.jsx +3 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/AbstractNotificationBanner.test.tsx +1 -1
- package/src/common/AbstractNotificationBanner.tsx +14 -13
- package/src/common/ActionLink.test.tsx +80 -0
- package/src/common/ActionLink.tsx +27 -0
- package/src/common/ConditionalWrapper.tsx +1 -1
- package/src/common/FileIcon.tsx +7 -11
- package/src/common/HintText.tsx +2 -2
- package/src/common/Icon.tsx +13 -17
- package/src/common/ScreenReaderText.tsx +2 -2
- package/src/common/WrapperTag.tsx +2 -2
- package/src/components/Accordion/Accordion.test.tsx +1 -1
- package/src/components/Accordion/Accordion.tsx +6 -7
- package/src/components/AspectBox/AspectBox.tsx +2 -2
- package/src/components/BackToTop/BackToTop.tsx +2 -2
- package/src/components/Breadcrumbs/Breadcrumbs.test.tsx +79 -47
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +31 -31
- package/src/components/Button/Button.tsx +2 -2
- package/src/components/Checkbox/Checkbox.test.tsx +1 -96
- package/src/components/Checkbox/Checkbox.tsx +3 -55
- package/src/components/Checkbox/CheckboxGroup.test.tsx +37 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +46 -0
- package/src/components/ConfirmationMessage/ConfirmationMessage.tsx +2 -2
- package/src/components/ContentsNav/ContentsNav.test.tsx +40 -51
- package/src/components/ContentsNav/ContentsNav.tsx +32 -25
- package/src/components/CookieBanner/CookieBanner.tsx +3 -3
- package/src/components/DatePicker/DatePicker.test.tsx +1 -1
- package/src/components/DatePicker/DatePicker.tsx +7 -7
- package/src/components/Details/Details.tsx +2 -2
- package/src/components/ErrorMessage/ErrorMessage.tsx +2 -2
- package/src/components/ErrorSummary/ErrorSummary.test.tsx +40 -34
- package/src/components/ErrorSummary/ErrorSummary.tsx +40 -32
- package/src/components/FileDownload/FileDownload.tsx +2 -2
- package/src/components/HideThisPage/HideThisPage.tsx +2 -2
- package/src/components/InsetText/InsetText.tsx +2 -2
- package/src/components/NotificationBanner/NotificationBanner.tsx +6 -7
- package/src/components/NotificationPanel/NotificationPanel.tsx +2 -2
- package/src/components/PageHeader/PageHeader.tsx +2 -2
- package/src/components/PageMetadata/PageMetadata.tsx +4 -5
- package/src/components/Pagination/Pagination.test.tsx +26 -7
- package/src/components/Pagination/Pagination.tsx +70 -36
- package/src/components/PhaseBanner/PhaseBanner.tsx +4 -5
- package/src/components/Question/Question.test.tsx +1 -1
- package/src/components/Question/Question.tsx +5 -5
- package/src/components/RadioButton/RadioButton.test.tsx +7 -126
- package/src/components/RadioButton/RadioButton.tsx +4 -41
- package/src/components/RadioButton/RadioGroup.test.tsx +65 -0
- package/src/components/RadioButton/RadioGroup.tsx +38 -0
- package/src/components/Select/Select.test.tsx +39 -37
- package/src/components/Select/Select.tsx +7 -22
- package/src/components/SequentialNavigation/SequentialNavigation.test.tsx +32 -21
- package/src/components/SequentialNavigation/SequentialNavigation.tsx +52 -30
- package/src/components/SideNavigation/SideNavigation.test.tsx +39 -85
- package/src/components/SideNavigation/SideNavigation.tsx +27 -29
- package/src/components/SiteFooter/SiteFooter.test.tsx +153 -0
- package/src/components/SiteFooter/SiteFooter.tsx +107 -0
- package/src/components/SiteHeader/SiteHeader.test.tsx +87 -79
- package/src/components/SiteHeader/SiteHeader.tsx +103 -40
- package/src/components/SiteNavigation/SiteNavigation.test.tsx +42 -23
- package/src/components/SiteNavigation/SiteNavigation.tsx +28 -16
- package/src/components/SiteSearch/SiteSearch.tsx +2 -2
- package/src/components/SkipLinks/SkipLinks.test.tsx +22 -10
- package/src/components/SkipLinks/SkipLinks.tsx +16 -15
- package/src/components/SummaryCard/SummaryCard.test.tsx +31 -35
- package/src/components/SummaryCard/SummaryCard.tsx +39 -28
- package/src/components/SummaryList/SummaryList.test.tsx +49 -148
- package/src/components/SummaryList/SummaryList.tsx +54 -92
- package/src/components/Table/Table.tsx +2 -2
- package/src/components/Tabs/Tabs.tsx +14 -15
- package/src/components/Tag/Tag.test.tsx +4 -4
- package/src/components/Tag/Tag.tsx +4 -4
- package/src/components/TaskList/TaskList.test.tsx +26 -0
- package/src/components/TaskList/TaskList.tsx +21 -11
- package/src/components/TextInput/TextInput.test.tsx +1 -1
- package/src/components/TextInput/TextInput.tsx +5 -5
- package/src/components/Textarea/Textarea.test.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +5 -5
- package/src/components/WarningText/WarningText.tsx +2 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { test, expect, vi } from 'vitest';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import ActionLink from './ActionLink';
|
|
4
|
+
|
|
5
|
+
const ONCLICK_FUNCTION = vi.fn();
|
|
6
|
+
const ACTION_HREF = "#foo"
|
|
7
|
+
const ACTION_ONCLICK = ONCLICK_FUNCTION;
|
|
8
|
+
const ACTION_TEXT = 'Name';
|
|
9
|
+
const DESCRIBEDBY_ID = 'q1-name';
|
|
10
|
+
|
|
11
|
+
test('button action', () => {
|
|
12
|
+
render(
|
|
13
|
+
<ActionLink
|
|
14
|
+
describedby={DESCRIBEDBY_ID}
|
|
15
|
+
href={undefined}
|
|
16
|
+
onclick={ACTION_ONCLICK}
|
|
17
|
+
>
|
|
18
|
+
{ACTION_TEXT}
|
|
19
|
+
</ActionLink>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const action = screen.getByRole('button');
|
|
23
|
+
|
|
24
|
+
expect(action).toHaveClass('ds_link');
|
|
25
|
+
expect(action).toHaveAttribute('aria-describedby', DESCRIBEDBY_ID);
|
|
26
|
+
expect(action).toHaveAttribute('type', 'button');
|
|
27
|
+
expect(action).not.toHaveAttribute('href');
|
|
28
|
+
expect(action.tagName).toEqual('BUTTON');
|
|
29
|
+
expect(action.textContent).toEqual(ACTION_TEXT);
|
|
30
|
+
|
|
31
|
+
fireEvent.click(action);
|
|
32
|
+
|
|
33
|
+
expect(ONCLICK_FUNCTION).toHaveBeenCalled();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('link action', () => {
|
|
37
|
+
render(
|
|
38
|
+
<ActionLink
|
|
39
|
+
describedby={DESCRIBEDBY_ID}
|
|
40
|
+
href={ACTION_HREF}
|
|
41
|
+
onclick={ACTION_ONCLICK}
|
|
42
|
+
>
|
|
43
|
+
{ACTION_TEXT}
|
|
44
|
+
</ActionLink >
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const action = screen.getByRole('link');
|
|
48
|
+
|
|
49
|
+
expect(action).toHaveClass('ds_link');
|
|
50
|
+
expect(action).toHaveAttribute('aria-describedby', DESCRIBEDBY_ID);
|
|
51
|
+
expect(action).toHaveAttribute('href', ACTION_HREF);
|
|
52
|
+
expect(action).not.toHaveAttribute('type');
|
|
53
|
+
expect(action.tagName).toEqual('A');
|
|
54
|
+
expect(action.textContent).toEqual(ACTION_TEXT);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('action with custom element', () => {
|
|
58
|
+
render(
|
|
59
|
+
<ActionLink
|
|
60
|
+
describedby={DESCRIBEDBY_ID}
|
|
61
|
+
href={ACTION_HREF}
|
|
62
|
+
onclick={ACTION_ONCLICK}
|
|
63
|
+
linkComponent={
|
|
64
|
+
({ className, ...props }) => (
|
|
65
|
+
<strong role="link" className={className} {...props}/>
|
|
66
|
+
)}>
|
|
67
|
+
{ACTION_TEXT}
|
|
68
|
+
</ActionLink>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const action = screen.getByRole('link');
|
|
72
|
+
|
|
73
|
+
expect(action).toHaveAttribute('aria-describedby', DESCRIBEDBY_ID);
|
|
74
|
+
expect(action?.tagName).toEqual('STRONG');
|
|
75
|
+
expect(action?.textContent).toEqual(ACTION_TEXT);
|
|
76
|
+
|
|
77
|
+
fireEvent.click(action);
|
|
78
|
+
|
|
79
|
+
expect(ONCLICK_FUNCTION).toHaveBeenCalled();
|
|
80
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const ActionLink = ({
|
|
2
|
+
children,
|
|
3
|
+
describedby,
|
|
4
|
+
href,
|
|
5
|
+
linkComponent,
|
|
6
|
+
onclick
|
|
7
|
+
}: SGDS.Common.ActionLink) => {
|
|
8
|
+
const CLASSNAME = 'ds_link';
|
|
9
|
+
|
|
10
|
+
function processChildren(children: React.ReactNode) {
|
|
11
|
+
if (linkComponent) {
|
|
12
|
+
return linkComponent({ className: CLASSNAME, href, children, onClick: onclick, 'aria-describedby': describedby });
|
|
13
|
+
} else if (href) {
|
|
14
|
+
return <a aria-describedby={describedby} onClick={onclick} href={href} className={CLASSNAME}>{children}</a>;
|
|
15
|
+
} else {
|
|
16
|
+
return <button type="button" aria-describedby={describedby} onClick={onclick} className={CLASSNAME}>{children}</button>;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
processChildren(children)
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
ActionLink.displayName = 'ActionLink';
|
|
26
|
+
|
|
27
|
+
export default ActionLink;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Wraps all children in a specified HTML tag if a condition is met.
|
|
3
3
|
*/
|
|
4
|
-
const ConditionalWrapper
|
|
4
|
+
const ConditionalWrapper = ({ condition, wrapper, children }:SGDS.Common.ConditionalWrapper) =>
|
|
5
5
|
condition ? wrapper(children) : children;
|
|
6
6
|
|
|
7
7
|
ConditionalWrapper.displayName = 'ConditionalWrapper';
|
package/src/common/FileIcon.tsx
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import * as FileIcons from '../images/documents';
|
|
3
3
|
|
|
4
|
-
const FileIcon
|
|
4
|
+
const FileIcon = ({
|
|
5
5
|
ariaLabel = '',
|
|
6
6
|
className,
|
|
7
7
|
icon
|
|
8
|
-
}) => {
|
|
9
|
-
const
|
|
10
|
-
{
|
|
11
|
-
className: className,
|
|
12
|
-
'aria-label': ariaLabel
|
|
13
|
-
}
|
|
14
|
-
);
|
|
8
|
+
}: SGDS.Common.FileIcon) => {
|
|
9
|
+
const FileIconComponent = FileIcons[icon];
|
|
15
10
|
|
|
16
11
|
return (
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
|
|
12
|
+
<FileIconComponent
|
|
13
|
+
aria-label={ariaLabel}
|
|
14
|
+
className={className}
|
|
15
|
+
/>
|
|
20
16
|
);
|
|
21
17
|
};
|
|
22
18
|
|
package/src/common/HintText.tsx
CHANGED
package/src/common/Icon.tsx
CHANGED
|
@@ -1,30 +1,26 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import * as Icons from '../images/icons';
|
|
3
3
|
|
|
4
|
-
const Icon
|
|
4
|
+
const Icon = ({
|
|
5
5
|
ariaLabel,
|
|
6
6
|
className,
|
|
7
7
|
fill,
|
|
8
8
|
icon,
|
|
9
9
|
iconSize
|
|
10
|
-
}) => {
|
|
11
|
-
const
|
|
12
|
-
{
|
|
13
|
-
'aria-hidden': ariaLabel ? undefined : true,
|
|
14
|
-
'aria-label': ariaLabel,
|
|
15
|
-
className: [
|
|
16
|
-
'ds_icon',
|
|
17
|
-
className,
|
|
18
|
-
fill && 'ds_icon--fill',
|
|
19
|
-
iconSize && `ds_icon--${iconSize}`
|
|
20
|
-
].join(' ')
|
|
21
|
-
}
|
|
22
|
-
);
|
|
10
|
+
}: SGDS.Common.Icon) => {
|
|
11
|
+
const IconComponent = Icons[icon];
|
|
23
12
|
|
|
24
13
|
return (
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
|
|
14
|
+
<IconComponent
|
|
15
|
+
aria-hidden={ariaLabel ? undefined : true}
|
|
16
|
+
aria-label={ariaLabel}
|
|
17
|
+
className={[
|
|
18
|
+
'ds_icon',
|
|
19
|
+
className,
|
|
20
|
+
fill && 'ds_icon--fill',
|
|
21
|
+
iconSize && `ds_icon--${iconSize}`
|
|
22
|
+
].join(' ')}
|
|
23
|
+
/>
|
|
28
24
|
);
|
|
29
25
|
};
|
|
30
26
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Wraps all children in a specified HTML tag.
|
|
3
3
|
*/
|
|
4
|
-
const WrapperTag
|
|
4
|
+
const WrapperTag = ({
|
|
5
5
|
children,
|
|
6
6
|
tagName = 'div',
|
|
7
7
|
...props
|
|
8
|
-
}) => {
|
|
8
|
+
}: SGDS.Common.WrapperTag) => {
|
|
9
9
|
const TagName = tagName;
|
|
10
10
|
return <TagName {...props}>{children}</TagName>;
|
|
11
11
|
};
|
|
@@ -216,7 +216,7 @@ test('passing additional props to accordion item', () => {
|
|
|
216
216
|
);
|
|
217
217
|
|
|
218
218
|
const accordionItem = screen.getByTestId(ACCORDION_ITEM_ID);
|
|
219
|
-
expect(accordionItem
|
|
219
|
+
expect(accordionItem.dataset.test).toEqual('foo');
|
|
220
220
|
});
|
|
221
221
|
|
|
222
222
|
test('passing additional CSS classes', () => {
|
|
@@ -5,7 +5,7 @@ import DSAccordion from '@scottish-government/design-system/src/components/accor
|
|
|
5
5
|
|
|
6
6
|
let accordionItemCounter = 0;
|
|
7
7
|
|
|
8
|
-
const AccordionItem
|
|
8
|
+
const AccordionItem = ({
|
|
9
9
|
children,
|
|
10
10
|
className,
|
|
11
11
|
headingLevel = 'h3',
|
|
@@ -13,7 +13,7 @@ const AccordionItem: React.FC<SGDS.Component.Accordion.Item> = ({
|
|
|
13
13
|
open = false,
|
|
14
14
|
title,
|
|
15
15
|
...props
|
|
16
|
-
}) => {
|
|
16
|
+
}: SGDS.Component.Accordion.Item) => {
|
|
17
17
|
accordionItemCounter = accordionItemCounter + 1;
|
|
18
18
|
const processedId = rawId || `accordion-item-${accordionItemCounter}`;
|
|
19
19
|
return (
|
|
@@ -43,7 +43,7 @@ const AccordionItem: React.FC<SGDS.Component.Accordion.Item> = ({
|
|
|
43
43
|
>
|
|
44
44
|
{title}
|
|
45
45
|
</WrapperTag>
|
|
46
|
-
<span className=
|
|
46
|
+
<span className="ds_accordion-item__indicator" />
|
|
47
47
|
<label
|
|
48
48
|
className="ds_accordion-item__label"
|
|
49
49
|
htmlFor={`${processedId}-control`}
|
|
@@ -58,14 +58,13 @@ const AccordionItem: React.FC<SGDS.Component.Accordion.Item> = ({
|
|
|
58
58
|
);
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
const Accordion
|
|
62
|
-
& { Item: React.FC<SGDS.Component.Accordion.Item> } = ({
|
|
61
|
+
const Accordion = ({
|
|
63
62
|
children,
|
|
64
63
|
className,
|
|
65
64
|
headingLevel = 'h3',
|
|
66
65
|
hideOpenAll,
|
|
67
66
|
...props
|
|
68
|
-
}) => {
|
|
67
|
+
}: SGDS.Component.Accordion) => {
|
|
69
68
|
const ref = useRef(null);
|
|
70
69
|
|
|
71
70
|
useEffect(() => {
|
|
@@ -112,7 +111,7 @@ const Accordion: React.FC<SGDS.Component.Accordion>
|
|
|
112
111
|
};
|
|
113
112
|
|
|
114
113
|
Accordion.displayName = 'Accordion';
|
|
115
|
-
AccordionItem.displayName = '
|
|
114
|
+
AccordionItem.displayName = 'Accordion.Item';
|
|
116
115
|
Accordion.Item = AccordionItem;
|
|
117
116
|
|
|
118
117
|
export default Accordion;
|
|
@@ -2,12 +2,12 @@ import React, { Children, useEffect, useRef } from 'react';
|
|
|
2
2
|
// @ts-ignore
|
|
3
3
|
import DSAspectBox from '@scottish-government/design-system/src/components/aspect-box/aspect-box-fallback';
|
|
4
4
|
|
|
5
|
-
const AspectBox
|
|
5
|
+
const AspectBox = ({
|
|
6
6
|
children,
|
|
7
7
|
className,
|
|
8
8
|
ratio,
|
|
9
9
|
...props
|
|
10
|
-
}) => {
|
|
10
|
+
}: SGDS.Component.AspectBox) => {
|
|
11
11
|
const ref = useRef(null);
|
|
12
12
|
|
|
13
13
|
useEffect(() => {
|
|
@@ -3,11 +3,11 @@ import Icon from '../../common/Icon';
|
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
import DSBackToTop from '@scottish-government/design-system/src/components/back-to-top/back-to-top';
|
|
5
5
|
|
|
6
|
-
const BackToTop
|
|
6
|
+
const BackToTop = ({
|
|
7
7
|
className,
|
|
8
8
|
href = '#page-top',
|
|
9
9
|
...props
|
|
10
|
-
}) => {
|
|
10
|
+
}: SGDS.Component.BackToTop) => {
|
|
11
11
|
const ref = useRef(null);
|
|
12
12
|
|
|
13
13
|
useEffect(() => {
|
|
@@ -2,15 +2,16 @@ import { test, expect } from 'vitest';
|
|
|
2
2
|
import { render, screen, within } from '@testing-library/react';
|
|
3
3
|
import Breadcrumbs from './Breadcrumbs';
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
{ href: 'category', title: 'Category' },
|
|
8
|
-
{ title: 'Page' }
|
|
9
|
-
];
|
|
5
|
+
const LINK_HREF = '#home';
|
|
6
|
+
const LINK_TEXT = 'Home';
|
|
10
7
|
|
|
11
|
-
test('
|
|
8
|
+
test('breadcrumbs render correctly', () => {
|
|
12
9
|
render(
|
|
13
|
-
<Breadcrumbs
|
|
10
|
+
<Breadcrumbs>
|
|
11
|
+
<Breadcrumbs.Item href="home">Home</Breadcrumbs.Item>
|
|
12
|
+
<Breadcrumbs.Item href="category">Category</Breadcrumbs.Item>
|
|
13
|
+
<Breadcrumbs.Item>Page</Breadcrumbs.Item>
|
|
14
|
+
</Breadcrumbs>
|
|
14
15
|
);
|
|
15
16
|
|
|
16
17
|
const nav = screen.getByRole('navigation');
|
|
@@ -23,67 +24,98 @@ test('renders correctly', () => {
|
|
|
23
24
|
// check list
|
|
24
25
|
expect(list.tagName).toEqual('OL');
|
|
25
26
|
expect(list).toHaveClass('ds_breadcrumbs');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('passing additional props to breadcrumbs', () => {
|
|
30
|
+
render(
|
|
31
|
+
<Breadcrumbs data-test="foo"/>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const nav = screen.getByRole('navigation');
|
|
35
|
+
expect(nav.dataset.test).toEqual('foo');
|
|
36
|
+
});
|
|
26
37
|
|
|
27
|
-
|
|
28
|
-
|
|
38
|
+
test('passing additional CSS classes to breadcrumbs', () => {
|
|
39
|
+
render(
|
|
40
|
+
<Breadcrumbs className="foo"/>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const nav = screen.getByRole('navigation');
|
|
44
|
+
expect(nav).toHaveClass('foo');
|
|
45
|
+
});
|
|
29
46
|
|
|
30
|
-
|
|
31
|
-
|
|
47
|
+
test('breadcrumb item with link', () => {
|
|
48
|
+
render(
|
|
49
|
+
<Breadcrumbs.Item href={LINK_HREF}>{LINK_TEXT}</Breadcrumbs.Item>
|
|
50
|
+
);
|
|
32
51
|
|
|
33
|
-
|
|
52
|
+
const item = screen.getByRole('listitem');
|
|
53
|
+
const link = within(item).getByRole('link');
|
|
34
54
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
expect(link).toHaveClass('ds_breadcrumbs__link');
|
|
38
|
-
} else {
|
|
39
|
-
expect(link).toBeNull();
|
|
40
|
-
}
|
|
41
|
-
});
|
|
55
|
+
expect(item).toHaveClass('ds_breadcrumbs__item');
|
|
56
|
+
expect(item?.tagName).toEqual('LI');
|
|
42
57
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
expect(
|
|
58
|
+
expect(link).toHaveClass('ds_breadcrumbs__link');
|
|
59
|
+
expect(link).toHaveAttribute('href', LINK_HREF);
|
|
60
|
+
expect(link?.tagName).toEqual('A');
|
|
61
|
+
expect(link?.textContent).toEqual(LINK_TEXT);
|
|
46
62
|
});
|
|
47
63
|
|
|
48
|
-
test('
|
|
64
|
+
test('breadcrumb item without link', () => {
|
|
49
65
|
render(
|
|
50
|
-
<Breadcrumbs
|
|
51
|
-
hideLastItem
|
|
52
|
-
items={ITEMS}
|
|
53
|
-
/>
|
|
66
|
+
<Breadcrumbs.Item>{LINK_TEXT}</Breadcrumbs.Item>
|
|
54
67
|
);
|
|
55
68
|
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
expect(
|
|
69
|
+
const item = screen.getByRole('listitem');
|
|
70
|
+
const link = within(item).queryByRole('link');
|
|
71
|
+
|
|
72
|
+
expect(item).toHaveClass('ds_breadcrumbs__item');
|
|
73
|
+
expect(item?.tagName).toEqual('LI');
|
|
74
|
+
expect(item?.textContent).toEqual(LINK_TEXT);
|
|
60
75
|
|
|
61
|
-
|
|
62
|
-
const pageCrumb = within(list).getByText('Page');
|
|
63
|
-
expect(pageCrumb).toHaveClass('visually-hidden');
|
|
64
|
-
expect(pageCrumb.tagName).toEqual('LI');
|
|
76
|
+
expect(link).not.toBeInTheDocument();
|
|
65
77
|
});
|
|
66
78
|
|
|
67
|
-
test('
|
|
79
|
+
test('hidden breadcrumb item', () => {
|
|
68
80
|
render(
|
|
69
|
-
<Breadcrumbs
|
|
70
|
-
items={ITEMS}
|
|
71
|
-
data-test="foo"
|
|
72
|
-
/>
|
|
81
|
+
<Breadcrumbs.Item data-testid="Breadcrumbs.Item" isHidden>{LINK_TEXT}</Breadcrumbs.Item>
|
|
73
82
|
);
|
|
74
83
|
|
|
75
|
-
const
|
|
84
|
+
const item = screen.getByRole('listitem');
|
|
85
|
+
expect(item).toHaveClass('visually-hidden');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('renders breadcrumb with custom element', () => {
|
|
89
|
+
render(
|
|
90
|
+
<Breadcrumbs.Item href="category" linkComponent={
|
|
91
|
+
({ className, ...props }) => (
|
|
92
|
+
<span role="link" className={className} {...props}/>
|
|
93
|
+
)}>
|
|
94
|
+
{LINK_TEXT}
|
|
95
|
+
</Breadcrumbs.Item>
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const item = screen.getByRole('listitem');
|
|
99
|
+
const link = within(item).queryByRole('link');
|
|
100
|
+
|
|
101
|
+
expect(link?.tagName).toEqual('SPAN');
|
|
102
|
+
expect(link?.textContent).toEqual(LINK_TEXT);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('passing additional props to breadcrumb item', () => {
|
|
106
|
+
render(
|
|
107
|
+
<Breadcrumbs.Item data-test="foo"/>
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const nav = screen.getByRole('listitem');
|
|
76
111
|
expect(nav.dataset.test).toEqual('foo');
|
|
77
112
|
});
|
|
78
113
|
|
|
79
|
-
test('passing additional CSS classes', () => {
|
|
114
|
+
test('passing additional CSS classes to breadcrumb item', () => {
|
|
80
115
|
render(
|
|
81
|
-
<Breadcrumbs
|
|
82
|
-
items={ITEMS}
|
|
83
|
-
className="foo"
|
|
84
|
-
/>
|
|
116
|
+
<Breadcrumbs.Item className="foo"/>
|
|
85
117
|
);
|
|
86
118
|
|
|
87
|
-
const nav = screen.getByRole('
|
|
119
|
+
const nav = screen.getByRole('listitem');
|
|
88
120
|
expect(nav).toHaveClass('foo');
|
|
89
121
|
});
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
1
|
+
const BreadcrumbItem = ({
|
|
2
|
+
children,
|
|
3
|
+
isHidden,
|
|
3
4
|
href,
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
linkComponent,
|
|
6
|
+
...props
|
|
7
|
+
}: SGDS.Component.Breadcrumbs.Item) => {
|
|
8
|
+
const BREADCRUMB_LINK_CLASSNAME = 'ds_breadcrumbs__link';
|
|
9
|
+
|
|
10
|
+
function processChildren(children: React.ReactNode) {
|
|
11
|
+
if (linkComponent) {
|
|
12
|
+
return linkComponent({ className: BREADCRUMB_LINK_CLASSNAME, href, children });
|
|
13
|
+
} else if (href) {
|
|
14
|
+
return <a href={href} className={BREADCRUMB_LINK_CLASSNAME}>{children}</a>;
|
|
15
|
+
} else {
|
|
16
|
+
return children;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
6
20
|
return (
|
|
7
|
-
<li
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
<li className={[
|
|
22
|
+
'ds_breadcrumbs__item',
|
|
23
|
+
isHidden && 'visually-hidden'
|
|
24
|
+
].join(' ')}
|
|
25
|
+
{...props}
|
|
12
26
|
>
|
|
13
|
-
|
|
14
|
-
{href ? (<a className="ds_breadcrumbs__link" href={href}>
|
|
15
|
-
{title}
|
|
16
|
-
</a>) : (title)}
|
|
27
|
+
{processChildren(children)}
|
|
17
28
|
</li>
|
|
18
29
|
);
|
|
19
30
|
};
|
|
20
31
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* @param {Array} items
|
|
24
|
-
* @param {Object} props - Properties for the element
|
|
25
|
-
* @returns {JSX.Element} - The element
|
|
26
|
-
*/
|
|
27
|
-
const Breadcrumbs: React.FC<SGDS.Component.Breadcrumbs> = ({
|
|
28
|
-
hideLastItem,
|
|
29
|
-
items,
|
|
32
|
+
const Breadcrumbs = ({
|
|
33
|
+
children,
|
|
30
34
|
...props
|
|
31
|
-
}) => {
|
|
35
|
+
}: SGDS.Component.Breadcrumbs & { Item: SGDS.Component.Breadcrumbs.Item }) => {
|
|
32
36
|
return (
|
|
33
37
|
<nav
|
|
34
38
|
aria-label="Breadcrumb"
|
|
35
39
|
{...props}
|
|
36
40
|
>
|
|
37
41
|
<ol className="ds_breadcrumbs">
|
|
38
|
-
{
|
|
39
|
-
<Breadcrumb
|
|
40
|
-
title={item.title}
|
|
41
|
-
href={item.href}
|
|
42
|
-
hidden={(hideLastItem) && index + 1 === items.length}
|
|
43
|
-
key={'breadcrumb' + index}
|
|
44
|
-
/>
|
|
45
|
-
))}
|
|
42
|
+
{children}
|
|
46
43
|
</ol>
|
|
47
44
|
</nav>
|
|
48
45
|
);
|
|
49
46
|
};
|
|
50
47
|
|
|
51
48
|
Breadcrumbs.displayName = 'Breadcrumbs';
|
|
49
|
+
BreadcrumbItem.displayName = 'Breadcrumbs.Item';
|
|
50
|
+
|
|
51
|
+
Breadcrumbs.Item = BreadcrumbItem;
|
|
52
52
|
|
|
53
53
|
export default Breadcrumbs;
|
|
@@ -2,7 +2,7 @@ import Icon from '../../common/Icon';
|
|
|
2
2
|
import ScreenReaderText from '../../common/ScreenReaderText';
|
|
3
3
|
import WrapperTag from '../../common/WrapperTag';
|
|
4
4
|
|
|
5
|
-
const Button
|
|
5
|
+
const Button = ({
|
|
6
6
|
buttonStyle,
|
|
7
7
|
children,
|
|
8
8
|
className,
|
|
@@ -15,7 +15,7 @@ const Button: React.FC<SGDS.Component.Button> = ({
|
|
|
15
15
|
type = 'button',
|
|
16
16
|
width,
|
|
17
17
|
...props
|
|
18
|
-
}) => {
|
|
18
|
+
}: SGDS.Component.Button) => {
|
|
19
19
|
// determine which HTML tag to use
|
|
20
20
|
let tagName = 'button';
|
|
21
21
|
if (href) {
|