@arbor-education/design-system.components 0.25.3 → 0.25.5

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 (110) hide show
  1. package/.gather/skills/write-stories/README.md +1 -1
  2. package/.gather/skills/write-stories/SKILL.md +1 -1
  3. package/.gather/skills/write-stories/meta.md +1 -1
  4. package/CHANGELOG.md +16 -0
  5. package/component-library.md +26 -1
  6. package/dist/components/breadcrumbs/BreadcrumbCopyButton.d.ts +3 -0
  7. package/dist/components/breadcrumbs/BreadcrumbCopyButton.d.ts.map +1 -0
  8. package/dist/components/breadcrumbs/BreadcrumbCopyButton.js +13 -0
  9. package/dist/components/breadcrumbs/BreadcrumbCopyButton.js.map +1 -0
  10. package/dist/components/breadcrumbs/BreadcrumbDropdown.d.ts +3 -0
  11. package/dist/components/breadcrumbs/BreadcrumbDropdown.d.ts.map +1 -0
  12. package/dist/components/breadcrumbs/BreadcrumbDropdown.js +8 -0
  13. package/dist/components/breadcrumbs/BreadcrumbDropdown.js.map +1 -0
  14. package/dist/components/breadcrumbs/BreadcrumbEllipsis.d.ts +3 -0
  15. package/dist/components/breadcrumbs/BreadcrumbEllipsis.d.ts.map +1 -0
  16. package/dist/components/breadcrumbs/BreadcrumbEllipsis.js +7 -0
  17. package/dist/components/breadcrumbs/BreadcrumbEllipsis.js.map +1 -0
  18. package/dist/components/breadcrumbs/BreadcrumbItemSeparator.d.ts +2 -0
  19. package/dist/components/breadcrumbs/BreadcrumbItemSeparator.d.ts.map +1 -0
  20. package/dist/components/breadcrumbs/BreadcrumbItemSeparator.js +5 -0
  21. package/dist/components/breadcrumbs/BreadcrumbItemSeparator.js.map +1 -0
  22. package/dist/components/breadcrumbs/BreadcrumbLink.d.ts +6 -0
  23. package/dist/components/breadcrumbs/BreadcrumbLink.d.ts.map +1 -0
  24. package/dist/components/breadcrumbs/BreadcrumbLink.js +9 -0
  25. package/dist/components/breadcrumbs/BreadcrumbLink.js.map +1 -0
  26. package/dist/components/breadcrumbs/Breadcrumbs.d.ts +21 -0
  27. package/dist/components/breadcrumbs/Breadcrumbs.d.ts.map +1 -0
  28. package/dist/components/breadcrumbs/Breadcrumbs.js +51 -0
  29. package/dist/components/breadcrumbs/Breadcrumbs.js.map +1 -0
  30. package/dist/components/breadcrumbs/Breadcrumbs.stories.d.ts +81 -0
  31. package/dist/components/breadcrumbs/Breadcrumbs.stories.d.ts.map +1 -0
  32. package/dist/components/breadcrumbs/Breadcrumbs.stories.js +681 -0
  33. package/dist/components/breadcrumbs/Breadcrumbs.stories.js.map +1 -0
  34. package/dist/components/breadcrumbs/Breadcrumbs.test.d.ts +2 -0
  35. package/dist/components/breadcrumbs/Breadcrumbs.test.d.ts.map +1 -0
  36. package/dist/components/breadcrumbs/Breadcrumbs.test.js +160 -0
  37. package/dist/components/breadcrumbs/Breadcrumbs.test.js.map +1 -0
  38. package/dist/components/breadcrumbs/types.d.ts +27 -0
  39. package/dist/components/breadcrumbs/types.d.ts.map +1 -0
  40. package/dist/components/breadcrumbs/types.js +2 -0
  41. package/dist/components/breadcrumbs/types.js.map +1 -0
  42. package/dist/components/fileUpload/FileUpload.d.ts +21 -0
  43. package/dist/components/fileUpload/FileUpload.d.ts.map +1 -0
  44. package/dist/components/fileUpload/FileUpload.js +23 -0
  45. package/dist/components/fileUpload/FileUpload.js.map +1 -0
  46. package/dist/components/fileUpload/FileUpload.stories.d.ts +71 -0
  47. package/dist/components/fileUpload/FileUpload.stories.d.ts.map +1 -0
  48. package/dist/components/fileUpload/FileUpload.stories.js +736 -0
  49. package/dist/components/fileUpload/FileUpload.stories.js.map +1 -0
  50. package/dist/components/fileUpload/FileUpload.test.d.ts +2 -0
  51. package/dist/components/fileUpload/FileUpload.test.d.ts.map +1 -0
  52. package/dist/components/fileUpload/FileUpload.test.js +99 -0
  53. package/dist/components/fileUpload/FileUpload.test.js.map +1 -0
  54. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js +1 -1
  55. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js.map +1 -1
  56. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.d.ts.map +1 -1
  57. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js +14 -15
  58. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.stories.js.map +1 -1
  59. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.test.js +11 -0
  60. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.test.js.map +1 -1
  61. package/dist/components/icon/allowedIcons.d.ts +1 -0
  62. package/dist/components/icon/allowedIcons.d.ts.map +1 -1
  63. package/dist/components/icon/allowedIcons.js +2 -1
  64. package/dist/components/icon/allowedIcons.js.map +1 -1
  65. package/dist/components/skeleton/Skeleton.d.ts +10 -0
  66. package/dist/components/skeleton/Skeleton.d.ts.map +1 -0
  67. package/dist/components/skeleton/Skeleton.js +9 -0
  68. package/dist/components/skeleton/Skeleton.js.map +1 -0
  69. package/dist/components/skeleton/Skeleton.stories.d.ts +99 -0
  70. package/dist/components/skeleton/Skeleton.stories.d.ts.map +1 -0
  71. package/dist/components/skeleton/Skeleton.stories.js +539 -0
  72. package/dist/components/skeleton/Skeleton.stories.js.map +1 -0
  73. package/dist/components/skeleton/Skeleton.test.d.ts +2 -0
  74. package/dist/components/skeleton/Skeleton.test.d.ts.map +1 -0
  75. package/dist/components/skeleton/Skeleton.test.js +49 -0
  76. package/dist/components/skeleton/Skeleton.test.js.map +1 -0
  77. package/dist/index.css +225 -0
  78. package/dist/index.css.map +1 -1
  79. package/dist/index.d.ts +3 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +3 -0
  82. package/dist/index.js.map +1 -1
  83. package/package.json +2 -1
  84. package/src/components/breadcrumbs/BreadcrumbCopyButton.tsx +27 -0
  85. package/src/components/breadcrumbs/BreadcrumbDropdown.tsx +27 -0
  86. package/src/components/breadcrumbs/BreadcrumbEllipsis.tsx +16 -0
  87. package/src/components/breadcrumbs/BreadcrumbItemSeparator.tsx +3 -0
  88. package/src/components/breadcrumbs/BreadcrumbLink.tsx +29 -0
  89. package/src/components/breadcrumbs/Breadcrumbs.stories.tsx +870 -0
  90. package/src/components/breadcrumbs/Breadcrumbs.test.tsx +185 -0
  91. package/src/components/breadcrumbs/Breadcrumbs.tsx +92 -0
  92. package/src/components/breadcrumbs/breadcrumbs.scss +158 -0
  93. package/src/components/breadcrumbs/types.ts +31 -0
  94. package/src/components/button/button.scss +2 -0
  95. package/src/components/dropdown/dropdown.scss +1 -0
  96. package/src/components/fileUpload/FileUpload.stories.tsx +920 -0
  97. package/src/components/fileUpload/FileUpload.test.tsx +172 -0
  98. package/src/components/fileUpload/FileUpload.tsx +144 -0
  99. package/src/components/fileUpload/fileUpload.scss +74 -0
  100. package/src/components/formField/inputs/selectDropdown/SelectDropdown.stories.tsx +15 -16
  101. package/src/components/formField/inputs/selectDropdown/SelectDropdown.test.tsx +19 -0
  102. package/src/components/formField/inputs/selectDropdown/SelectDropdown.tsx +1 -1
  103. package/src/components/icon/allowedIcons.tsx +2 -0
  104. package/src/components/progress/progress.scss +1 -0
  105. package/src/components/skeleton/Skeleton.stories.tsx +748 -0
  106. package/src/components/skeleton/Skeleton.test.tsx +55 -0
  107. package/src/components/skeleton/Skeleton.tsx +36 -0
  108. package/src/components/skeleton/skeleton.scss +23 -0
  109. package/src/index.scss +3 -0
  110. package/src/index.ts +3 -0
@@ -0,0 +1,185 @@
1
+ import { describe, expect, test, vi, beforeEach } from 'vitest';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom/vitest';
4
+ import { Breadcrumbs } from './Breadcrumbs.js';
5
+
6
+ const basicItems = [
7
+ { label: 'Home', href: '/' },
8
+ { label: 'Library', href: '/library' },
9
+ { label: 'Current page', href: '/library/current' },
10
+ ];
11
+
12
+ describe('Breadcrumbs', () => {
13
+ test('renders a nav element', () => {
14
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
15
+ expect(screen.getByRole('navigation')).toBeInTheDocument();
16
+ });
17
+
18
+ test('uses breadcrumbLabel as aria-label', () => {
19
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Page navigation" />);
20
+ expect(screen.getByRole('navigation')).toHaveAttribute('aria-label', 'Page navigation');
21
+ });
22
+
23
+ test('renders all item labels', () => {
24
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
25
+ expect(screen.getByText('Home')).toBeInTheDocument();
26
+ expect(screen.getByText('Library')).toBeInTheDocument();
27
+ expect(screen.getByText('Current page')).toBeInTheDocument();
28
+ });
29
+
30
+ test('applies a custom className to the nav element', () => {
31
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" className="custom-class" />);
32
+ expect(screen.getByRole('navigation')).toHaveClass('custom-class');
33
+ });
34
+
35
+ describe('active item', () => {
36
+ test('marks the last item as aria-current="page"', () => {
37
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
38
+ expect(screen.getByText('Current page').closest('a')).toHaveAttribute('aria-current', 'page');
39
+ });
40
+
41
+ test('does not mark non-last items as aria-current', () => {
42
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
43
+ expect(screen.getByText('Home').closest('a')).not.toHaveAttribute('aria-current');
44
+ expect(screen.getByText('Library').closest('a')).not.toHaveAttribute('aria-current');
45
+ });
46
+
47
+ test('does not have an href — the current page is non-navigable', () => {
48
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
49
+ expect(screen.getByText('Current page').closest('a')).not.toHaveAttribute('href');
50
+ });
51
+ });
52
+
53
+ describe('separators', () => {
54
+ test('renders a separator between each item but not after the last', () => {
55
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
56
+ const separators = screen.getAllByText('/', { selector: '[aria-hidden]' });
57
+ expect(separators).toHaveLength(basicItems.length - 1);
58
+ });
59
+ });
60
+
61
+ describe('copy button', () => {
62
+ beforeEach(() => {
63
+ Object.defineProperty(navigator, 'clipboard', {
64
+ value: { writeText: vi.fn().mockResolvedValue(undefined) },
65
+ configurable: true,
66
+ });
67
+ });
68
+
69
+ test('renders a copy button on the last item', () => {
70
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
71
+ expect(screen.getByRole('button', { name: 'Copy breadcrumb path' })).toBeInTheDocument();
72
+ });
73
+
74
+ test('copies item labels joined with " > " to clipboard on click', () => {
75
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
76
+ fireEvent.click(screen.getByRole('button', { name: 'Copy breadcrumb path' }));
77
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith('Home > Library > Current page');
78
+ });
79
+
80
+ test('uses copyText prop when provided instead of deriving from items', () => {
81
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" copyText="Custom > Path > Override" />);
82
+ fireEvent.click(screen.getByRole('button', { name: 'Copy breadcrumb path' }));
83
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith('Custom > Path > Override');
84
+ });
85
+
86
+ test('excludes ellipsis item labels from the copied path', () => {
87
+ const itemsWithEllipsis = [
88
+ { label: 'Home', href: '/' },
89
+ { label: 'Show hidden pages', isEllipsis: true as const },
90
+ { label: 'Year 11', href: '/students/year-11' },
91
+ { label: 'Attendance', href: '/students/year-11/attendance' },
92
+ ];
93
+ render(<Breadcrumbs items={itemsWithEllipsis} breadcrumbLabel="Breadcrumb" />);
94
+ fireEvent.click(screen.getByRole('button', { name: 'Copy breadcrumb path' }));
95
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith('Home > Year 11 > Attendance');
96
+ });
97
+
98
+ test('shows a check icon immediately after clicking', () => {
99
+ render(<Breadcrumbs items={basicItems} breadcrumbLabel="Breadcrumb" />);
100
+ fireEvent.click(screen.getByRole('button', { name: 'Copy breadcrumb path' }));
101
+ expect(screen.getByRole('button', { name: 'Breadcrumb path copied' })).toBeInTheDocument();
102
+ });
103
+ });
104
+
105
+ describe('ellipsis item', () => {
106
+ const itemsWithEllipsis = [
107
+ { label: 'Home', href: '/' },
108
+ { label: 'More pages', isEllipsis: true as const },
109
+ { label: 'Current page', href: '/current' },
110
+ ];
111
+
112
+ const itemsWithEllipsisLast = [
113
+ { label: 'Home', href: '/' },
114
+ { label: 'More pages', isEllipsis: true as const },
115
+ ];
116
+
117
+ test('renders an ellipsis button with accessible label from item label', () => {
118
+ render(<Breadcrumbs items={itemsWithEllipsis} breadcrumbLabel="Breadcrumb" />);
119
+ expect(screen.getByRole('button', { name: 'More pages' })).toBeInTheDocument();
120
+ });
121
+
122
+ test('does not render the ellipsis item as a link', () => {
123
+ render(<Breadcrumbs items={itemsWithEllipsis} breadcrumbLabel="Breadcrumb" />);
124
+ const links = screen.getAllByRole('link');
125
+ expect(links.every(link => link.textContent !== 'More pages')).toBe(true);
126
+ });
127
+
128
+ test('does not render a copy button when the last item is an ellipsis', () => {
129
+ render(<Breadcrumbs items={itemsWithEllipsisLast} breadcrumbLabel="Breadcrumb" />);
130
+ expect(screen.queryByRole('button', { name: 'Copy breadcrumb path' })).not.toBeInTheDocument();
131
+ });
132
+
133
+ test('forwards onClick from the item to the ellipsis button', () => {
134
+ const handleClick = vi.fn();
135
+ const items = [
136
+ { label: 'Home', href: '/' },
137
+ { label: 'More pages', isEllipsis: true as const, onClick: handleClick },
138
+ { label: 'Current page', href: '/current' },
139
+ ];
140
+ render(<Breadcrumbs items={items} breadcrumbLabel="Breadcrumb" />);
141
+ fireEvent.click(screen.getByRole('button', { name: 'More pages' }));
142
+ expect(handleClick).toHaveBeenCalledTimes(1);
143
+ });
144
+ });
145
+
146
+ describe('dropdown item', () => {
147
+ const itemsWithDropdown = [
148
+ { label: 'Home', href: '/' },
149
+ {
150
+ label: 'Students',
151
+ href: '/students',
152
+ dropdownOptions: [
153
+ { label: 'All students', href: '/students' },
154
+ { label: 'My tutor group', href: '/students/tutor-group' },
155
+ ],
156
+ },
157
+ { label: 'Current page', href: '/current' },
158
+ ];
159
+
160
+ test('renders the dropdown trigger with the item label', () => {
161
+ render(<Breadcrumbs items={itemsWithDropdown} breadcrumbLabel="Breadcrumb" />);
162
+ expect(screen.getByText('Students')).toBeInTheDocument();
163
+ });
164
+
165
+ test('renders the dropdown trigger with role="button"', () => {
166
+ render(<Breadcrumbs items={itemsWithDropdown} breadcrumbLabel="Breadcrumb" />);
167
+ expect(screen.getByRole('button', { name: 'Students' })).toBeInTheDocument();
168
+ });
169
+
170
+ test('renders the dropdown trigger with aria-haspopup="menu"', () => {
171
+ render(<Breadcrumbs items={itemsWithDropdown} breadcrumbLabel="Breadcrumb" />);
172
+ expect(screen.getByRole('button', { name: 'Students' })).toHaveAttribute('aria-haspopup', 'menu');
173
+ });
174
+
175
+ test('does not have an href — the dropdown trigger is not a link', () => {
176
+ render(<Breadcrumbs items={itemsWithDropdown} breadcrumbLabel="Breadcrumb" />);
177
+ expect(screen.getByRole('button', { name: 'Students' })).not.toHaveAttribute('href');
178
+ });
179
+
180
+ test('has tabIndex=0 so it remains keyboard focusable without an href', () => {
181
+ render(<Breadcrumbs items={itemsWithDropdown} breadcrumbLabel="Breadcrumb" />);
182
+ expect(screen.getByRole('button', { name: 'Students' })).toHaveAttribute('tabindex', '0');
183
+ });
184
+ });
185
+ });
@@ -0,0 +1,92 @@
1
+ import { BreadcrumbLink } from './BreadcrumbLink.js';
2
+ import { BreadcrumbEllipsis } from './BreadcrumbEllipsis.js';
3
+ import { BreadcrumbItemSeparator } from './BreadcrumbItemSeparator.js';
4
+ import { BreadcrumbDropdown } from './BreadcrumbDropdown.js';
5
+ import type {
6
+ BreadcrumbsProps,
7
+ BreadcrumbLinkProps,
8
+ BreadcrumbEllipsisProps,
9
+ BreadcrumbDropdownProps,
10
+ BreadcrumbCopyButtonProps,
11
+ BreadcrumbItem,
12
+ } from './types.js';
13
+ import classNames from 'classnames';
14
+ import { BreadcrumbCopyButton } from './BreadcrumbCopyButton.js';
15
+ import { useEffect, useRef, useState } from 'react';
16
+
17
+ const COPY_FEEDBACK_DURATION_MS = 2000;
18
+
19
+ const renderLink = (item: BreadcrumbItem, isLast: boolean) => {
20
+ const hasDropdownOptions = Boolean(item.dropdownOptions?.length);
21
+ if (item.isEllipsis) {
22
+ return <BreadcrumbEllipsis screenReaderText={item.label} onClick={item.onClick} />;
23
+ }
24
+ if (hasDropdownOptions) {
25
+ return <BreadcrumbDropdown item={item} />;
26
+ }
27
+ return <BreadcrumbLink item={item} isActive={isLast} />;
28
+ };
29
+
30
+ export const Breadcrumbs = (props: BreadcrumbsProps) => {
31
+ const { items, breadcrumbLabel = 'Breadcrumbs', className, copyText, ...rest } = props;
32
+ const [copyButtonClicked, setCopyButtonClicked] = useState(false);
33
+ const copyTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
34
+
35
+ useEffect(() => () => clearTimeout(copyTimerRef.current), []);
36
+
37
+ const handleCopyButtonClick = () => {
38
+ if (!navigator.clipboard) {
39
+ return;
40
+ }
41
+ setCopyButtonClicked(true);
42
+ navigator.clipboard
43
+ .writeText(copyText ?? items.filter(item => !item.isEllipsis).map(item => item.label).join(' > '))
44
+ .catch(() => {})
45
+ .finally(() => {
46
+ copyTimerRef.current = setTimeout(() => {
47
+ setCopyButtonClicked(false);
48
+ }, COPY_FEEDBACK_DURATION_MS);
49
+ });
50
+ };
51
+
52
+ return (
53
+ <nav
54
+ {...rest}
55
+ aria-label={breadcrumbLabel}
56
+ className={classNames('ds-breadcrumbs__container', className)}
57
+ >
58
+ <ol className="ds-breadcrumbs__list">
59
+ {items.map((item, index) => {
60
+ const isLast = index === items.length - 1;
61
+
62
+ return (
63
+ <li key={`${item.label}-${index}`} className="ds-breadcrumbs__item">
64
+ {renderLink(item, isLast)}
65
+ {isLast
66
+ ? (
67
+ !item.isEllipsis && <BreadcrumbCopyButton isClicked={copyButtonClicked} onClick={handleCopyButtonClick} />
68
+ )
69
+ : (
70
+ <BreadcrumbItemSeparator />
71
+ )}
72
+ </li>
73
+ );
74
+ })}
75
+ </ol>
76
+ </nav>
77
+ );
78
+ };
79
+
80
+ Breadcrumbs.Link = BreadcrumbLink;
81
+ Breadcrumbs.Ellipsis = BreadcrumbEllipsis;
82
+ Breadcrumbs.ItemSeparator = BreadcrumbItemSeparator;
83
+ Breadcrumbs.Dropdown = BreadcrumbDropdown;
84
+ Breadcrumbs.CopyButton = BreadcrumbCopyButton;
85
+
86
+ export namespace Breadcrumbs {
87
+ export type Props = BreadcrumbsProps;
88
+ export type LinkProps = BreadcrumbLinkProps;
89
+ export type EllipsisProps = BreadcrumbEllipsisProps;
90
+ export type DropdownProps = BreadcrumbDropdownProps;
91
+ export type CopyButtonProps = BreadcrumbCopyButtonProps;
92
+ }
@@ -0,0 +1,158 @@
1
+ .ds-breadcrumbs__container {
2
+ display: flex;
3
+ align-items: center;
4
+ }
5
+
6
+ .ds-breadcrumbs__list {
7
+ display: flex;
8
+ flex-wrap: wrap;
9
+ align-items: center;
10
+ gap: var(--breadcrumbs-spacing-gap-horizontal);
11
+ padding: 0;
12
+ margin: 0;
13
+ list-style: none;
14
+ }
15
+
16
+ .ds-breadcrumbs__item {
17
+ display: inline-flex;
18
+ align-items: center;
19
+ gap: var(--breadcrumbs-breadcrumb-link-spacing-gap-horizontal);
20
+ }
21
+
22
+ .ds-breadcrumbs__label {
23
+ font-family: var(--type-body-p-family);
24
+ font-size: var(--type-body-p-size);
25
+ font-weight: var(--type-body-p-weight);
26
+ color: var(--breadcrumbs-breadcrumb-link-default-color-text);
27
+ line-height: var(--type-body-line-height);
28
+ }
29
+
30
+ .ds-breadcrumbs__link {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ gap: var(--breadcrumbs-breadcrumb-link-spacing-gap-horizontal);
34
+ text-decoration: none;
35
+ cursor: pointer;
36
+ border-radius: var(--spacing-small);
37
+
38
+ &:hover {
39
+ .ds-breadcrumbs__label {
40
+ color: var(--breadcrumbs-breadcrumb-link-hover-color-text);
41
+ }
42
+ }
43
+
44
+ &:focus-visible {
45
+ border: none;
46
+ outline: none;
47
+ box-shadow: 0 0 0 3px var(--color-brand-300);
48
+ text-decoration: none;
49
+ }
50
+
51
+
52
+ &[aria-current="page"] {
53
+ .ds-breadcrumbs__label {
54
+ font-family: var(--type-body-bold-family);
55
+ font-weight: var(--type-body-bold-weight);
56
+ font-size: var(--type-body-bold-size);
57
+ color: var(--breadcrumbs-breadcrumb-link-active-color-text);
58
+
59
+ &:hover {
60
+ color: var(--breadcrumbs-breadcrumb-link-hover-color-text);
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ .ds-breadcrumbs__separator {
67
+ display: inline-flex;
68
+ align-items: center;
69
+ margin: 0 var(--breadcrumbs-breadcrumb-link-default-spacing-gap-horizontal);
70
+ color: var(--breadcrumbs-divider-default-color);
71
+ user-select: none;
72
+ }
73
+
74
+ .ds-button--icon-only.ds-button--S.ds-breadcrumbs__ellipsis-button {
75
+ display: inline-flex;
76
+ align-items: center;
77
+ justify-content: center;
78
+ padding: 0;
79
+ background-color: transparent;
80
+ border: none;
81
+ border-radius: var(--breadcrumbs-elipsis-radius);
82
+ cursor: pointer;
83
+ width: calc(16px + 2 * var(--spacing-xsmall));
84
+ height: calc(16px + 2 * var(--spacing-xsmall));
85
+
86
+ svg {
87
+ color: var(--breadcrumbs-elipsis-default-color);
88
+ width: var(--icon-size-small);
89
+ height: var(--icon-size-small);
90
+ }
91
+
92
+ &:active {
93
+ color: var(--breadcrumbs-elipsis-hover-color);
94
+ background-color: var(--breadcrumbs-elipsis-hover-colour-background);
95
+ }
96
+
97
+ &:hover {
98
+ color: var(--breadcrumbs-elipsis-hover-color);
99
+ background-color: var(--breadcrumbs-elipsis-hover-colour-background);
100
+ }
101
+
102
+ &:focus-visible {
103
+ outline: none;
104
+ box-shadow: 0 0 0 var(--focus-border) var(--color-brand-300);
105
+ background-color: transparent;
106
+ }
107
+
108
+ &:focus:not(:focus-visible) {
109
+ outline: none;
110
+ box-shadow: none;
111
+ }
112
+ }
113
+
114
+ .ds-button--icon-only.ds-button--S.ds-breadcrumbs__copy-icon {
115
+ display: inline-flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ padding: 0;
119
+ background-color: transparent;
120
+ border: none;
121
+ border-radius: var(--breadcrumbs-copy-icon-radius);
122
+ cursor: pointer;
123
+ width: calc(16px + 2 * var(--spacing-xsmall));
124
+ height: calc(16px + 2 * var(--spacing-xsmall));
125
+
126
+ svg {
127
+ color: var(--breadcrumbs-copy-icon-default-color);
128
+ width: var(--icon-size-xsmall);
129
+ height: var(--icon-size-xsmall);
130
+ }
131
+
132
+ &:active {
133
+ color: var(--breadcrumbs-copy-icon-hover-color);
134
+ background-color: var(--breadcrumbs-copy-icon-hover-color-background);
135
+ }
136
+
137
+ &:hover {
138
+ color: var(--breadcrumbs-copy-icon-hover-color);
139
+ background-color: var(--breadcrumbs-copy-icon-hover-color-background);
140
+ }
141
+
142
+ &:focus-visible {
143
+ outline: none;
144
+ box-shadow: 0 0 0 var(--focus-border) var(--color-brand-300);
145
+ background-color: transparent;
146
+ }
147
+
148
+ &:focus:not(:focus-visible) {
149
+ outline: none;
150
+ box-shadow: none;
151
+ }
152
+
153
+ &--clicked {
154
+ svg {
155
+ color: var(--breadcrumbs-breadcrumb-link-hover-color-icon);
156
+ }
157
+ }
158
+ }
@@ -0,0 +1,31 @@
1
+ export type BreadcrumbItem = {
2
+ label: string;
3
+ href?: string;
4
+ dropdownOptions?: BreadcrumbItem[];
5
+ isEllipsis?: boolean;
6
+ onClick?: () => void;
7
+ };
8
+
9
+ export type BreadcrumbsProps = {
10
+ items: BreadcrumbItem[];
11
+ breadcrumbLabel?: string;
12
+ copyText?: string;
13
+ } & React.HTMLAttributes<HTMLElement>;
14
+
15
+ export type BreadcrumbLinkProps = {
16
+ item: BreadcrumbItem;
17
+ isDropdown?: boolean;
18
+ isActive?: boolean;
19
+ } & React.AnchorHTMLAttributes<HTMLAnchorElement>;
20
+
21
+ export type BreadcrumbEllipsisProps = {
22
+ screenReaderText: string;
23
+ } & React.ButtonHTMLAttributes<HTMLButtonElement>;
24
+
25
+ export type BreadcrumbDropdownProps = {
26
+ item: BreadcrumbItem;
27
+ };
28
+
29
+ export type BreadcrumbCopyButtonProps = {
30
+ isClicked?: boolean;
31
+ } & React.ButtonHTMLAttributes<HTMLButtonElement>;
@@ -206,6 +206,8 @@
206
206
 
207
207
  // Text Link Button
208
208
  &--text-link {
209
+ display: inline;
210
+ padding: 0;
209
211
  background-color: var(--button-medium-text-link-default-color-background);
210
212
  color: var(--button-medium-text-link-default-color-text);
211
213
  border: var(--border-weight) solid var(--button-medium-text-link-default-color-border);
@@ -21,6 +21,7 @@
21
21
  color: var(--form-dropdown-form-drop-item-default-color-text);
22
22
  gap: var(--form-dropdown-form-drop-item-spacing-gap-horizontal);
23
23
  cursor: pointer;
24
+ text-decoration: none;
24
25
 
25
26
  &:hover {
26
27
  background: var(--form-dropdown-form-drop-item-hover-color-background);