uikit-react-public 0.50.5 → 0.50.7

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 (36) hide show
  1. package/dist/components/ToggleGroup/ToggleGroup.d.ts +5 -3
  2. package/dist/components/ToggleGroup/ToggleGroup.stories.d.ts +7 -2
  3. package/dist/components/ToggleGroup/ToggleGroupContext.d.ts +2 -0
  4. package/dist/components/ToggleGroup/index.d.ts +1 -2
  5. package/dist/components/ToggleGroup/subcomponents/ToggleGroupItem.d.ts +13 -0
  6. package/dist/components/ToggleGroup/subcomponents/{Item.stories.d.ts → ToggleGroupItem.stories.d.ts} +2 -1
  7. package/dist/components/index.d.ts +1 -1
  8. package/dist/index.js +5138 -5050
  9. package/lib/components/Accordion/Accordion.Heading.tsx +1 -1
  10. package/lib/components/Accordion/Accordion.Panel.tsx +1 -1
  11. package/lib/components/Accordion/__tests__/Accordion.test.tsx +21 -0
  12. package/lib/components/Accordion/__tests__/__snapshots__/Accordion.test.tsx.snap +2 -2
  13. package/lib/components/Badge/Badge.tsx +3 -1
  14. package/lib/components/Badge/__tests__/Badge.test.tsx +10 -2
  15. package/lib/components/Badge/__tests__/__snapshots__/Badge.test.tsx.snap +9 -9
  16. package/lib/components/Calendar/__tests__/__snapshots__/Calendar.test.tsx.snap +4 -4
  17. package/lib/components/Callout/Callout.tsx +1 -1
  18. package/lib/components/Datepicker/__tests__/__snapshots__/Datepicker.test.tsx.snap +6 -6
  19. package/lib/components/Datepicker/subcomponents/DatepickerInput.tsx +1 -0
  20. package/lib/components/Datepicker/subcomponents/VisibleField.tsx +2 -1
  21. package/lib/components/NativeDatepicker/NativeDatepicker.tsx +1 -0
  22. package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +1 -1
  23. package/lib/components/Select/subcomponents/CustomSelect.tsx +1 -0
  24. package/lib/components/Select/subcomponents/NativeSelect.tsx +1 -0
  25. package/lib/components/ToggleGroup/ToggleGroup.stories.tsx +3 -0
  26. package/lib/components/ToggleGroup/ToggleGroup.tsx +51 -10
  27. package/lib/components/ToggleGroup/ToggleGroupContext.tsx +2 -0
  28. package/lib/components/ToggleGroup/__tests__/ToggleGroup.test.tsx +105 -6
  29. package/lib/components/ToggleGroup/index.ts +1 -2
  30. package/lib/components/ToggleGroup/subcomponents/{Item.stories.tsx → ToggleGroupItem.stories.tsx} +6 -4
  31. package/lib/components/ToggleGroup/subcomponents/{Item.tsx → ToggleGroupItem.tsx} +126 -52
  32. package/lib/components/ToggleGroup/subcomponents/__tests__/{Item.test.tsx → ToggleGroupItem.test.tsx} +50 -6
  33. package/lib/components/index.ts +1 -1
  34. package/package.json +1 -1
  35. package/dist/components/ToggleGroup/subcomponents/Item.d.ts +0 -11
  36. /package/dist/components/ToggleGroup/subcomponents/__tests__/{Item.test.d.ts → ToggleGroupItem.test.d.ts} +0 -0
@@ -38,7 +38,7 @@ const AccordionHeading: React.FC<AccordionHeadingProps> = ({
38
38
  position: relative;
39
39
  justify-content: space-between;
40
40
  align-items: center;
41
- padding: ${theme.padding.p16};
41
+ padding: ${theme.padding.p16} ${theme.padding.p8};
42
42
  background-color: ${theme.colour.fill.inverse};
43
43
  cursor: pointer;
44
44
  transition: background-color 0.15s ease;
@@ -29,7 +29,7 @@ const AccordionPanel: React.FC<AccordionPanelProps> = ({
29
29
  max-width: 100%;
30
30
  min-width: 0;
31
31
  flex-shrink: 1;
32
- padding: ${theme.padding.p16};
32
+ padding: ${theme.padding.p8} ${theme.padding.p8} ${theme.padding.p24};
33
33
  box-sizing: border-box;
34
34
  overflow: hidden;
35
35
  overflow-wrap: break-word;
@@ -40,6 +40,27 @@ describe('Accordion', () => {
40
40
 
41
41
  // Interaction tests
42
42
 
43
+ test('uses the expected heading and panel padding', () => {
44
+ render(
45
+ <ThemeContextProvider>
46
+ <Accordion>
47
+ <Accordion.Heading>Heading</Accordion.Heading>
48
+ <Accordion.Panel>Panel Content</Accordion.Panel>
49
+ </Accordion>
50
+ </ThemeContextProvider>
51
+ );
52
+
53
+ const heading = screen.getByTestId('ucl-uikit-accordion__heading');
54
+ expect(getComputedStyle(heading).padding).toBe('16px 8px');
55
+
56
+ fireEvent.click(heading);
57
+ const panel = screen.getByTestId('ucl-uikit-accordion__panel');
58
+ expect(getComputedStyle(panel).paddingTop).toBe('8px');
59
+ expect(getComputedStyle(panel).paddingRight).toBe('8px');
60
+ expect(getComputedStyle(panel).paddingBottom).toBe('24px');
61
+ expect(getComputedStyle(panel).paddingLeft).toBe('8px');
62
+ });
63
+
43
64
  test('should open and close the accordion', () => {
44
65
  render(
45
66
  <ThemeContextProvider>
@@ -8,7 +8,7 @@ exports[`Accordion > snapshot: no props 1`] = `
8
8
  >
9
9
  <h6
10
10
  aria-expanded="false"
11
- class="ucl-uikit-heading ucl-uikit-accordion__heading css-spdtpe"
11
+ class="ucl-uikit-heading ucl-uikit-accordion__heading css-3x70cw"
12
12
  data-testid="ucl-uikit-accordion__heading"
13
13
  role="button"
14
14
  tabindex="0"
@@ -44,7 +44,7 @@ exports[`Accordion > snapshot: testId prop 1`] = `
44
44
  >
45
45
  <h6
46
46
  aria-expanded="false"
47
- class="ucl-uikit-heading ucl-uikit-accordion__heading css-spdtpe"
47
+ class="ucl-uikit-heading ucl-uikit-accordion__heading css-3x70cw"
48
48
  data-testid="ucl-uikit-accordion__heading"
49
49
  role="button"
50
50
  tabindex="0"
@@ -91,8 +91,9 @@ export const getBadgeVariantColours = (
91
91
  color: theme.colour.text.inverse,
92
92
  backgroundColor: theme.colour.fill.warning,
93
93
  },
94
+ // TODO: replace primitiveColour use here with colour.text.warningOnBgFill when token is updated to matching hex
94
95
  'warning-subtle': {
95
- color: theme.colour.text.warningOnBgFill,
96
+ color: theme.primitiveColour.orange['13'].$value.hex,
96
97
  backgroundColor: theme.colour.fill.warningSubtle,
97
98
  },
98
99
  disabled: {
@@ -145,6 +146,7 @@ const Badge = ({
145
146
  padding: 0 ${theme.padding.p8};
146
147
 
147
148
  color: ${color};
149
+ font-weight: ${theme.typography.body.smSemibold.fontWeight};
148
150
  background-color: ${backgroundColor};
149
151
  border: ${border ?? 'none'};
150
152
 
@@ -91,6 +91,14 @@ describe('Badge', () => {
91
91
  expect(getByText('Hello')).toBeTruthy();
92
92
  });
93
93
 
94
+ test('uses semibold text', () => {
95
+ const { getByTestId } = wrap(<Badge>Text</Badge>);
96
+
97
+ expect(getComputedStyle(getByTestId('ucl-uikit-badge')).fontWeight).toBe(
98
+ String(lightTheme.typography.body.smSemibold.fontWeight)
99
+ );
100
+ });
101
+
94
102
  test('applies custom testId', () => {
95
103
  const { getByTestId } = wrap(<Badge testId='my-badge'>Text</Badge>);
96
104
  expect(getByTestId('my-badge')).toBeTruthy();
@@ -175,7 +183,7 @@ describe('Badge', () => {
175
183
  backgroundColor: darkTheme.colour.fill.warning,
176
184
  });
177
185
  expect(colours['warning-subtle']).toMatchObject({
178
- color: darkTheme.colour.text.warningOnBgFill,
186
+ color: darkTheme.primitiveColour.orange['13'].$value.hex,
179
187
  backgroundColor: darkTheme.colour.fill.warningSubtle,
180
188
  });
181
189
  });
@@ -217,7 +225,7 @@ describe('Badge', () => {
217
225
  backgroundColor: lightTheme.colour.fill.warning,
218
226
  });
219
227
  expect(colours['warning-subtle']).toMatchObject({
220
- color: lightTheme.colour.text.warningOnBgFill,
228
+ color: '#994D29',
221
229
  backgroundColor: lightTheme.colour.fill.warningSubtle,
222
230
  });
223
231
  });
@@ -2,7 +2,7 @@
2
2
 
3
3
  exports[`Badge > snapshot: default 1`] = `
4
4
  <span
5
- class="ucl-uikit-text ucl-uikit-badge css-1qd9y8u"
5
+ class="ucl-uikit-text ucl-uikit-badge css-193xmhy"
6
6
  data-testid="ucl-uikit-badge"
7
7
  >
8
8
  Default
@@ -11,7 +11,7 @@ exports[`Badge > snapshot: default 1`] = `
11
11
 
12
12
  exports[`Badge > snapshot: size=large 1`] = `
13
13
  <span
14
- class="ucl-uikit-text ucl-uikit-badge css-1j13tem"
14
+ class="ucl-uikit-text ucl-uikit-badge css-xkp5fs"
15
15
  data-testid="ucl-uikit-badge"
16
16
  >
17
17
  Large
@@ -20,7 +20,7 @@ exports[`Badge > snapshot: size=large 1`] = `
20
20
 
21
21
  exports[`Badge > snapshot: statusIndicator 1`] = `
22
22
  <span
23
- class="ucl-uikit-text ucl-uikit-badge css-1qd9y8u"
23
+ class="ucl-uikit-text ucl-uikit-badge css-193xmhy"
24
24
  data-testid="ucl-uikit-badge"
25
25
  >
26
26
  <span
@@ -33,7 +33,7 @@ exports[`Badge > snapshot: statusIndicator 1`] = `
33
33
 
34
34
  exports[`Badge > snapshot: variant=critical with icon 1`] = `
35
35
  <span
36
- class="ucl-uikit-text ucl-uikit-badge css-rkw7bu"
36
+ class="ucl-uikit-text ucl-uikit-badge css-1ussa90"
37
37
  data-testid="ucl-uikit-badge"
38
38
  >
39
39
  <svg
@@ -66,7 +66,7 @@ exports[`Badge > snapshot: variant=critical with icon 1`] = `
66
66
 
67
67
  exports[`Badge > snapshot: variant=disabled 1`] = `
68
68
  <span
69
- class="ucl-uikit-text ucl-uikit-badge css-a4ty56"
69
+ class="ucl-uikit-text ucl-uikit-badge css-1lag7jx"
70
70
  data-testid="ucl-uikit-badge"
71
71
  >
72
72
  Disabled
@@ -75,7 +75,7 @@ exports[`Badge > snapshot: variant=disabled 1`] = `
75
75
 
76
76
  exports[`Badge > snapshot: variant=info with icon 1`] = `
77
77
  <span
78
- class="ucl-uikit-text ucl-uikit-badge css-9ddki6"
78
+ class="ucl-uikit-text ucl-uikit-badge css-pmqt4o"
79
79
  data-testid="ucl-uikit-badge"
80
80
  >
81
81
  <svg
@@ -108,7 +108,7 @@ exports[`Badge > snapshot: variant=info with icon 1`] = `
108
108
 
109
109
  exports[`Badge > snapshot: variant=info-subtle 1`] = `
110
110
  <span
111
- class="ucl-uikit-text ucl-uikit-badge css-1mmhjpi"
111
+ class="ucl-uikit-text ucl-uikit-badge css-1ikogho"
112
112
  data-testid="ucl-uikit-badge"
113
113
  >
114
114
  Info subtle
@@ -117,7 +117,7 @@ exports[`Badge > snapshot: variant=info-subtle 1`] = `
117
117
 
118
118
  exports[`Badge > snapshot: variant=success with icon 1`] = `
119
119
  <span
120
- class="ucl-uikit-text ucl-uikit-badge css-1k9d7cn"
120
+ class="ucl-uikit-text ucl-uikit-badge css-plwfy3"
121
121
  data-testid="ucl-uikit-badge"
122
122
  >
123
123
  <svg
@@ -148,7 +148,7 @@ exports[`Badge > snapshot: variant=success with icon 1`] = `
148
148
 
149
149
  exports[`Badge > snapshot: variant=warning with icon 1`] = `
150
150
  <span
151
- class="ucl-uikit-text ucl-uikit-badge css-1vsenfu"
151
+ class="ucl-uikit-text ucl-uikit-badge css-159h60j"
152
152
  data-testid="ucl-uikit-badge"
153
153
  >
154
154
  <svg
@@ -41,7 +41,7 @@ exports[`Calendar > Snapshot: no date provided 1`] = `
41
41
  aria-expanded="false"
42
42
  aria-haspopup="listbox"
43
43
  aria-label="Month"
44
- class="ucl-uikit-select css-njh36g"
44
+ class="ucl-uikit-select css-1mgirop"
45
45
  data-testid="ucl-uikit-select"
46
46
  role="combobox"
47
47
  tabindex="0"
@@ -88,7 +88,7 @@ exports[`Calendar > Snapshot: no date provided 1`] = `
88
88
  aria-expanded="false"
89
89
  aria-haspopup="listbox"
90
90
  aria-label="Year"
91
- class="ucl-uikit-select css-njh36g"
91
+ class="ucl-uikit-select css-1mgirop"
92
92
  data-testid="ucl-uikit-select"
93
93
  role="combobox"
94
94
  tabindex="0"
@@ -921,7 +921,7 @@ exports[`Calendar > Snapshot: with date provided 1`] = `
921
921
  aria-expanded="false"
922
922
  aria-haspopup="listbox"
923
923
  aria-label="Month"
924
- class="ucl-uikit-select css-njh36g"
924
+ class="ucl-uikit-select css-1mgirop"
925
925
  data-testid="ucl-uikit-select"
926
926
  role="combobox"
927
927
  tabindex="0"
@@ -968,7 +968,7 @@ exports[`Calendar > Snapshot: with date provided 1`] = `
968
968
  aria-expanded="false"
969
969
  aria-haspopup="listbox"
970
970
  aria-label="Year"
971
- class="ucl-uikit-select css-njh36g"
971
+ class="ucl-uikit-select css-1mgirop"
972
972
  data-testid="ucl-uikit-select"
973
973
  role="combobox"
974
974
  tabindex="0"
@@ -38,7 +38,7 @@ const Callout = ({
38
38
  gap: 30px;
39
39
  width: 100%;
40
40
  padding: ${theme.padding.p24};
41
- background-color: ${theme.colour.surface.brand};
41
+ background-color: ${theme.colour.surface.brandSecondary};
42
42
  border: 1px solid ${theme.colour.border.brand};
43
43
  `;
44
44
 
@@ -6,11 +6,11 @@ exports[`Datepicker > Snapshot: no date provided 1`] = `
6
6
  data-testid="ucl-uikit-datepicker"
7
7
  >
8
8
  <div
9
- class="ucl-uikit-datepicker__visible-field css-2ui4ji"
9
+ class="ucl-uikit-datepicker__visible-field css-51hkje"
10
10
  data-testid="ucl-uikit-datepicker__visible-field"
11
11
  >
12
12
  <input
13
- class="ucl-uikit-datepicker__input css-19gsrgi"
13
+ class="ucl-uikit-datepicker__input css-gjp4ve"
14
14
  data-testid="ucl-uikit-datepicker__input"
15
15
  inputmode="numeric"
16
16
  placeholder="DD/MM/YYYY"
@@ -25,7 +25,7 @@ exports[`Datepicker > Snapshot: no date provided 1`] = `
25
25
  type="button"
26
26
  >
27
27
  <svg
28
- class="ucl-uikit-icon css-kzaqcy"
28
+ class="ucl-uikit-icon css-7p0vcp"
29
29
  data-testid="ucl-uikit-icon"
30
30
  fill="none"
31
31
  focusable="false"
@@ -61,11 +61,11 @@ exports[`Datepicker > Snapshot: with date provided 1`] = `
61
61
  data-testid="ucl-uikit-datepicker"
62
62
  >
63
63
  <div
64
- class="ucl-uikit-datepicker__visible-field css-2ui4ji"
64
+ class="ucl-uikit-datepicker__visible-field css-51hkje"
65
65
  data-testid="ucl-uikit-datepicker__visible-field"
66
66
  >
67
67
  <input
68
- class="ucl-uikit-datepicker__input css-19gsrgi"
68
+ class="ucl-uikit-datepicker__input css-gjp4ve"
69
69
  data-testid="ucl-uikit-datepicker__input"
70
70
  inputmode="numeric"
71
71
  placeholder="DD/MM/YYYY"
@@ -80,7 +80,7 @@ exports[`Datepicker > Snapshot: with date provided 1`] = `
80
80
  type="button"
81
81
  >
82
82
  <svg
83
- class="ucl-uikit-icon css-kzaqcy"
83
+ class="ucl-uikit-icon css-7p0vcp"
84
84
  data-testid="ucl-uikit-icon"
85
85
  fill="none"
86
86
  focusable="false"
@@ -45,6 +45,7 @@ const DatepickerInput = ({
45
45
  height: 100%;
46
46
  padding: 0 0 0 16px;
47
47
  border: none;
48
+ border-radius: ${theme.radius.r4} 0 0 ${theme.radius.r4};
48
49
  color: ${theme.colour.text.default};
49
50
  background-color: ${theme.colour.fill.inverse};
50
51
  font-family: ${md.fontFamily};
@@ -44,6 +44,7 @@ const VisibleField = ({
44
44
  height: 100%;
45
45
  box-sizing: border-box;
46
46
  border: 1px solid ${theme.colour.speciality.inputDefault};
47
+ border-radius: ${theme.radius.r4};
47
48
  background-color: ${theme.colour.fill.inverse};
48
49
 
49
50
  &:focus-within {
@@ -88,7 +89,7 @@ const VisibleField = ({
88
89
  // TODO: This ought to be fixed in the <Icon> component itself.
89
90
  const iconStyle = css`
90
91
  min-width: 24px;
91
- color: ${disabled ? theme.colour.icon.disabled : theme.colour.icon.tertiary};
92
+ color: ${disabled ? theme.colour.icon.disabled : theme.colour.icon.secondary};
92
93
  `;
93
94
 
94
95
  // Determine which icon to show
@@ -25,6 +25,7 @@ const NativeDatepicker = ({
25
25
  box-sizing: border-box;
26
26
  padding: 0 16px;
27
27
  border: 1px solid ${theme.colour.speciality.inputDefault};
28
+ border-radius: ${theme.radius.r4};
28
29
  color: ${theme.colour.text.default};
29
30
  font-family: ${md.fontFamily};
30
31
  font-feature-settings: ${md.fontSettings};
@@ -4,7 +4,7 @@ exports[`Select > Snapshot: default 1`] = `
4
4
  <div
5
5
  aria-expanded="false"
6
6
  aria-haspopup="listbox"
7
- class="ucl-uikit-select css-1gu3yiz"
7
+ class="ucl-uikit-select css-1o1z0ec"
8
8
  data-testid="ucl-uikit-select"
9
9
  role="combobox"
10
10
  tabindex="0"
@@ -604,6 +604,7 @@ const CustomSelect = <T extends string | number>({
604
604
  font-weight: ${md.fontWeight};
605
605
  line-height: ${md.lineHeight}%;
606
606
  border: ${theme.border.b1} solid ${theme.colour.speciality.inputDefault};
607
+ border-radius: ${theme.radius.r4};
607
608
  cursor: pointer;
608
609
  user-select: none;
609
610
 
@@ -44,6 +44,7 @@ const NativeSelect = ({
44
44
  line-height: ${md.lineHeight}%;
45
45
  background-color: ${theme.colour.fill.inverse};
46
46
  border: ${theme.border.b1} solid ${theme.colour.border.secondary};
47
+ border-radius: ${theme.radius.r4};
47
48
  color: ${theme.colour.text.default};
48
49
  appearance: none;
49
50
  -webkit-appearance: none;
@@ -8,6 +8,9 @@ const meta = {
8
8
  parameters: {
9
9
  layout: 'centered',
10
10
  },
11
+ args: {
12
+ style: { width: 507 },
13
+ },
11
14
  tags: ['autodocs'],
12
15
  } satisfies Meta<typeof ToggleGroup>;
13
16
 
@@ -7,18 +7,23 @@ import {
7
7
  useState,
8
8
  } from 'react';
9
9
 
10
- import { css } from '@emotion/css';
10
+ import { css, cx } from '@emotion/css';
11
11
  import useTheme from '../../theme/useTheme';
12
12
  import useMediaQuery from '../../hooks/useMediaQuery';
13
13
  import Select, { NonClearableSelectProps } from '../Select';
14
14
  import ToggleGroupContext from './ToggleGroupContext';
15
- import Item, { ItemProps } from './subcomponents/Item';
15
+ import ToggleGroupItem, {
16
+ ToggleGroupItemProps,
17
+ } from './subcomponents/ToggleGroupItem';
18
+
19
+ export const NAME = 'ucl-uikit-toggle-group';
16
20
 
17
21
  export interface ToggleGroupProps extends React.HTMLAttributes<HTMLDivElement> {
18
22
  children: React.ReactNode;
19
23
  value?: string;
20
24
  defaultValue?: string;
21
25
  onValueChange?: (value: string) => void;
26
+ testId?: string;
22
27
  selectProps?: Omit<
23
28
  NonClearableSelectProps<string>,
24
29
  'options' | 'value' | 'onValueChange'
@@ -27,7 +32,14 @@ export interface ToggleGroupProps extends React.HTMLAttributes<HTMLDivElement> {
27
32
 
28
33
  const MOBILE_SELECT_ITEM_THRESHOLD = 2;
29
34
 
30
- const NAVIGATION_KEYS = ['ArrowRight', 'ArrowLeft', 'Home', 'End'];
35
+ const NAVIGATION_KEYS = [
36
+ 'ArrowRight',
37
+ 'ArrowDown',
38
+ 'ArrowLeft',
39
+ 'ArrowUp',
40
+ 'Home',
41
+ 'End',
42
+ ];
31
43
 
32
44
  const getEnabledItems = (container: HTMLElement) =>
33
45
  Array.from(
@@ -38,13 +50,13 @@ const getEnabledItems = (container: HTMLElement) =>
38
50
 
39
51
  const getItemsFromChildren = (
40
52
  children: React.ReactNode
41
- ): React.ReactElement<ItemProps>[] =>
53
+ ): React.ReactElement<ToggleGroupItemProps>[] =>
42
54
  Children.toArray(children).flatMap((child) => {
43
55
  if (!isValidElement<{ children?: React.ReactNode }>(child)) {
44
56
  return [];
45
57
  }
46
- if (child.type === Item) {
47
- return [child as React.ReactElement<ItemProps>];
58
+ if (child.type === ToggleGroupItem) {
59
+ return [child as React.ReactElement<ToggleGroupItemProps>];
48
60
  }
49
61
  return getItemsFromChildren(child.props.children);
50
62
  });
@@ -55,6 +67,10 @@ const ToggleGroup = ({
55
67
  defaultValue,
56
68
  onValueChange,
57
69
  selectProps,
70
+ className,
71
+ onKeyDown,
72
+ testId = NAME,
73
+ ...htmlAttributes
58
74
  }: ToggleGroupProps) => {
59
75
  const [theme] = useTheme();
60
76
  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
@@ -63,6 +79,14 @@ const ToggleGroup = ({
63
79
  const isMobile = !useMediaQuery(`(min-width: ${theme.breakpoints.tablet}px)`);
64
80
  const items = getItemsFromChildren(children);
65
81
  const name = useId();
82
+ const hasSublabel = items.some((item) => item.props.sublabel);
83
+ const firstEnabledValue = items.find((item) => !item.props.disabled)?.props
84
+ .value;
85
+ const tabbableValue = items.some(
86
+ (item) => item.props.value === selectedValue && !item.props.disabled
87
+ )
88
+ ? selectedValue
89
+ : firstEnabledValue;
66
90
 
67
91
  const setValue = (nextValue: string) => {
68
92
  if (!isControlled) {
@@ -73,6 +97,12 @@ const ToggleGroup = ({
73
97
  };
74
98
 
75
99
  const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
100
+ onKeyDown?.(event);
101
+
102
+ if (event.defaultPrevented) {
103
+ return;
104
+ }
105
+
76
106
  if (!NAVIGATION_KEYS.includes(event.key)) {
77
107
  return;
78
108
  }
@@ -91,8 +121,10 @@ const ToggleGroup = ({
91
121
  const nextIndex =
92
122
  {
93
123
  ArrowRight: (currentIndex + 1) % enabledItems.length,
124
+ ArrowDown: (currentIndex + 1) % enabledItems.length,
94
125
  ArrowLeft:
95
126
  (currentIndex - 1 + enabledItems.length) % enabledItems.length,
127
+ ArrowUp: (currentIndex - 1 + enabledItems.length) % enabledItems.length,
96
128
  Home: 0,
97
129
  End: enabledItems.length - 1,
98
130
  }[event.key] ?? currentIndex;
@@ -104,7 +136,6 @@ const ToggleGroup = ({
104
136
 
105
137
  const toggleGroupStyle = css`
106
138
  display: flex;
107
- gap: -1px;
108
139
  padding: ${theme.padding.p4};
109
140
  background-color: ${theme.colour.fill.subtle};
110
141
  border-radius: ${theme.radius.r4};
@@ -114,6 +145,8 @@ const ToggleGroup = ({
114
145
  return (
115
146
  <Select
116
147
  {...selectProps}
148
+ className={cx(NAME, selectProps?.className, className)}
149
+ testId={testId}
117
150
  options={items.map((item) => ({
118
151
  label: item.props.label,
119
152
  value: item.props.value,
@@ -126,10 +159,18 @@ const ToggleGroup = ({
126
159
 
127
160
  return (
128
161
  <ToggleGroupContext.Provider
129
- value={{ value: selectedValue, setValue, name }}
162
+ value={{
163
+ value: selectedValue,
164
+ setValue,
165
+ name,
166
+ tabbableValue,
167
+ hasSublabel,
168
+ }}
130
169
  >
131
170
  <div
132
- className={toggleGroupStyle}
171
+ {...htmlAttributes}
172
+ className={cx(NAME, toggleGroupStyle, className)}
173
+ data-testid={testId}
133
174
  role='radiogroup'
134
175
  onKeyDown={handleKeyDown}
135
176
  >
@@ -139,6 +180,6 @@ const ToggleGroup = ({
139
180
  );
140
181
  };
141
182
 
142
- ToggleGroup.Item = Item;
183
+ ToggleGroup.Item = ToggleGroupItem;
143
184
 
144
185
  export default ToggleGroup;
@@ -4,6 +4,8 @@ export interface ToggleGroupContextType {
4
4
  value: string | undefined;
5
5
  setValue: (value: string) => void;
6
6
  name: string;
7
+ tabbableValue: string | undefined;
8
+ hasSublabel: boolean;
7
9
  }
8
10
 
9
11
  const ToggleGroupContext = createContext<ToggleGroupContextType | undefined>(