@arbor-education/design-system.components 1.0.5 → 2.0.1

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 (117) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/components/breadcrumbs/BreadcrumbCopyButton.d.ts.map +1 -1
  3. package/dist/components/breadcrumbs/BreadcrumbCopyButton.js +3 -2
  4. package/dist/components/breadcrumbs/BreadcrumbCopyButton.js.map +1 -1
  5. package/dist/components/breadcrumbs/BreadcrumbEllipsis.d.ts.map +1 -1
  6. package/dist/components/breadcrumbs/BreadcrumbEllipsis.js +2 -1
  7. package/dist/components/breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
  8. package/dist/components/button/Button.d.ts +17 -8
  9. package/dist/components/button/Button.d.ts.map +1 -1
  10. package/dist/components/button/Button.js +5 -6
  11. package/dist/components/button/Button.js.map +1 -1
  12. package/dist/components/button/Button.stories.d.ts +17 -36
  13. package/dist/components/button/Button.stories.d.ts.map +1 -1
  14. package/dist/components/button/Button.stories.js +165 -214
  15. package/dist/components/button/Button.stories.js.map +1 -1
  16. package/dist/components/button/Button.test.d.ts.map +1 -1
  17. package/dist/components/button/Button.test.js +16 -10
  18. package/dist/components/button/Button.test.js.map +1 -1
  19. package/dist/components/datePicker/DatePicker.d.ts.map +1 -1
  20. package/dist/components/datePicker/DatePicker.js +2 -1
  21. package/dist/components/datePicker/DatePicker.js.map +1 -1
  22. package/dist/components/dateTimePicker/DateTimePicker.d.ts.map +1 -1
  23. package/dist/components/dateTimePicker/DateTimePicker.js +2 -1
  24. package/dist/components/dateTimePicker/DateTimePicker.js.map +1 -1
  25. package/dist/components/dropdown/Dropdown.stories.d.ts.map +1 -1
  26. package/dist/components/dropdown/Dropdown.stories.js +5 -3
  27. package/dist/components/dropdown/Dropdown.stories.js.map +1 -1
  28. package/dist/components/emptyState/EmptyState.stories.d.ts.map +1 -1
  29. package/dist/components/emptyState/EmptyState.stories.js +3 -2
  30. package/dist/components/emptyState/EmptyState.stories.js.map +1 -1
  31. package/dist/components/fileUpload/FileUpload.d.ts.map +1 -1
  32. package/dist/components/fileUpload/FileUpload.js +1 -1
  33. package/dist/components/fileUpload/FileUpload.js.map +1 -1
  34. package/dist/components/fileUpload/FileUpload.test.js +4 -4
  35. package/dist/components/fileUpload/FileUpload.test.js.map +1 -1
  36. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.d.ts.map +1 -1
  37. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.js +2 -1
  38. package/dist/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.js.map +1 -1
  39. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts.map +1 -1
  40. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js +2 -1
  41. package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js.map +1 -1
  42. package/dist/components/grid/Grid.stories.d.ts.map +1 -1
  43. package/dist/components/grid/Grid.stories.js +4 -3
  44. package/dist/components/grid/Grid.stories.js.map +1 -1
  45. package/dist/components/heading/Heading.stories.d.ts.map +1 -1
  46. package/dist/components/heading/Heading.stories.js +18 -14
  47. package/dist/components/heading/Heading.stories.js.map +1 -1
  48. package/dist/components/icon/Icon.stories.d.ts.map +1 -1
  49. package/dist/components/icon/Icon.stories.js +30 -31
  50. package/dist/components/icon/Icon.stories.js.map +1 -1
  51. package/dist/components/modal/ModalCloseButton.d.ts.map +1 -1
  52. package/dist/components/modal/ModalCloseButton.js +2 -1
  53. package/dist/components/modal/ModalCloseButton.js.map +1 -1
  54. package/dist/components/section/Section.stories.d.ts.map +1 -1
  55. package/dist/components/section/Section.stories.js +52 -25
  56. package/dist/components/section/Section.stories.js.map +1 -1
  57. package/dist/components/table/Table.stories.d.ts.map +1 -1
  58. package/dist/components/table/Table.stories.js +4 -6
  59. package/dist/components/table/Table.stories.js.map +1 -1
  60. package/dist/components/table/pagination/PageSizeSelector.d.ts.map +1 -1
  61. package/dist/components/table/pagination/PageSizeSelector.js +2 -1
  62. package/dist/components/table/pagination/PageSizeSelector.js.map +1 -1
  63. package/dist/components/table/pagination/PaginationControls.d.ts.map +1 -1
  64. package/dist/components/table/pagination/PaginationControls.js +3 -2
  65. package/dist/components/table/pagination/PaginationControls.js.map +1 -1
  66. package/dist/components/table/tableControls/BulkActionsDropdown.d.ts.map +1 -1
  67. package/dist/components/table/tableControls/BulkActionsDropdown.js +3 -2
  68. package/dist/components/table/tableControls/BulkActionsDropdown.js.map +1 -1
  69. package/dist/components/table/tableControls/TableControls.d.ts.map +1 -1
  70. package/dist/components/table/tableControls/TableControls.js +2 -1
  71. package/dist/components/table/tableControls/TableControls.js.map +1 -1
  72. package/dist/components/table/tableControls/TableSettingsDropdown.d.ts.map +1 -1
  73. package/dist/components/table/tableControls/TableSettingsDropdown.js +2 -1
  74. package/dist/components/table/tableControls/TableSettingsDropdown.js.map +1 -1
  75. package/dist/components/toast/Toast.js +1 -1
  76. package/dist/components/toast/Toast.js.map +1 -1
  77. package/dist/components/tooltip/Tooltip.stories.js +5 -5
  78. package/dist/components/tooltip/Tooltip.stories.js.map +1 -1
  79. package/dist/components/tooltip/TooltipWrapper.stories.js +2 -2
  80. package/dist/components/tooltip/TooltipWrapper.stories.js.map +1 -1
  81. package/dist/components/userDropdown/internal/UserDropdownTrigger.d.ts.map +1 -1
  82. package/dist/components/userDropdown/internal/UserDropdownTrigger.js +3 -2
  83. package/dist/components/userDropdown/internal/UserDropdownTrigger.js.map +1 -1
  84. package/dist/index.css +16 -16
  85. package/dist/index.css.map +1 -1
  86. package/package.json +1 -1
  87. package/src/components/breadcrumbs/BreadcrumbCopyButton.tsx +5 -2
  88. package/src/components/breadcrumbs/BreadcrumbEllipsis.tsx +5 -2
  89. package/src/components/breadcrumbs/breadcrumbs.scss +2 -2
  90. package/src/components/button/Button.stories.tsx +205 -230
  91. package/src/components/button/Button.test.tsx +36 -9
  92. package/src/components/button/Button.tsx +6 -16
  93. package/src/components/button/button.scss +8 -8
  94. package/src/components/combobox/combobox.scss +1 -1
  95. package/src/components/datePicker/DatePicker.tsx +5 -3
  96. package/src/components/dateTimePicker/DateTimePicker.tsx +5 -3
  97. package/src/components/dropdown/Dropdown.stories.tsx +7 -3
  98. package/src/components/emptyState/EmptyState.stories.tsx +4 -2
  99. package/src/components/fileUpload/FileUpload.test.tsx +4 -4
  100. package/src/components/fileUpload/FileUpload.tsx +8 -4
  101. package/src/components/formField/inputs/colourPickerDropdown/ColourPickerDropdown.tsx +3 -1
  102. package/src/components/formField/inputs/selectDropdown/SelectDropdown.tsx +3 -2
  103. package/src/components/grid/Grid.stories.tsx +17 -4
  104. package/src/components/heading/Heading.stories.tsx +23 -16
  105. package/src/components/icon/Icon.stories.tsx +43 -44
  106. package/src/components/modal/ModalCloseButton.tsx +5 -3
  107. package/src/components/section/Section.stories.tsx +86 -31
  108. package/src/components/table/Table.stories.tsx +19 -6
  109. package/src/components/table/pagination/PageSizeSelector.tsx +3 -1
  110. package/src/components/table/pagination/PaginationControls.tsx +17 -12
  111. package/src/components/table/tableControls/BulkActionsDropdown.tsx +3 -1
  112. package/src/components/table/tableControls/TableControls.tsx +31 -22
  113. package/src/components/table/tableControls/TableSettingsDropdown.tsx +7 -4
  114. package/src/components/toast/Toast.tsx +1 -1
  115. package/src/components/tooltip/Tooltip.stories.tsx +7 -7
  116. package/src/components/tooltip/TooltipWrapper.stories.tsx +4 -4
  117. package/src/components/userDropdown/internal/UserDropdownTrigger.tsx +2 -1
@@ -1,5 +1,6 @@
1
1
  import { expect, test, describe, vi } from 'vitest';
2
2
  import { Button } from './Button.js';
3
+ import { Icon } from 'Components/icon/Icon';
3
4
  import { render, screen, fireEvent } from '@testing-library/react';
4
5
  import '@testing-library/jest-dom/vitest';
5
6
 
@@ -9,29 +10,55 @@ describe('Button component', () => {
9
10
  expect(screen.getByText('Button Text')).toBeInTheDocument();
10
11
  });
11
12
 
12
- test('renders button with a right icon', () => {
13
- render(<Button iconRightName="info" iconRightScreenReaderText="the icon is here">Button Text</Button>);
13
+ test('renders button with an icon after the text', () => {
14
+ render(
15
+ <Button>
16
+ Button Text
17
+ <Icon name="info" screenReaderText="the icon is here" />
18
+ </Button>,
19
+ );
14
20
  expect(screen.getByText('the icon is here')).toBeInTheDocument();
15
21
  expect(screen.getByText('Button Text')).toBeInTheDocument();
16
22
  });
17
23
 
18
- test('renders button with a left icon', () => {
19
- render(<Button iconLeftName="info" iconLeftScreenReaderText="the icon is here">Button Text</Button>);
24
+ test('renders button with an icon before the text', () => {
25
+ render(
26
+ <Button>
27
+ <Icon name="info" screenReaderText="the icon is here" />
28
+ Button Text
29
+ </Button>,
30
+ );
20
31
  expect(screen.getByText('the icon is here')).toBeInTheDocument();
21
32
  expect(screen.getByText('Button Text')).toBeInTheDocument();
22
33
  });
23
34
 
24
- test('renders button with a right icon and a left icon', () => {
25
- render(<Button iconRightName="info" iconRightScreenReaderText="the right icon is here" iconLeftName="info" iconLeftScreenReaderText="the left icon is here">Button Text</Button>);
35
+ test('renders button with an icon before and after the text', () => {
36
+ render(
37
+ <Button>
38
+ <Icon name="info" screenReaderText="the left icon is here" />
39
+ Button Text
40
+ <Icon name="info" screenReaderText="the right icon is here" />
41
+ </Button>,
42
+ );
26
43
  expect(screen.getByText('the right icon is here')).toBeInTheDocument();
27
44
  expect(screen.getByText('the left icon is here')).toBeInTheDocument();
28
45
  expect(screen.getByText('Button Text')).toBeInTheDocument();
29
46
  });
30
47
 
31
- test('applies icon only style if no children are provided', () => {
32
- render(<Button iconRightName="info" iconRightScreenReaderText="the icon is here" />);
48
+ test('applies round style when isRound is set', () => {
49
+ render(
50
+ <Button isRound>
51
+ <Icon name="info" screenReaderText="the icon is here" />
52
+ </Button>,
53
+ );
33
54
  const button = screen.getByText('the icon is here').closest('button');
34
- expect(button).toHaveClass('ds-button--icon-only');
55
+ expect(button).toHaveClass('ds-button--round');
56
+ });
57
+
58
+ test('does not apply round style by default', () => {
59
+ render(<Button>Button Text</Button>);
60
+ const button = screen.getByText('Button Text').closest('button');
61
+ expect(button).not.toHaveClass('ds-button--round');
35
62
  });
36
63
 
37
64
  test('applies primary style by default', () => {
@@ -1,7 +1,5 @@
1
1
  import classNames from 'classnames';
2
2
  import React, { forwardRef, type ButtonHTMLAttributes, type PropsWithChildren } from 'react';
3
- import type { IconName } from 'Components/icon/allowedIcons';
4
- import { Icon } from 'Components/icon/Icon';
5
3
 
6
4
  export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'primary-destructive' | 'secondary-destructive' | 'text-link' | 'dropdown';
7
5
 
@@ -13,15 +11,12 @@ export type ButtonProps = {
13
11
  children?: React.ReactNode;
14
12
  className?: string;
15
13
  error?: boolean;
16
- [key: string]: unknown;
14
+ [key: `data-${string}`]: unknown;
17
15
  hasHorizontalPadding?: boolean;
18
- iconRightName?: IconName;
19
- iconRightScreenReaderText?: string;
20
- iconLeftName?: IconName;
21
- iconLeftScreenReaderText?: string;
16
+ isRound?: boolean;
22
17
  borderless?: boolean;
23
18
  onClick?: (event: React.MouseEvent<HTMLButtonElement>, ...otherArgs: unknown[]) => void;
24
- } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type' | 'onClick'>;
19
+ } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'>;
25
20
 
26
21
  export const Button = forwardRef<HTMLButtonElement, PropsWithChildren<ButtonProps>>((props: ButtonProps, ref) => {
27
22
  const {
@@ -32,10 +27,7 @@ export const Button = forwardRef<HTMLButtonElement, PropsWithChildren<ButtonProp
32
27
  disabled = false,
33
28
  className = '',
34
29
  hasHorizontalPadding = true,
35
- iconRightName,
36
- iconRightScreenReaderText,
37
- iconLeftName,
38
- iconLeftScreenReaderText,
30
+ isRound = false,
39
31
  borderless = false,
40
32
  error = false,
41
33
  ...rest
@@ -49,7 +41,7 @@ export const Button = forwardRef<HTMLButtonElement, PropsWithChildren<ButtonProp
49
41
  {
50
42
  'ds-button--error': error,
51
43
  'ds-button--no-horizontal-padding': !hasHorizontalPadding,
52
- 'ds-button--icon-only': !children && (iconRightName || iconLeftName),
44
+ 'ds-button--round': isRound,
53
45
  'ds-button--borderless': borderless,
54
46
  },
55
47
  className,
@@ -59,9 +51,7 @@ export const Button = forwardRef<HTMLButtonElement, PropsWithChildren<ButtonProp
59
51
  ref={ref}
60
52
  {...rest}
61
53
  >
62
- {iconLeftName && <Icon name={iconLeftName} size={16} screenReaderText={iconLeftScreenReaderText || iconLeftName} />}
63
- {children ? <span className="ds-button__content">{children}</span> : children}
64
- {iconRightName && <Icon name={iconRightName} size={16} screenReaderText={iconRightScreenReaderText || iconRightName} />}
54
+ {children}
65
55
  </button>
66
56
  );
67
57
  });
@@ -25,20 +25,20 @@
25
25
  // Button sizes
26
26
  &--M {
27
27
  height: var(--size-medium);
28
- }
29
28
 
30
- &--icon-only.ds-button--M {
31
- padding: 0;
32
- width: var(--size-medium);
29
+ &.ds-button--round {
30
+ padding: 0;
31
+ width: var(--size-medium);
32
+ }
33
33
  }
34
34
 
35
35
  &--S {
36
36
  height: var(--size-small);
37
- }
38
37
 
39
- &--icon-only.ds-button--S {
40
- padding: 0;
41
- width: var(--size-small);
38
+ &.ds-button--round {
39
+ padding: 0;
40
+ width: var(--size-small);
41
+ }
42
42
  }
43
43
 
44
44
  // Button text
@@ -76,7 +76,7 @@
76
76
  }
77
77
 
78
78
  .ds-combobox__tag-list {
79
- flex: 1;
79
+ flex: 0 1 auto;
80
80
  min-width: 0;
81
81
  }
82
82
 
@@ -1,5 +1,6 @@
1
1
  import classNames from 'classnames';
2
2
  import { Button } from 'Components/button/Button';
3
+ import { Icon } from 'Components/icon/Icon';
3
4
  import { TextInput } from 'Components/formField/inputs/text/TextInput';
4
5
  import { Popover } from 'radix-ui';
5
6
  import {
@@ -201,9 +202,10 @@ export const DatePicker = (props: DatePickerProps) => {
201
202
  <Button
202
203
  className="ds-date-picker__button"
203
204
  variant="text-link"
204
- iconLeftName="date"
205
- iconLeftScreenReaderText="Open date picker"
206
- />
205
+ isRound
206
+ >
207
+ <Icon name="date" screenReaderText="Open date picker" />
208
+ </Button>
207
209
  </Popover.Trigger>
208
210
  <Popover.Portal container={popupParentRef.current}>
209
211
  <Popover.Content
@@ -1,5 +1,6 @@
1
1
  import classNames from 'classnames';
2
2
  import { Button } from 'Components/button/Button';
3
+ import { Icon } from 'Components/icon/Icon';
3
4
  import type { ComboboxSearchType } from 'Components/combobox/types';
4
5
  import {
5
6
  formatDateTimeInputValue,
@@ -275,9 +276,10 @@ export const DateTimePicker = (props: DateTimePickerProps) => {
275
276
  <Button
276
277
  className="ds-date-time-picker__button"
277
278
  variant="text-link"
278
- iconLeftName="date"
279
- iconLeftScreenReaderText="Open date and time picker"
280
- />
279
+ isRound
280
+ >
281
+ <Icon name="date" screenReaderText="Open date and time picker" />
282
+ </Button>
281
283
  </Popover.Trigger>
282
284
  <Popover.Portal container={popupParentRef.current}>
283
285
  <Popover.Content
@@ -881,7 +881,9 @@ export const WithIcons: Story = withDescription(
881
881
  <div style={{ padding: 'var(--spacing-xlarge)' }}>
882
882
  <Dropdown>
883
883
  <Dropdown.Trigger>
884
- <Button variant="secondary" iconRightName="3-dot" iconRightScreenReaderText="More actions" hasHorizontalPadding={false} />
884
+ <Button variant="secondary" isRound hasHorizontalPadding={false}>
885
+ <Icon name="3-dot" screenReaderText="More actions" />
886
+ </Button>
885
887
  </Dropdown.Trigger>
886
888
  <Dropdown.Content>
887
889
  <Dropdown.Item onSelect={() => console.log('view')}>
@@ -915,7 +917,9 @@ function IconItemsDropdown() {
915
917
  return (
916
918
  <Dropdown>
917
919
  <Dropdown.Trigger>
918
- <Button variant="secondary" iconRightName="3-dot" iconRightScreenReaderText="More actions" hasHorizontalPadding={false} />
920
+ <Button variant="secondary" isRound hasHorizontalPadding={false}>
921
+ <Icon name="3-dot" screenReaderText="More actions" />
922
+ </Button>
919
923
  </Dropdown.Trigger>
920
924
  <Dropdown.Content>
921
925
  <Dropdown.Item onSelect={() => console.log('view')}>
@@ -945,7 +949,7 @@ export default IconItemsDropdown;`,
945
949
  },
946
950
  },
947
951
  },
948
- 'Icons inside `Dropdown.Item` children align automatically via the item\'s flex layout (`gap` + `align-items: center`). Use `size={16}` to match the item\'s line height. The trigger here uses an icon-only `Button` with `iconRightName="3-dot"` — the classic "more actions" kebab pattern used throughout Arbor\'s table rows. Always provide `iconRightScreenReaderText` so screen reader users know what the button does.',
952
+ 'Icons inside `Dropdown.Item` children align automatically via the item\'s flex layout (`gap` + `align-items: center`). Use `size={16}` to match the item\'s line height. The trigger here uses an icon-only `Button` with `isRound` and a child `Icon` — the classic "more actions" kebab pattern used throughout Arbor\'s table rows. Always pass `screenReaderText` on that `Icon` so screen reader users know what the button does.',
949
953
  );
950
954
 
951
955
  export const WithDestructiveAction: Story = withDescription(
@@ -208,7 +208,8 @@ const TertiaryActionTemplate = () => (
208
208
  subHeading="Start by adding the content and data blocks you want to see here."
209
209
  orientation="vertical"
210
210
  actions={(
211
- <Button type="button" variant="text-link" iconLeftName="plus">
211
+ <Button type="button" variant="text-link">
212
+ <Icon name="plus" />
212
213
  Add
213
214
  </Button>
214
215
  )}
@@ -353,7 +354,8 @@ function EmptyStateTertiaryActionExample() {
353
354
  subHeading="Start by adding the content and data blocks you want to see here."
354
355
  orientation="vertical"
355
356
  actions={
356
- <Button type="button" variant="text-link" iconLeftName="plus">
357
+ <Button type="button" variant="text-link">
358
+ <Icon name="plus" />
357
359
  Add
358
360
  </Button>
359
361
  }
@@ -70,7 +70,7 @@ describe('FileUpload', () => {
70
70
  <FileUpload uploadedFiles={[{ ...baseFile, onDelete }]} uploadFiles={vi.fn()} />,
71
71
  );
72
72
 
73
- await userEvent.click(screen.getByRole('button', { name: 'trash' }));
73
+ await userEvent.click(screen.getByRole('button', { name: 'Delete file' }));
74
74
  expect(onDelete).toHaveBeenCalledOnce();
75
75
  });
76
76
 
@@ -81,12 +81,12 @@ describe('FileUpload', () => {
81
81
  uploadFiles={vi.fn()}
82
82
  />,
83
83
  );
84
- expect(screen.getByRole('button', { name: 'download' })).toBeInTheDocument();
84
+ expect(screen.getByRole('button', { name: 'Download file' })).toBeInTheDocument();
85
85
  });
86
86
 
87
87
  test('does not render download button when onDownload is not provided', () => {
88
88
  render(<FileUpload uploadedFiles={[baseFile]} uploadFiles={vi.fn()} />);
89
- expect(screen.queryByRole('button', { name: 'download' })).not.toBeInTheDocument();
89
+ expect(screen.queryByRole('button', { name: 'Download file' })).not.toBeInTheDocument();
90
90
  });
91
91
 
92
92
  test('calls onDownload when download button is clicked', async () => {
@@ -98,7 +98,7 @@ describe('FileUpload', () => {
98
98
  />,
99
99
  );
100
100
 
101
- await userEvent.click(screen.getByRole('button', { name: 'download' }));
101
+ await userEvent.click(screen.getByRole('button', { name: 'Download file' }));
102
102
  expect(onDownload).toHaveBeenCalledOnce();
103
103
  });
104
104
  });
@@ -111,16 +111,20 @@ export const FileUpload = (props: FileUploadProps) => {
111
111
  {onDownload && (
112
112
  <Button
113
113
  variant="secondary"
114
- iconLeftName="download"
114
+ isRound
115
115
  onClick={onDownload}
116
- />
116
+ >
117
+ <Icon name="download" screenReaderText="Download file" />
118
+ </Button>
117
119
  )}
118
120
  {onDelete && (
119
121
  <Button
120
122
  variant="secondary"
121
- iconLeftName="trash"
123
+ isRound
122
124
  onClick={onDelete}
123
- />
125
+ >
126
+ <Icon name="trash" screenReaderText="Delete file" />
127
+ </Button>
124
128
  )}
125
129
  </span>
126
130
  {typeof progressPercentage === 'number' && progressPercentage < 100
@@ -1,5 +1,6 @@
1
1
  import { Dropdown } from 'Components/dropdown/Dropdown';
2
2
  import { Button } from 'Components/button/Button';
3
+ import { Icon } from 'Components/icon/Icon';
3
4
  import { Sketch } from '@uiw/react-color';
4
5
  import type { ColorResult } from '@uiw/color-convert';
5
6
  import { useEffect, useState } from 'react';
@@ -31,7 +32,7 @@ export const ColourPickerDropdown = ({ value = '#3cad51', onChange, hasError, di
31
32
  <input type="hidden" name={id} value={selectedColour} />
32
33
  <Dropdown>
33
34
  <Dropdown.Trigger disabled={disabled} className="ds-colour-picker-dropdown__trigger">
34
- <Button variant="dropdown" iconRightName="chevron-down" error={hasError} id={id} aria-describedby={ariaDescribedBy} aria-invalid={ariaInvalid}>
35
+ <Button variant="dropdown" error={hasError} id={id} aria-describedby={ariaDescribedBy} aria-invalid={ariaInvalid}>
35
36
  <span className="sr-only">
36
37
  Toggle colour picker.
37
38
  </span>
@@ -42,6 +43,7 @@ export const ColourPickerDropdown = ({ value = '#3cad51', onChange, hasError, di
42
43
  {selectedColour}
43
44
  </span>
44
45
  </div>
46
+ <Icon name="chevron-down" />
45
47
  </Button>
46
48
  </Dropdown.Trigger>
47
49
  <Dropdown.Content className="ds-colour-picker-dropdown__content">
@@ -4,6 +4,7 @@ import classNames from 'classnames';
4
4
  import { SelectDropdownItem, type SelectDropdownItemProps } from './items/item/SelectDropdownItem.js';
5
5
  import { Dropdown } from 'Components/dropdown/Dropdown';
6
6
  import { Button } from 'Components/button/Button';
7
+ import { Icon } from 'Components/icon/Icon';
7
8
 
8
9
  export type SelectDropdownInputProps = {
9
10
  'placeholder'?: string;
@@ -115,13 +116,13 @@ export const SelectDropdown = (props: SelectDropdownInputProps) => {
115
116
  <Button
116
117
  variant="dropdown"
117
118
  error={hasError}
118
- iconRightName="chevron-down"
119
119
  id={id}
120
120
  aria-describedby={ariaDescribedBy}
121
121
  aria-invalid={ariaInvalid}
122
122
  className={classNames({ 'ds-select-dropdown--icon-click-only': iconClickOnly })}
123
123
  >
124
- {renderedSelectContent}
124
+ <span className="ds-button__content">{renderedSelectContent}</span>
125
+ <Icon name="chevron-down" />
125
126
  </Button>
126
127
  </Dropdown.Trigger>
127
128
  <Dropdown.Content contentProps={{ onCloseAutoFocus }}>
@@ -13,6 +13,7 @@ import { Grid } from './Grid.js';
13
13
  import { Avatar } from 'Components/avatar/Avatar';
14
14
  import { Badge } from 'Components/badge/Badge';
15
15
  import { Button } from 'Components/button/Button';
16
+ import { Icon } from 'Components/icon/Icon';
16
17
  import { DataViewCard } from 'Components/dataViewCard/DataViewCard';
17
18
  import { Heading } from 'Components/heading/Heading';
18
19
  import { KPICard } from 'Components/kpiCard/KPICard';
@@ -803,8 +804,14 @@ export const StudentProfilePage: Story = withDescription(
803
804
  </div>
804
805
  </div>
805
806
  <div style={actionRowStyle}>
806
- <Button variant="secondary" size="S" iconLeftName="pencil">Edit</Button>
807
- <Button variant="secondary" size="S" iconLeftName="file">Print</Button>
807
+ <Button variant="secondary" size="S">
808
+ <Icon name="pencil" />
809
+ Edit
810
+ </Button>
811
+ <Button variant="secondary" size="S">
812
+ <Icon name="file" />
813
+ Print
814
+ </Button>
808
815
  <Button variant="primary" size="S">Contact Home</Button>
809
816
  </div>
810
817
  </div>
@@ -911,7 +918,10 @@ export const AttendanceDashboard: Story = withDescription(
911
918
  </div>
912
919
  </div>
913
920
  <div style={actionRowStyle}>
914
- <Button variant="secondary" size="S" iconLeftName="download">Export</Button>
921
+ <Button variant="secondary" size="S">
922
+ <Icon name="download" />
923
+ Export
924
+ </Button>
915
925
  <Button variant="primary" size="S">Generate Report</Button>
916
926
  </div>
917
927
  </div>
@@ -1024,7 +1034,10 @@ export const StaffDirectory: Story = withDescription(
1024
1034
  <Heading level={1}>Staff Directory</Heading>
1025
1035
  <Badge colour="blue">24 staff</Badge>
1026
1036
  </div>
1027
- <Button variant="primary" size="S" iconLeftName="plus">Add Staff Member</Button>
1037
+ <Button variant="primary" size="S">
1038
+ <Icon name="plus" />
1039
+ Add Staff Member
1040
+ </Button>
1028
1041
  </div>
1029
1042
  </Grid.Column>
1030
1043
  </Grid.Row>
@@ -10,6 +10,7 @@ import {
10
10
  } from '@storybook/addon-docs/blocks';
11
11
  import { Heading } from './Heading.js';
12
12
  import { Button } from 'Components/button/Button';
13
+ import { Icon } from 'Components/icon/Icon';
13
14
  import { EditableText } from 'Components/editableText/EditableText';
14
15
  import { TooltipWrapper } from 'Components/tooltip/TooltipWrapper';
15
16
 
@@ -141,7 +142,7 @@ const DEVELOPER_NOTES = [
141
142
  '- **Keyboard order** — the DOM order is left container then right container, matching reading order and Tab sequence',
142
143
  '- **Icon-only buttons in the heading** require a `TooltipWrapper` with an accessible label. A tooltip',
143
144
  ' on hover alone is not sufficient — it must also appear on keyboard focus, and assistive technology',
144
- ' must be able to read the label. Always pair `iconRightScreenReaderText` with `TooltipWrapper`.',
145
+ ' must be able to read the label. Always pair a `screenReaderText` on the child `Icon` with `TooltipWrapper`.',
145
146
  '- **`id` + `aria-labelledby`** — give the heading an `id` and reference it from an adjacent',
146
147
  ' `<section aria-labelledby="...">` to associate the landmark with its heading. Screen readers',
147
148
  ' announce the section name when the user enters the landmark region.',
@@ -339,9 +340,10 @@ const WithIconOnlyActionTemplate = () => (
339
340
  <Button
340
341
  variant="secondary"
341
342
  type="button"
342
- iconRightName="download"
343
- iconRightScreenReaderText="Download student records"
344
- />
343
+ isRound
344
+ >
345
+ <Icon name="download" screenReaderText="Download student records" />
346
+ </Button>
345
347
  </TooltipWrapper>
346
348
  </Heading.InnerContainer>
347
349
  </Heading>
@@ -352,7 +354,8 @@ const WithBackControlLegacyTemplate = () => (
352
354
  <div style={{ padding: 'var(--spacing-xlarge)' }}>
353
355
  <Heading level={1}>
354
356
  <Heading.InnerContainer className="medium-spacing-gap">
355
- <Button variant="tertiary" type="button" iconLeftName="chevron-left" iconLeftScreenReaderText="Go back">
357
+ <Button variant="tertiary" type="button">
358
+ <Icon name="chevron-left" />
356
359
  Back
357
360
  </Button>
358
361
  Attendance register
@@ -396,7 +399,8 @@ const WithEditableTitleAndMultipleActionsTemplate = () => (
396
399
  <div style={{ padding: 'var(--spacing-xlarge)' }}>
397
400
  <Heading level={1}>
398
401
  <Heading.InnerContainer className="medium-spacing-gap">
399
- <Button variant="tertiary" type="button" iconLeftName="chevron-left" iconLeftScreenReaderText="Go back">
402
+ <Button variant="tertiary" type="button">
403
+ <Icon name="chevron-left" />
400
404
  Back
401
405
  </Button>
402
406
  <EditableText
@@ -578,7 +582,7 @@ export const WithIconOnlyAction: Story = withDescription(
578
582
  source: {
579
583
  language: 'tsx',
580
584
  code: `
581
- import { Heading, Button, TooltipWrapper } from '@arbor-education/design-system.components';
585
+ import { Heading, Button, Icon, TooltipWrapper } from '@arbor-education/design-system.components';
582
586
 
583
587
  <div style={{ padding: 'var(--spacing-xlarge)' }}>
584
588
  <Heading level={1}>
@@ -588,9 +592,10 @@ import { Heading, Button, TooltipWrapper } from '@arbor-education/design-system.
588
592
  <Button
589
593
  variant="secondary"
590
594
  type="button"
591
- iconRightName="download"
592
- iconRightScreenReaderText="Download student records"
593
- />
595
+ isRound
596
+ >
597
+ <Icon name="download" screenReaderText="Download student records" />
598
+ </Button>
594
599
  </TooltipWrapper>
595
600
  </Heading.InnerContainer>
596
601
  </Heading>
@@ -604,8 +609,8 @@ import { Heading, Button, TooltipWrapper } from '@arbor-education/design-system.
604
609
  'Title left, an icon-only action button right. **Icon-only buttons in a heading require a `TooltipWrapper`**',
605
610
  '— hover tooltips alone are not sufficient. The tooltip must appear on keyboard focus too, and assistive',
606
611
  'technology must be able to read the label. Always pair `TooltipWrapper` with a descriptive',
607
- '`iconRightScreenReaderText` prop on the button. This story uses both: `tooltipContent="Download student records"`',
608
- 'for sighted keyboard users and `iconRightScreenReaderText="Download student records"` for screen reader users.',
612
+ '`screenReaderText` on the child `Icon`. This story uses both: `tooltipContent="Download student records"`',
613
+ 'for sighted keyboard users and `screenReaderText="Download student records"` for screen reader users.',
609
614
  ].join(' '),
610
615
  );
611
616
 
@@ -617,12 +622,13 @@ export const WithBackControlLegacy: Story = withDescription(
617
622
  source: {
618
623
  language: 'tsx',
619
624
  code: `
620
- import { Heading, Button } from '@arbor-education/design-system.components';
625
+ import { Heading, Button, Icon } from '@arbor-education/design-system.components';
621
626
 
622
627
  <div style={{ padding: 'var(--spacing-xlarge)' }}>
623
628
  <Heading level={1}>
624
629
  <Heading.InnerContainer className="medium-spacing-gap">
625
- <Button variant="tertiary" type="button" iconLeftName="chevron-left" iconLeftScreenReaderText="Go back">
630
+ <Button variant="tertiary" type="button">
631
+ <Icon name="chevron-left" />
626
632
  Back
627
633
  </Button>
628
634
  Attendance register
@@ -709,12 +715,13 @@ export const WithEditableTitleAndMultipleActions: Story = withDescription(
709
715
  source: {
710
716
  language: 'tsx',
711
717
  code: `
712
- import { Heading, Button, EditableText } from '@arbor-education/design-system.components';
718
+ import { Heading, Button, Icon, EditableText } from '@arbor-education/design-system.components';
713
719
 
714
720
  <div style={{ padding: 'var(--spacing-xlarge)' }}>
715
721
  <Heading level={1}>
716
722
  <Heading.InnerContainer className="medium-spacing-gap">
717
- <Button variant="tertiary" type="button" iconLeftName="chevron-left" iconLeftScreenReaderText="Go back">
723
+ <Button variant="tertiary" type="button">
724
+ <Icon name="chevron-left" />
718
725
  Back
719
726
  </Button>
720
727
  <EditableText text="Autumn term report" onEditSave={() => {}} />