uikit-react-public 0.29.6 → 0.30.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 (143) hide show
  1. package/dist/components/Accordion/Accordion.stories.d.ts +1 -1
  2. package/dist/components/Alert/Alert.stories.d.ts +1 -1
  3. package/dist/components/AppHeader/AppHeader.stories.d.ts +1 -1
  4. package/dist/components/AppMenu/AppMenu.stories.d.ts +1 -1
  5. package/dist/components/Avatar/Avatar.stories.d.ts +1 -1
  6. package/dist/components/Badge/Badge.stories.d.ts +1 -1
  7. package/dist/components/BaseCheckbox/BaseCheckbox.stories.d.ts +1 -1
  8. package/dist/components/Blanket/Blanket.stories.d.ts +1 -1
  9. package/dist/components/Breadcrumbs/Breadcrumbs.stories.d.ts +1 -1
  10. package/dist/components/Button/Button.stories.d.ts +1 -1
  11. package/dist/components/Calendar/Calendar.stories.d.ts +1 -1
  12. package/dist/components/Calendar/subcomponents/Day.stories.d.ts +1 -1
  13. package/dist/components/Checkbox/Checkbox.stories.d.ts +1 -1
  14. package/dist/components/Chip/Chip.stories.d.ts +1 -1
  15. package/dist/components/Datepicker/Datepicker.stories.d.ts +1 -1
  16. package/dist/components/Dialog/BaseDialog.d.ts +1 -1
  17. package/dist/components/Dialog/Dialog.d.ts +1 -1
  18. package/dist/components/Dialog/Dialog.stories.d.ts +2 -2
  19. package/dist/components/Divider/Divider.stories.d.ts +1 -1
  20. package/dist/components/Dropdown/Dropdown.stories.d.ts +1 -1
  21. package/dist/components/FeedbackDialog/FeedbackDialog.stories.d.ts +1 -1
  22. package/dist/components/Field/Field.stories.d.ts +1 -1
  23. package/dist/components/FileInput/FileInput.stories.d.ts +1 -1
  24. package/dist/components/Footer/Footer.stories.d.ts +1 -1
  25. package/dist/components/Header/Header.stories.d.ts +1 -1
  26. package/dist/components/Heading/Heading.stories.d.ts +1 -1
  27. package/dist/components/Icon/Icon.stories.d.ts +1 -1
  28. package/dist/components/IconButton/IconButton.stories.d.ts +1 -1
  29. package/dist/components/Input/Input.stories.d.ts +1 -1
  30. package/dist/components/Label/Label.stories.d.ts +1 -1
  31. package/dist/components/Layout/Layout.stories.d.ts +1 -1
  32. package/dist/components/Link/Link.stories.d.ts +1 -1
  33. package/dist/components/Main/Main.stories.d.ts +1 -1
  34. package/dist/components/Modal/Modal.stories.d.ts +1 -1
  35. package/dist/components/NativeDatepicker/NativeDatepicker.stories.d.ts +1 -1
  36. package/dist/components/Overlay/Overlay.stories.d.ts +2 -2
  37. package/dist/components/Pagination/Pagination.stories.d.ts +1 -1
  38. package/dist/components/Paragraph/Paragraph.stories.d.ts +1 -1
  39. package/dist/components/Radio/Radio.stories.d.ts +1 -1
  40. package/dist/components/Search/Search.stories.d.ts +1 -1
  41. package/dist/components/Select/Select.stories.d.ts +1 -1
  42. package/dist/components/Snackbar/Snackbar.stories.d.ts +1 -1
  43. package/dist/components/Spinner/Spinner.stories.d.ts +1 -1
  44. package/dist/components/StandaloneLink/StandaloneLink.stories.d.ts +1 -1
  45. package/dist/components/Table/Table.stories.d.ts +1 -1
  46. package/dist/components/Table/subcomponents/Cell/Cell.stories.d.ts +2 -2
  47. package/dist/components/Table/subcomponents/HeadCell/HeadCell.stories.d.ts +2 -2
  48. package/dist/components/Tabs/Tab.d.ts +11 -5
  49. package/dist/components/Tabs/TabContext.d.ts +14 -8
  50. package/dist/components/Tabs/Tabs.d.ts +25 -8
  51. package/dist/components/Tabs/Tabs.stories.d.ts +5 -9
  52. package/dist/components/Tabs/TabsList.d.ts +9 -0
  53. package/dist/components/Tabs/TabsPanel.d.ts +10 -0
  54. package/dist/components/Tabs/index.d.ts +2 -1
  55. package/dist/components/Textarea/Textarea.stories.d.ts +1 -1
  56. package/dist/components/Timepicker/Timepicker.stories.d.ts +1 -1
  57. package/dist/components/Toggle/Toggle.stories.d.ts +1 -1
  58. package/dist/components/Tooltip/Tooltip.stories.d.ts +1 -1
  59. package/dist/components/UclLogo/UclLogo.stories.d.ts +1 -1
  60. package/dist/components/WeekPicker/WeekPicker.stories.d.ts +1 -1
  61. package/dist/components/index.d.ts +1 -1
  62. package/dist/index.js +4392 -4123
  63. package/dist/utils/announce.d.ts +2 -1
  64. package/lib/Welcome.mdx +1 -1
  65. package/lib/components/Accordion/Accordion.stories.tsx +1 -1
  66. package/lib/components/Alert/Alert.mdx +1 -1
  67. package/lib/components/Alert/Alert.stories.tsx +1 -1
  68. package/lib/components/AppHeader/AppHeader.stories.tsx +1 -1
  69. package/lib/components/AppMenu/AppMenu.stories.tsx +1 -1
  70. package/lib/components/Avatar/Avatar.mdx +1 -1
  71. package/lib/components/Avatar/Avatar.stories.tsx +1 -1
  72. package/lib/components/Badge/Badge.stories.tsx +1 -1
  73. package/lib/components/BaseCheckbox/BaseCheckbox.stories.tsx +2 -2
  74. package/lib/components/Blanket/Blanket.stories.tsx +1 -1
  75. package/lib/components/Breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  76. package/lib/components/Button/Button.mdx +1 -1
  77. package/lib/components/Button/Button.stories.tsx +2 -2
  78. package/lib/components/Calendar/Calendar.stories.tsx +2 -2
  79. package/lib/components/Calendar/subcomponents/Day.stories.tsx +1 -1
  80. package/lib/components/Checkbox/Checkbox.stories.tsx +2 -2
  81. package/lib/components/Chip/Chip.stories.tsx +2 -2
  82. package/lib/components/Datepicker/Datepicker.stories.tsx +2 -2
  83. package/lib/components/Dialog/BaseDialog.tsx +180 -161
  84. package/lib/components/Dialog/Dialog.stories.tsx +1 -1
  85. package/lib/components/Dialog/Dialog.tsx +15 -11
  86. package/lib/components/Divider/Divider.stories.tsx +1 -1
  87. package/lib/components/Dropdown/Dropdown.stories.tsx +1 -1
  88. package/lib/components/FeedbackDialog/FeedbackDialog.stories.tsx +1 -1
  89. package/lib/components/Field/Field.stories.tsx +2 -2
  90. package/lib/components/FileInput/FileInput.stories.tsx +1 -1
  91. package/lib/components/Footer/Footer.stories.tsx +1 -1
  92. package/lib/components/Header/Header.mdx +1 -1
  93. package/lib/components/Header/Header.stories.tsx +1 -1
  94. package/lib/components/Heading/Documentation.mdx +1 -1
  95. package/lib/components/Heading/Heading.stories.tsx +1 -1
  96. package/lib/components/Icon/Icon.stories.tsx +1 -1
  97. package/lib/components/IconButton/IconButton.stories.tsx +1 -1
  98. package/lib/components/Input/Documentation.mdx +1 -1
  99. package/lib/components/Input/Input.stories.tsx +1 -1
  100. package/lib/components/Label/Label.stories.tsx +1 -1
  101. package/lib/components/Layout/Layout.stories.tsx +1 -1
  102. package/lib/components/Link/Link.stories.tsx +1 -1
  103. package/lib/components/Main/Main.stories.tsx +1 -1
  104. package/lib/components/Modal/Modal.stories.tsx +1 -1
  105. package/lib/components/NativeDatepicker/NativeDatepicker.stories.tsx +2 -2
  106. package/lib/components/Overlay/Overlay.stories.tsx +1 -1
  107. package/lib/components/Overlay/Overlay.tsx +1 -4
  108. package/lib/components/Pagination/Pagination.stories.tsx +1 -1
  109. package/lib/components/Paragraph/Paragraph.stories.tsx +1 -1
  110. package/lib/components/Radio/Radio.stories.tsx +2 -2
  111. package/lib/components/Search/Search.stories.tsx +1 -1
  112. package/lib/components/Select/Select.mdx +1 -1
  113. package/lib/components/Select/Select.stories.tsx +2 -2
  114. package/lib/components/Select/Select.types.ts +1 -2
  115. package/lib/components/Snackbar/Snackbar.stories.tsx +1 -1
  116. package/lib/components/Spinner/Spinner.stories.tsx +1 -1
  117. package/lib/components/StandaloneLink/StandaloneLink.stories.tsx +1 -1
  118. package/lib/components/Table/Table.stories.tsx +1 -1
  119. package/lib/components/Table/subcomponents/Cell/Cell.stories.tsx +2 -2
  120. package/lib/components/Table/subcomponents/HeadCell/HeadCell.stories.tsx +1 -1
  121. package/lib/components/Tabs/Tab.tsx +209 -36
  122. package/lib/components/Tabs/TabContext.tsx +20 -7
  123. package/lib/components/Tabs/Tabs.stories.tsx +87 -68
  124. package/lib/components/Tabs/Tabs.tsx +129 -37
  125. package/lib/components/Tabs/TabsList.tsx +134 -0
  126. package/lib/components/Tabs/TabsPanel.tsx +55 -0
  127. package/lib/components/Tabs/__tests__/Tabs.test.tsx +173 -105
  128. package/lib/components/Tabs/index.ts +8 -1
  129. package/lib/components/Textarea/Textarea.stories.tsx +1 -1
  130. package/lib/components/Timepicker/Timepicker.stories.tsx +1 -1
  131. package/lib/components/Toggle/Documentation.mdx +1 -1
  132. package/lib/components/Toggle/Toggle.stories.tsx +1 -1
  133. package/lib/components/Tooltip/Tooltip.stories.tsx +1 -1
  134. package/lib/components/UclLogo/UclLogo.stories.tsx +1 -1
  135. package/lib/components/WeekPicker/WeekPicker.stories.tsx +2 -2
  136. package/lib/components/common/Common.mdx +1 -1
  137. package/lib/components/index.ts +7 -1
  138. package/lib/theme/Icons.mdx +1 -1
  139. package/lib/theme/Typography.mdx +1 -1
  140. package/lib/utils/__tests__/announce.test.ts +53 -0
  141. package/lib/utils/announce.ts +33 -10
  142. package/package.json +8 -11
  143. package/lib/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap +0 -185
@@ -1,122 +1,190 @@
1
- import { describe, expect, test } from 'vitest';
2
- import { render } from '@testing-library/react';
1
+ import { describe, expect, test, vi } from 'vitest';
2
+ import { fireEvent, render, screen } from '@testing-library/react';
3
3
  import Tabs from '../Tabs';
4
4
  import { ThemeContextProvider } from '../../../theme/useTheme';
5
5
 
6
+ const wrap = (ui: React.ReactElement) =>
7
+ render(<ThemeContextProvider>{ui}</ThemeContextProvider>);
8
+
6
9
  describe('Tabs', () => {
7
- // Snapshot tests
10
+ test('renders a tablist with selectable button tabs', () => {
11
+ const onValueChange = vi.fn();
8
12
 
9
- test('snapshot: Minimal props', () => {
10
- const renderResult = render(
11
- <ThemeContextProvider>
12
- <Tabs activeTab='1'>
13
- <Tabs.Tab
14
- label='Tab Item 1'
15
- value='1'
16
- />
17
- <Tabs.Tab
18
- label='Tab Item 2'
19
- value='2'
20
- />
21
- <Tabs.Tab
22
- label='Tab Item 3'
23
- value='3'
24
- />
25
- <Tabs.Tab
26
- label='Tab Item 4'
27
- value='4'
28
- />
29
- </Tabs>
30
- </ThemeContextProvider>
13
+ wrap(
14
+ <Tabs
15
+ defaultValue='overview'
16
+ onValueChange={onValueChange}
17
+ >
18
+ <Tabs.List aria-label='Course sections'>
19
+ <Tabs.Tab value='overview'>Overview</Tabs.Tab>
20
+ <Tabs.Tab value='modules'>Modules</Tabs.Tab>
21
+ </Tabs.List>
22
+ </Tabs>
23
+ );
24
+
25
+ expect(
26
+ screen.getByRole('tablist', { name: 'Course sections' })
27
+ ).toBeInTheDocument();
28
+ expect(screen.getByRole('tab', { name: 'Overview' })).toHaveAttribute(
29
+ 'aria-selected',
30
+ 'true'
31
+ );
32
+ expect(screen.getByRole('tab', { name: 'Overview' })).not.toHaveAttribute(
33
+ 'aria-controls'
34
+ );
35
+
36
+ fireEvent.click(screen.getByRole('tab', { name: 'Modules' }));
37
+
38
+ expect(onValueChange).toHaveBeenCalledWith('modules');
39
+ expect(screen.getByRole('tab', { name: 'Modules' })).toHaveAttribute(
40
+ 'aria-selected',
41
+ 'true'
31
42
  );
32
- expect(renderResult.container.firstChild).toMatchSnapshot();
33
43
  });
34
44
 
35
- test('snapshot: Custom test id', () => {
36
- const renderResult = render(
37
- <ThemeContextProvider>
38
- <Tabs
39
- activeTab='1'
40
- data-testid='custom-test-id'
41
- >
42
- <Tabs.Tab
43
- label='Tab Item 1'
44
- value='1'
45
- />
46
- <Tabs.Tab
47
- label='Tab Item 2'
48
- value='2'
49
- />
50
- <Tabs.Tab
51
- label='Tab Item 3'
52
- value='3'
53
- />
54
- <Tabs.Tab
55
- label='Tab Item 4'
56
- value='4'
57
- />
58
- </Tabs>
59
- </ThemeContextProvider>
45
+ test('supports panels linked to their tabs', () => {
46
+ wrap(
47
+ <Tabs defaultValue='overview'>
48
+ <Tabs.List>
49
+ <Tabs.Tab value='overview'>Overview</Tabs.Tab>
50
+ <Tabs.Tab value='modules'>Modules</Tabs.Tab>
51
+ </Tabs.List>
52
+ <Tabs.Panel value='overview'>Overview content</Tabs.Panel>
53
+ <Tabs.Panel value='modules'>Modules content</Tabs.Panel>
54
+ </Tabs>
55
+ );
56
+
57
+ const overviewTab = screen.getByRole('tab', { name: 'Overview' });
58
+ const overviewPanel = screen.getByRole('tabpanel', {
59
+ name: 'Overview',
60
+ });
61
+
62
+ expect(overviewTab).toHaveAttribute(
63
+ 'aria-controls',
64
+ overviewPanel.getAttribute('id')
60
65
  );
61
- expect(renderResult.container.firstChild).toMatchSnapshot();
66
+ expect(screen.queryByText('Modules content')).not.toBeInTheDocument();
62
67
  });
63
68
 
64
- test('snapshot: Auto width', () => {
65
- const renderResult = render(
66
- <ThemeContextProvider>
67
- <Tabs
68
- activeTab='1'
69
- fullWidth={false}
70
- >
71
- <Tabs.Tab
72
- label='Tab Item 1'
73
- value='1'
74
- />
75
- <Tabs.Tab
76
- label='Tab Item 2'
77
- value='2'
78
- />
79
- <Tabs.Tab
80
- label='Tab Item 3'
81
- value='3'
82
- />
83
- <Tabs.Tab
84
- label='A longer tab item showing auto width'
85
- value='4'
86
- />
87
- </Tabs>
88
- </ThemeContextProvider>
69
+ test('supports asChild link tabs', () => {
70
+ wrap(
71
+ <Tabs value='overview'>
72
+ <Tabs.List>
73
+ <Tabs.Tab
74
+ value='overview'
75
+ asChild
76
+ >
77
+ <a href='/overview'>Overview</a>
78
+ </Tabs.Tab>
79
+ <Tabs.Tab
80
+ value='modules'
81
+ asChild
82
+ >
83
+ <a href='/modules'>Modules</a>
84
+ </Tabs.Tab>
85
+ </Tabs.List>
86
+ </Tabs>
89
87
  );
90
- expect(renderResult.container.firstChild).toMatchSnapshot();
88
+
89
+ const overview = screen.getByRole('tab', { name: 'Overview' });
90
+
91
+ expect(overview.tagName).toBe('A');
92
+ expect(overview).toHaveAttribute('href', '/overview');
93
+ expect(overview).toHaveAttribute('aria-selected', 'true');
91
94
  });
92
95
 
93
- test('snapshot: Counter', () => {
94
- const renderResult = render(
95
- <ThemeContextProvider>
96
- <Tabs activeTab='1'>
97
- <Tabs.Tab
98
- counter={12}
99
- label='Tab Item 1'
100
- value='1'
101
- />
102
- <Tabs.Tab
103
- counter={6}
104
- label='Tab Item 2'
105
- value='2'
106
- />
107
- <Tabs.Tab
108
- counter={9}
109
- label='Tab Item 3'
110
- value='3'
111
- />
112
- <Tabs.Tab
113
- counter={0}
114
- label='Tab Item 4'
115
- value='4'
116
- />
117
- </Tabs>
118
- </ThemeContextProvider>
96
+ test('renders counts as plain tab text', () => {
97
+ wrap(
98
+ <Tabs defaultValue='overview'>
99
+ <Tabs.List>
100
+ <Tabs.Tab
101
+ count={12}
102
+ value='overview'
103
+ >
104
+ Overview
105
+ </Tabs.Tab>
106
+ </Tabs.List>
107
+ </Tabs>
108
+ );
109
+
110
+ expect(screen.getByRole('tab', { name: 'Overview 12' })).toHaveTextContent(
111
+ 'Overview 12'
112
+ );
113
+ expect(screen.queryByRole('tab', { name: 'Overview (12)' })).toBe(null);
114
+ });
115
+
116
+ test('moves focus with arrow keys and activates automatically', () => {
117
+ wrap(
118
+ <Tabs defaultValue='overview'>
119
+ <Tabs.List>
120
+ <Tabs.Tab value='overview'>Overview</Tabs.Tab>
121
+ <Tabs.Tab value='modules'>Modules</Tabs.Tab>
122
+ <Tabs.Tab value='people'>People</Tabs.Tab>
123
+ </Tabs.List>
124
+ </Tabs>
125
+ );
126
+
127
+ const overview = screen.getByRole('tab', { name: 'Overview' });
128
+ const modules = screen.getByRole('tab', { name: 'Modules' });
129
+
130
+ overview.focus();
131
+ fireEvent.keyDown(screen.getByRole('tablist'), { key: 'ArrowRight' });
132
+
133
+ expect(modules).toHaveFocus();
134
+ expect(modules).toHaveAttribute('aria-selected', 'true');
135
+ });
136
+
137
+ test('keeps deprecated activeTab/onChange/label props working', () => {
138
+ const onChange = vi.fn();
139
+
140
+ wrap(
141
+ <Tabs
142
+ activeTab='overview'
143
+ onChange={onChange}
144
+ >
145
+ <Tabs.Tab
146
+ label='Overview'
147
+ value='overview'
148
+ />
149
+ <Tabs.Tab
150
+ label='Modules'
151
+ value='modules'
152
+ />
153
+ </Tabs>
154
+ );
155
+
156
+ fireEvent.click(screen.getByRole('tab', { name: 'Modules' }));
157
+
158
+ expect(screen.getByRole('tablist')).toHaveClass('ucl-uikit-tabs__list');
159
+ expect(onChange).toHaveBeenCalledWith('modules');
160
+ });
161
+
162
+ test('does not activate disabled tabs', () => {
163
+ const onValueChange = vi.fn();
164
+
165
+ wrap(
166
+ <Tabs
167
+ defaultValue='overview'
168
+ onValueChange={onValueChange}
169
+ >
170
+ <Tabs.List>
171
+ <Tabs.Tab value='overview'>Overview</Tabs.Tab>
172
+ <Tabs.Tab
173
+ value='modules'
174
+ disabled
175
+ >
176
+ Modules
177
+ </Tabs.Tab>
178
+ </Tabs.List>
179
+ </Tabs>
180
+ );
181
+
182
+ fireEvent.click(screen.getByRole('tab', { name: 'Modules' }));
183
+
184
+ expect(onValueChange).not.toHaveBeenCalled();
185
+ expect(screen.getByRole('tab', { name: 'Overview' })).toHaveAttribute(
186
+ 'aria-selected',
187
+ 'true'
119
188
  );
120
- expect(renderResult.container.firstChild).toMatchSnapshot();
121
189
  });
122
190
  });
@@ -1,2 +1,9 @@
1
1
  export { default } from './Tabs';
2
- export type { TabsProps } from './Tabs';
2
+ export type {
3
+ TabsActivationMode,
4
+ TabsListProps,
5
+ TabsOrientation,
6
+ TabsPanelProps,
7
+ TabsProps,
8
+ } from './Tabs';
9
+ export type { TabProps } from './Tab';
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
 
3
3
  import Textarea from './Textarea';
4
4
 
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { useState } from 'react';
3
3
  import Timepicker from './Timepicker';
4
4
 
@@ -7,7 +7,7 @@ import {
7
7
  Controls,
8
8
  Source,
9
9
  ArgTypes,
10
- } from '@storybook/blocks';
10
+ } from '@storybook/addon-docs/blocks';
11
11
 
12
12
  export const usage = {
13
13
  default: `<Toggle />`,
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
 
3
3
  import Toggle from './Toggle';
4
4
 
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
 
3
3
  import Tooltip from './Tooltip';
4
4
 
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import UclLogo from './UclLogo';
3
3
 
4
4
  // Need this because the `UclLogo` component will default to 0 size :(
@@ -1,5 +1,5 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import { useArgs } from '@storybook/preview-api';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { useArgs } from 'storybook/preview-api';
3
3
  import WeekPicker from './WeekPicker';
4
4
 
5
5
  const meta = {
@@ -1,4 +1,4 @@
1
- import { Meta, Title, Subtitle, Canvas, Controls, ArgTypes } from '@storybook/blocks';
1
+ import { Meta, Title, Subtitle, Canvas, Controls, ArgTypes } from '@storybook/addon-docs/blocks';
2
2
  import marginsStyle from './marginsStyle';
3
3
  import { CommonMargins } from './../Button/Button.stories';
4
4
  import { theme } from '../../theme';
@@ -134,7 +134,13 @@ export { default as Alert } from './Alert';
134
134
  export type { AlertProps } from './Alert';
135
135
 
136
136
  export { default as Tabs } from './Tabs';
137
- export type { TabsProps } from './Tabs';
137
+ export type {
138
+ TabsActivationMode,
139
+ TabsListProps,
140
+ TabsOrientation,
141
+ TabsPanelProps,
142
+ TabsProps,
143
+ } from './Tabs';
138
144
  export type { TabProps } from './Tabs/Tab';
139
145
 
140
146
  export { default as Accordion } from './Accordion';
@@ -5,7 +5,7 @@ import {
5
5
  Subtitle,
6
6
  IconGallery,
7
7
  IconItem,
8
- } from "@storybook/blocks";
8
+ } from "@storybook/addon-docs/blocks";
9
9
  import { ThemeContextProvider } from "./useTheme";
10
10
  import { Icon } from "../components";
11
11
 
@@ -1,4 +1,4 @@
1
- import { Meta, Title, Subtitle, Typeset } from '@storybook/blocks';
1
+ import { Meta, Title, Subtitle, Typeset } from '@storybook/addon-docs/blocks';
2
2
  import theme from './original/defaultTheme';
3
3
 
4
4
  { console.log(theme.font) }
@@ -60,6 +60,59 @@ describe('announce (ARIA live region)', () => {
60
60
  expect(allRegions.length).toBe(1);
61
61
  });
62
62
 
63
+ test('appends the live region to the provided container', async () => {
64
+ const container = document.createElement('section');
65
+ container.setAttribute('data-testid', 'announce-container');
66
+ document.body.appendChild(container);
67
+
68
+ announce('Container message', false, container);
69
+
70
+ const region = await screen.findByTestId('aria-live-region');
71
+
72
+ expect(container.contains(region)).toBe(true);
73
+ expect(document.body.contains(region)).toBe(true);
74
+ });
75
+
76
+ test('falls back to document.body when no container is provided', async () => {
77
+ announce('Default container message');
78
+
79
+ const region = await screen.findByTestId('aria-live-region');
80
+
81
+ expect(region.parentElement).toBe(document.body);
82
+ });
83
+
84
+ test('moves live region when a new container is provided later', async () => {
85
+ const firstContainer = document.createElement('div');
86
+ const secondContainer = document.createElement('div');
87
+ document.body.appendChild(firstContainer);
88
+ document.body.appendChild(secondContainer);
89
+
90
+ announce('First container message', false, firstContainer);
91
+
92
+ const region = await screen.findByTestId('aria-live-region');
93
+ expect(firstContainer.contains(region)).toBe(true);
94
+
95
+ announce('Second container message', false, secondContainer);
96
+
97
+ expect(firstContainer.contains(region)).toBe(false);
98
+ expect(secondContainer.contains(region)).toBe(true);
99
+ });
100
+
101
+ test('recreates live region when previous one was removed from the DOM', async () => {
102
+ announce('Initial message');
103
+
104
+ const initialRegion = await screen.findByTestId('aria-live-region');
105
+ initialRegion.remove();
106
+
107
+ expect(screen.queryByTestId('aria-live-region')).toBeNull();
108
+
109
+ announce('Message after removal');
110
+
111
+ const recreatedRegion = await screen.findByTestId('aria-live-region');
112
+ expect(recreatedRegion).not.toBe(initialRegion);
113
+ expect(recreatedRegion.parentElement).toBe(document.body);
114
+ });
115
+
63
116
  test('queues multiple announcements and processes sequentially', async () => {
64
117
  vi.useFakeTimers();
65
118
 
@@ -11,6 +11,10 @@
11
11
  *
12
12
  * @param force - If true, forces the announcement even if it's the same as the last one.
13
13
  *
14
+ * @param container - Optional element to contain the live region. Accepts `null`
15
+ * to support passing values like `ref.current`; falls back to `document.body`
16
+ * when no container is available.
17
+ *
14
18
  * @example
15
19
  * announce("Item removed");
16
20
  *
@@ -26,14 +30,28 @@ const SR_DETECTION_DELAY = 100; // Time for SR to detect change
26
30
  const SR_PROCESSING_DELAY = 1000; // Time for SR to finish announcement
27
31
 
28
32
  /**
29
- * Creates the live region if it does not already exist.
33
+ * Creates the live region if it does not already exist, or moves it to a new
34
+ * container if one is provided. If the existing region has been removed from
35
+ * the DOM (e.g. a dialog unmounted), it will be recreated.
30
36
  */
31
- const initLiveRegion = (): void => {
32
- if (typeof document === 'undefined' || liveRegion) return;
37
+ const initLiveRegion = (container?: HTMLElement | null): void => {
38
+ if (typeof document === 'undefined') return;
33
39
 
34
- // Guard: body may not exist yet (e.g., script in <head>)
35
- const container = document.body || document.documentElement;
36
- if (!container) return;
40
+ // Reset if existing region has been removed from the DOM
41
+ if (liveRegion && !document.contains(liveRegion)) {
42
+ liveRegion = null;
43
+ }
44
+
45
+ const target = container || document.body || document.documentElement;
46
+ if (!target) return;
47
+
48
+ // If a specific container is requested and the region is elsewhere, move it
49
+ if (liveRegion && container && liveRegion.parentNode !== container) {
50
+ container.appendChild(liveRegion);
51
+ return;
52
+ }
53
+
54
+ if (liveRegion) return;
37
55
 
38
56
  const region = document.createElement('div');
39
57
  region.setAttribute('aria-live', 'polite');
@@ -48,7 +66,7 @@ const initLiveRegion = (): void => {
48
66
  region.style.height = '1px';
49
67
  region.style.overflow = 'hidden';
50
68
 
51
- document.body.appendChild(region);
69
+ target.appendChild(region);
52
70
  liveRegion = region;
53
71
  };
54
72
 
@@ -86,13 +104,18 @@ const processQueue = (): void => {
86
104
 
87
105
  /**
88
106
  * Announces a message to screen readers.
107
+ *
89
108
  */
90
- const announce = (message: string, force = false): void => {
109
+ const announce = (
110
+ message: string,
111
+ force = false,
112
+ container?: HTMLElement | null
113
+ ): void => {
91
114
  // SSR guard: do nothing if document is unavailable
92
115
  if (typeof document === 'undefined') return;
93
116
 
94
- // Initialize live region on first use
95
- if (!liveRegion) initLiveRegion();
117
+ // Ensure live region exists and is attached to the requested container when provided
118
+ initLiveRegion(container);
96
119
 
97
120
  // If forcing, clear the queue to prevent old messages
98
121
  if (force) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "uikit-react-public",
3
3
  "private": false,
4
4
  "license": "UNLICENSED",
5
- "version": "0.29.6",
5
+ "version": "0.30.1",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
@@ -51,18 +51,15 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@azure/msal-browser": "^4.7.0",
54
- "@chromatic-com/storybook": "^3.2.5",
54
+ "@chromatic-com/storybook": "^5.2.1",
55
55
  "@eslint/compat": "^1.2.7",
56
56
  "@eslint/eslintrc": "^3.3.0",
57
57
  "@eslint/js": "^9.22.0",
58
- "@storybook/addon-essentials": "^8.6.14",
59
- "@storybook/addon-interactions": "^8.6.4",
60
- "@storybook/addon-links": "^8.6.4",
61
- "@storybook/addon-onboarding": "^8.6.4",
62
- "@storybook/blocks": "^8.6.4",
63
- "@storybook/react": "^8.6.4",
64
- "@storybook/react-vite": "^8.6.4",
65
- "@storybook/test": "^8.6.4",
58
+ "@storybook/addon-links": "10.4.6",
59
+ "@storybook/addon-onboarding": "10.4.6",
60
+ "@storybook/react-vite": "10.4.6",
61
+ "@storybook/addon-docs": "10.4.6",
62
+ "eslint-plugin-storybook": "10.4.6",
66
63
  "@testing-library/jest-dom": "^6.6.3",
67
64
  "@testing-library/react": "^16.2.0",
68
65
  "@testing-library/user-event": "^14.6.1",
@@ -92,7 +89,7 @@
92
89
  "react": "^19.1.0",
93
90
  "react-dom": "^19.1.0",
94
91
  "react-router": "^7.13.1",
95
- "storybook": "^8.6.4",
92
+ "storybook": "10.4.6",
96
93
  "typescript": "^5.8.2",
97
94
  "typescript-eslint": "^8.26.1",
98
95
  "vite": "^6.2.1",