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,6 +1,7 @@
1
1
  /**
2
2
  * Announces a message to screen readers.
3
+ *
3
4
  */
4
- declare const announce: (message: string, force?: boolean) => void;
5
+ declare const announce: (message: string, force?: boolean, container?: HTMLElement | null) => void;
5
6
  export declare const __resetForTesting: () => void;
6
7
  export default announce;
package/lib/Welcome.mdx CHANGED
@@ -1,4 +1,4 @@
1
- import { Meta } from "@storybook/blocks";
1
+ import { Meta } from "@storybook/addon-docs/blocks";
2
2
  import { UclLogo } from "./components/";
3
3
 
4
4
  <Meta title="Welcome" />
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import Accordion from './Accordion';
3
3
 
4
4
  const meta = {
@@ -1,5 +1,5 @@
1
1
  import * as AlertStories from './Alert.stories';
2
- import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/blocks';
2
+ import { Meta, Title, Subtitle, Canvas, Controls } from '@storybook/addon-docs/blocks';
3
3
 
4
4
  <Meta of={AlertStories} />
5
5
  <Title />
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import Alert from './Alert';
3
3
  import { theme } from '../../theme';
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 { css } from '@emotion/css';
3
3
 
4
4
  import AppHeader from './AppHeader';
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { Link as InternalLink } from 'wouter';
3
3
  import AppMenu from './AppMenu';
4
4
  import Icon from '../Icon/Icon';
@@ -1,5 +1,5 @@
1
1
  import * as AvatarStories from "./Avatar.stories";
2
- import { Meta, Title, Subtitle, Canvas, Controls, ArgTypes } from "@storybook/blocks";
2
+ import { Meta, Title, Subtitle, Canvas, Controls, ArgTypes } from "@storybook/addon-docs/blocks";
3
3
 
4
4
  export const usage = {
5
5
  image: `<Avatar
@@ -1,5 +1,5 @@
1
1
  import type { CSSProperties } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
3
  import Avatar, { type AvatarProps } from './Avatar';
4
4
  import sampleAvatarPhoto from '../../../public/sample-avatar-photo.jpg';
5
5
 
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { css } from '@emotion/css';
3
3
  import Badge from './Badge';
4
4
  import { type BadgeVariant } from './Badge';
@@ -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
 
4
4
  import BaseCheckbox from './BaseCheckbox';
5
5
  import Icon from '../Icon';
@@ -1,5 +1,5 @@
1
1
  import { CSSProperties } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
3
  import Blanket from './Blanket';
4
4
 
5
5
  const meta = {
@@ -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 Breadcrumbs from './Breadcrumbs';
4
4
 
@@ -7,7 +7,7 @@ import {
7
7
  Canvas,
8
8
  Controls,
9
9
  ArgTypes,
10
- } from '@storybook/blocks';
10
+ } from '@storybook/addon-docs/blocks';
11
11
 
12
12
  export const usage = {
13
13
  default: `<Button>Default Button</Button>`,
@@ -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 Button from './Button';
4
4
  import Icon from '../Icon';
5
5
  import { theme } from '../../theme';
@@ -1,6 +1,6 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import Calendar from './Calendar';
3
- import { useArgs } from '@storybook/preview-api';
3
+ import { useArgs } from 'storybook/preview-api';
4
4
  import type { AcademicWeek } from './Calendar.types';
5
5
 
6
6
  const meta = {
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import Day from './Day';
3
3
 
4
4
  const meta = {
@@ -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
 
4
4
  import Checkbox from './Checkbox';
5
5
 
@@ -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
 
4
4
  import Chip from './Chip';
5
5
 
@@ -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 Datepicker from './Datepicker';
4
4
  import Field from '../Field';
5
5
  import Label from '../Label';
@@ -5,6 +5,7 @@ import React, {
5
5
  useEffect,
6
6
  useRef,
7
7
  useContext,
8
+ forwardRef,
8
9
  } from 'react';
9
10
  import { css, cx } from '@emotion/css';
10
11
  import useTheme from '../../theme/useTheme';
@@ -33,182 +34,200 @@ export interface BaseDialogProps extends HTMLAttributes<HTMLDialogElement> {
33
34
  testId?: string;
34
35
  }
35
36
 
36
- const BaseDialog = ({
37
- open = false,
38
- size = 'medium',
39
- modal = true,
40
- closeOnClickOutside = true,
41
- closeOnClickOutsideStopPropagation = true,
42
- nonModalCloseOnEscape = false,
43
- onClose,
44
- className,
45
- children,
46
- initialFocusRef,
47
- finalFocusRef,
48
- disableFocusTrap = false,
49
- restoreFocus = true,
50
- skipCloseOnInitialFocus = false,
51
- testId = NAME,
52
- ...props
53
- }: BaseDialogProps) => {
54
- const width = {
55
- small: SMALL_WIDTH,
56
- medium: MEDIUM_WIDTH,
57
- large: LARGE_WIDTH,
58
- }[size];
59
-
60
- const [theme] = useTheme();
61
-
62
- const dialogRef = useRef<HTMLDialogElement>(null);
63
- const previousActiveElement = useRef<HTMLElement | null>(null);
64
-
65
- const context = useContext(DialogContext);
66
- const dialogHeaderId = context?.dialogHeaderId;
67
- const dialogBodyId = context?.dialogBodyId;
68
-
69
- // Use the focus trap hook
70
- useFocusTrap({
71
- isActive: open && modal && !disableFocusTrap,
72
- containerRef: dialogRef,
73
- initialFocusRef,
74
- finalFocusRef,
75
- restoreFocus,
76
- skipFirstFocusable: skipCloseOnInitialFocus,
77
- });
78
-
79
- const hideBodyScroll = css`
80
- overflow: hidden;
81
- `;
82
-
83
- useEffect(() => {
84
- if (open && modal) {
85
- document.body.classList.add(hideBodyScroll);
86
- } else {
87
- document.body.classList.remove(hideBodyScroll);
88
- }
89
- return () => {
90
- document.body.classList.remove(hideBodyScroll);
91
- };
92
- }, [open, modal, hideBodyScroll]);
93
-
94
- useEffect(() => {
95
- const dialogElement = dialogRef.current;
96
-
97
- if (!dialogElement) return;
37
+ const BaseDialog = forwardRef<HTMLDialogElement, BaseDialogProps>(
38
+ function BaseDialog(
39
+ {
40
+ open = false,
41
+ size = 'medium',
42
+ modal = true,
43
+ closeOnClickOutside = true,
44
+ closeOnClickOutsideStopPropagation = true,
45
+ nonModalCloseOnEscape = false,
46
+ onClose,
47
+ className,
48
+ children,
49
+ initialFocusRef,
50
+ finalFocusRef,
51
+ disableFocusTrap = false,
52
+ restoreFocus = true,
53
+ skipCloseOnInitialFocus = false,
54
+ testId = NAME,
55
+ ...props
56
+ }: BaseDialogProps,
57
+ ref
58
+ ) {
59
+ const width = {
60
+ small: SMALL_WIDTH,
61
+ medium: MEDIUM_WIDTH,
62
+ large: LARGE_WIDTH,
63
+ }[size];
64
+
65
+ const [theme] = useTheme();
66
+
67
+ const dialogRef = useRef<HTMLDialogElement>(null);
68
+ const previousActiveElement = useRef<HTMLElement | null>(null);
69
+
70
+ const setDialogRef = useCallback(
71
+ (node: HTMLDialogElement | null) => {
72
+ dialogRef.current = node;
73
+
74
+ if (typeof ref === 'function') {
75
+ ref(node);
76
+ } else if (ref) {
77
+ ref.current = node;
78
+ }
79
+ },
80
+ [ref]
81
+ );
98
82
 
99
- if (open && !dialogElement.hasAttribute('open')) {
100
- previousActiveElement.current = document.activeElement as HTMLElement;
101
- if (modal) {
102
- dialogElement.showModal();
83
+ const context = useContext(DialogContext);
84
+ const dialogHeaderId = context?.dialogHeaderId;
85
+ const dialogBodyId = context?.dialogBodyId;
86
+
87
+ // Use the focus trap hook
88
+ useFocusTrap({
89
+ isActive: open && modal && !disableFocusTrap,
90
+ containerRef: dialogRef,
91
+ initialFocusRef,
92
+ finalFocusRef,
93
+ restoreFocus,
94
+ skipFirstFocusable: skipCloseOnInitialFocus,
95
+ });
96
+
97
+ const hideBodyScroll = css`
98
+ overflow: hidden;
99
+ `;
100
+
101
+ useEffect(() => {
102
+ if (open && modal) {
103
+ document.body.classList.add(hideBodyScroll);
103
104
  } else {
104
- dialogElement.show();
105
- }
106
- } else if (!open && dialogElement.hasAttribute('open')) {
107
- dialogElement.close();
108
- // Focus restoration is handled by the focus trap hook for modal dialogs,
109
- // but we keep the fallback for non-modal dialogs or when focus trap is disabled
110
- if ((!modal || disableFocusTrap) && restoreFocus) {
111
- previousActiveElement.current?.focus();
105
+ document.body.classList.remove(hideBodyScroll);
112
106
  }
113
- }
114
- }, [open, modal, disableFocusTrap, restoreFocus]);
115
-
116
- // Handle Escape key to close dialog
117
- useEffect(() => {
118
- if (!open || modal || !nonModalCloseOnEscape) return;
119
-
120
- const handleKeyDown = (event: KeyboardEvent) => {
121
- if (event.key === 'Escape' && onClose) {
122
- onClose(event);
107
+ return () => {
108
+ document.body.classList.remove(hideBodyScroll);
109
+ };
110
+ }, [open, modal, hideBodyScroll]);
111
+
112
+ useEffect(() => {
113
+ const dialogElement = dialogRef.current;
114
+
115
+ if (!dialogElement) return;
116
+
117
+ if (open && !dialogElement.hasAttribute('open')) {
118
+ previousActiveElement.current = document.activeElement as HTMLElement;
119
+ if (modal) {
120
+ dialogElement.showModal();
121
+ } else {
122
+ dialogElement.show();
123
+ }
124
+ } else if (!open && dialogElement.hasAttribute('open')) {
125
+ dialogElement.close();
126
+ // Focus restoration is handled by the focus trap hook for modal dialogs,
127
+ // but we keep the fallback for non-modal dialogs or when focus trap is disabled
128
+ if ((!modal || disableFocusTrap) && restoreFocus) {
129
+ previousActiveElement.current?.focus();
130
+ }
123
131
  }
124
- };
132
+ }, [open, modal, disableFocusTrap, restoreFocus]);
125
133
 
126
- document.addEventListener('keydown', handleKeyDown);
127
- return () => document.removeEventListener('keydown', handleKeyDown);
128
- }, [open, modal, nonModalCloseOnEscape, onClose]);
134
+ // Handle Escape key to close dialog
135
+ useEffect(() => {
136
+ if (!open || modal || !nonModalCloseOnEscape) return;
129
137
 
130
- const handleClick = useCallback(
131
- (ev: React.MouseEvent<HTMLDialogElement>) => {
132
- if (closeOnClickOutside && onClose && dialogRef.current) {
133
- if (closeOnClickOutsideStopPropagation) {
134
- ev.stopPropagation();
138
+ const handleKeyDown = (event: KeyboardEvent) => {
139
+ if (event.key === 'Escape' && onClose) {
140
+ onClose(event);
141
+ }
142
+ };
143
+
144
+ document.addEventListener('keydown', handleKeyDown);
145
+ return () => document.removeEventListener('keydown', handleKeyDown);
146
+ }, [open, modal, nonModalCloseOnEscape, onClose]);
147
+
148
+ const handleClick = useCallback(
149
+ (ev: React.MouseEvent<HTMLDialogElement>) => {
150
+ if (closeOnClickOutside && onClose && dialogRef.current) {
151
+ if (closeOnClickOutsideStopPropagation) {
152
+ ev.stopPropagation();
153
+ }
154
+ const rect = dialogRef.current.getBoundingClientRect();
155
+ const isInDialog =
156
+ rect.top <= ev.clientY &&
157
+ ev.clientY <= rect.top + rect.height &&
158
+ rect.left <= ev.clientX &&
159
+ ev.clientX <= rect.left + rect.width;
160
+ if (!isInDialog) {
161
+ onClose(ev);
162
+ }
135
163
  }
136
- const rect = dialogRef.current.getBoundingClientRect();
137
- const isInDialog =
138
- rect.top <= ev.clientY &&
139
- ev.clientY <= rect.top + rect.height &&
140
- rect.left <= ev.clientX &&
141
- ev.clientX <= rect.left + rect.width;
142
- if (!isInDialog) {
164
+ },
165
+ [closeOnClickOutside, closeOnClickOutsideStopPropagation, onClose]
166
+ );
167
+
168
+ const handleDialogClose = useCallback(
169
+ (ev: React.MouseEvent<HTMLDialogElement>) => {
170
+ if (onClose) {
143
171
  onClose(ev);
144
172
  }
145
- }
146
- },
147
- [closeOnClickOutside, closeOnClickOutsideStopPropagation, onClose]
148
- );
149
-
150
- const handleDialogClose = useCallback(
151
- (ev: React.MouseEvent<HTMLDialogElement>) => {
152
- if (onClose) {
153
- onClose(ev);
154
- }
155
- },
156
- [onClose]
157
- );
158
-
159
- const baseStyle = css`
160
- padding: 0;
161
- border: none;
162
- background: ${theme.color.neutral.white};
163
- color: ${theme.color.text.primary};
164
- font-family: ${theme.font.family.primary};
165
- font-size: ${theme.font.size.f16};
166
- width: 100vw;
167
- height: 100vh;
168
- margin: auto;
169
-
170
- &:modal {
171
- max-width: none;
172
- max-height: none;
173
- }
173
+ },
174
+ [onClose]
175
+ );
174
176
 
175
- @media (min-width: ${theme.breakpoints.tablet}px) {
176
- width: ${width}px;
177
- max-width: calc(100vw - ${theme.margin.m16});
178
- height: fit-content;
177
+ const baseStyle = css`
178
+ padding: 0;
179
+ border: none;
180
+ background: ${theme.color.neutral.white};
181
+ color: ${theme.color.text.primary};
182
+ font-family: ${theme.font.family.primary};
183
+ font-size: ${theme.font.size.f16};
184
+ width: 100vw;
185
+ height: 100vh;
186
+ margin: auto;
179
187
 
180
188
  &:modal {
181
- max-width: min(${width}px, calc(100vw - ${theme.margin.m16}));
182
- max-height: calc(100vh - 32px);
189
+ max-width: none;
190
+ max-height: none;
183
191
  }
184
- }
185
192
 
186
- &::backdrop {
187
- background-color: ${theme.color.overlay.blanket};
193
+ @media (min-width: ${theme.breakpoints.tablet}px) {
194
+ width: ${width}px;
195
+ max-width: calc(100vw - ${theme.margin.m16});
196
+ height: fit-content;
197
+
198
+ &:modal {
199
+ max-width: min(${width}px, calc(100vw - ${theme.margin.m16}));
200
+ max-height: calc(100vh - 32px);
201
+ }
202
+ }
203
+
204
+ &::backdrop {
205
+ background-color: ${theme.color.overlay.blanket};
206
+ }
207
+ `;
208
+
209
+ const style = cx(NAME, baseStyle, className);
210
+
211
+ if (open) {
212
+ return (
213
+ <dialog
214
+ ref={setDialogRef}
215
+ className={style}
216
+ data-testid={testId}
217
+ onClick={handleClick}
218
+ onClose={handleDialogClose}
219
+ aria-modal={modal ? 'true' : 'false'}
220
+ aria-labelledby={dialogHeaderId}
221
+ aria-describedby={dialogBodyId}
222
+ {...props}
223
+ >
224
+ {children}
225
+ </dialog>
226
+ );
227
+ } else {
228
+ return null;
188
229
  }
189
- `;
190
-
191
- const style = cx(NAME, baseStyle, className);
192
-
193
- if (open) {
194
- return (
195
- <dialog
196
- ref={dialogRef}
197
- className={style}
198
- data-testid={testId}
199
- onClick={handleClick}
200
- onClose={handleDialogClose}
201
- aria-modal={modal ? 'true' : 'false'}
202
- aria-labelledby={dialogHeaderId}
203
- aria-describedby={dialogBodyId}
204
- {...props}
205
- >
206
- {children}
207
- </dialog>
208
- );
209
- } else {
210
- return null;
211
230
  }
212
- };
231
+ );
213
232
 
214
233
  export default memo(BaseDialog);
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import Dialog from './Dialog';
3
3
 
4
4
  const meta = {
@@ -1,4 +1,4 @@
1
- import { createContext, useId } from 'react';
1
+ import { createContext, forwardRef, useId } from 'react';
2
2
  import { css, cx } from '@emotion/css';
3
3
  import useTheme from '../../theme/useTheme';
4
4
  import BaseDialog, { BaseDialogProps } from './BaseDialog';
@@ -23,15 +23,18 @@ export interface DialogProps extends BaseDialogProps {
23
23
  onSecondaryAction?: () => void;
24
24
  }
25
25
 
26
- const Dialog = ({
27
- onClose,
28
- onAction,
29
- onSecondaryAction,
30
- testId = NAME,
31
- className,
32
- children,
33
- ...props
34
- }: DialogProps) => {
26
+ const Dialog = forwardRef<HTMLDialogElement, DialogProps>(function Dialog(
27
+ {
28
+ onClose,
29
+ onAction,
30
+ onSecondaryAction,
31
+ testId = NAME,
32
+ className,
33
+ children,
34
+ ...props
35
+ }: DialogProps,
36
+ ref
37
+ ) {
35
38
  const [theme] = useTheme();
36
39
  const dialogHeaderId = useId();
37
40
  const dialogBodyId = useId();
@@ -53,6 +56,7 @@ const Dialog = ({
53
56
  return (
54
57
  <DialogContext value={contextValue}>
55
58
  <BaseDialog
59
+ ref={ref}
56
60
  onClose={onClose}
57
61
  testId={testId}
58
62
  className={style}
@@ -63,7 +67,7 @@ const Dialog = ({
63
67
  </BaseDialog>
64
68
  </DialogContext>
65
69
  );
66
- };
70
+ });
67
71
 
68
72
  export interface DialogSubComponents {
69
73
  Header: typeof DialogHeader;
@@ -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 Divider from './Divider';
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 { css } from '@emotion/css';
3
3
  import Dropdown from './Dropdown';
4
4
  import Button from '../Button';
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import FeedbackDialog from './FeedbackDialog';
3
3
 
4
4
  const meta = {
@@ -1,6 +1,6 @@
1
1
  import { useState } from 'react';
2
- import type { Meta, StoryObj } from '@storybook/react';
3
- import { useArgs } from '@storybook/preview-api';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { useArgs } from 'storybook/preview-api';
4
4
  import { css } from '@emotion/css';
5
5
  import Field from './Field';
6
6
  // Direct imports to avoid circular dependency warning at build time due to context
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import FileInput from './FileInput';
3
3
 
4
4
  const meta = {
@@ -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 Footer from './Footer';
4
4
 
@@ -1,5 +1,5 @@
1
1
  import * as HeaderStories from "./Header.stories";
2
- import { Meta, Title, Subtitle, Canvas, Controls } from "@storybook/blocks";
2
+ import { Meta, Title, Subtitle, Canvas, Controls } from "@storybook/addon-docs/blocks";
3
3
 
4
4
  export const usage = {
5
5
  default: `<Header title='App Name' />`,
@@ -1,4 +1,4 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import Header from './Header';
3
3
  import Menu from '../Menu';
4
4
  import Icon from '../Icon';