@kaizen/components 0.0.0-canary-v2-20250901045936 → 0.0.0-canary-alpha-release-20250918043833
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.
- package/alpha/README.md +28 -0
- package/alpha/package.json +5 -0
- package/dist/cjs/alpha.cjs +1 -0
- package/dist/cjs/src/Modal/GenericModal/GenericModal.cjs +33 -65
- package/dist/cjs/src/Modal/GenericModal/GenericModal.module.scss.cjs +1 -3
- package/dist/cjs/src/Notification/InlineNotification/InlineNotification.cjs +1 -1
- package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +35 -74
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.cjs +105 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css.cjs +11 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.cjs +112 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css.cjs +16 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/List/List.cjs +35 -10
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.cjs +61 -8
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css.cjs +10 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.cjs +38 -9
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css.cjs +4 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +60 -30
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.cjs +2 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +2 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +4 -2
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Select/Select.cjs +87 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css.cjs +11 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.cjs +52 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css.cjs +13 -0
- package/dist/esm/alpha.mjs +1 -1
- package/dist/esm/src/Modal/GenericModal/GenericModal.mjs +34 -65
- package/dist/esm/src/Modal/GenericModal/GenericModal.module.scss.mjs +1 -3
- package/dist/esm/src/Notification/InlineNotification/InlineNotification.mjs +1 -1
- package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +39 -73
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.mjs +96 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css.mjs +9 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.mjs +103 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css.mjs +14 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/List/List.mjs +37 -14
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.mjs +63 -13
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css.mjs +10 -1
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.mjs +41 -15
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css.mjs +4 -1
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +69 -43
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +2 -1
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +2 -1
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +4 -2
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Select/Select.mjs +78 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css.mjs +9 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.mjs +43 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css.mjs +11 -0
- package/dist/styles.css +385 -21
- package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +14 -19
- package/dist/types/__alpha__/SingleSelect/_docs/mockData.d.ts +3 -0
- package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +15 -7
- package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBox/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -7
- package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -7
- package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -9
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +3 -6
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Select/Select.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Select/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/SelectTrigger/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +4 -1
- package/dist/types/__alpha__/SingleSelect/types.d.ts +68 -11
- package/locales/en.json +9 -1
- package/package.json +10 -3
- package/src/Modal/GenericModal/GenericModal.spec.tsx +1 -1
- package/src/Modal/GenericModal/GenericModal.tsx +38 -70
- package/src/Notification/InlineNotification/InlineNotification.tsx +1 -1
- package/src/RichTextEditor/RichTextEditor/RichTextEditor.spec.stories.tsx +10 -3
- package/src/__alpha__/SingleSelect/SingleSelect.tsx +35 -88
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +96 -6
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +22 -24
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +389 -33
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +41 -22
- package/src/__alpha__/SingleSelect/_docs/mockData.ts +20 -14
- package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +18 -7
- package/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css +35 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.tsx +106 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBox/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css +130 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.tsx +121 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +5 -0
- package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +36 -13
- package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +84 -3
- package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +67 -11
- package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css +20 -5
- package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +46 -19
- package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +7 -5
- package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +90 -37
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +2 -2
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +9 -8
- package/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css +35 -0
- package/src/__alpha__/SingleSelect/subcomponents/Select/Select.tsx +84 -0
- package/src/__alpha__/SingleSelect/subcomponents/Select/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css +77 -0
- package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.tsx +52 -0
- package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/index.ts +4 -1
- package/src/__alpha__/SingleSelect/types.ts +94 -14
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +0 -57
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css.cjs +0 -6
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +0 -49
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css.mjs +0 -4
- package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +0 -2
- package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/index.d.ts +0 -1
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +0 -19
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +0 -35
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/index.ts +0 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export * from './List';
|
|
2
2
|
export * from './ListSection';
|
|
3
3
|
export * from './ListItem';
|
|
4
|
-
export * from './
|
|
4
|
+
export * from './SelectTrigger';
|
|
5
|
+
export * from './ComboBoxTrigger';
|
|
5
6
|
export * from './Popover';
|
|
7
|
+
export * from './Select';
|
|
8
|
+
export * from './ComboBox';
|
|
@@ -1,25 +1,66 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { DOMAttributes, RefObject } from 'react';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
import { type ComboBoxState, type ComboBoxStateOptions } from '@react-stately/combobox';
|
|
4
|
+
import type { ListState } from '@react-stately/list';
|
|
5
|
+
import { type SelectState, type SelectStateOptions } from '@react-stately/select';
|
|
6
|
+
import { type Key, type Node } from '@react-types/shared';
|
|
7
|
+
import { type FocusableElement } from '@react-types/shared/src/dom';
|
|
8
|
+
import type { AriaButtonProps, AriaListBoxOptions, AriaPopoverProps } from 'react-aria';
|
|
3
9
|
export type SelectItem = {
|
|
4
10
|
label: string;
|
|
5
11
|
value: string;
|
|
12
|
+
key: Key;
|
|
6
13
|
};
|
|
7
14
|
export type SelectSection = {
|
|
8
15
|
label: string;
|
|
9
16
|
options: SelectItem[];
|
|
10
17
|
};
|
|
11
|
-
export type
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
export type SelectLabel = {
|
|
19
|
+
labelHidden: true;
|
|
20
|
+
label: string;
|
|
21
|
+
} | {
|
|
22
|
+
labelHidden?: false;
|
|
23
|
+
label: React.ReactNode;
|
|
24
|
+
};
|
|
25
|
+
export type SelectBaseProps = {
|
|
26
|
+
variant?: 'primary' | 'secondary';
|
|
27
|
+
size?: 'small' | 'medium' | 'large';
|
|
28
|
+
labelPosition?: 'top' | 'side';
|
|
29
|
+
isReadOnly?: boolean;
|
|
30
|
+
} & SelectLabel;
|
|
31
|
+
export type SelectProps<T extends SelectItem> = Omit<SelectStateOptions<T>, 'label' | 'defaultFilter' | 'menuTrigger' | 'allowsCustomValue'> & SelectBaseProps;
|
|
32
|
+
export type ComboBoxProps<T extends SelectItem> = Omit<ComboBoxStateOptions<T>, 'label' | 'defaultFilter' | 'menuTrigger' | 'allowsCustomValue'> & SelectBaseProps;
|
|
33
|
+
export type SingleSelectProps<T extends SelectItem> = (ComboBoxProps<T> & {
|
|
34
|
+
isComboBox?: true;
|
|
35
|
+
}) | (SelectProps<T> & {
|
|
36
|
+
isComboBox?: false;
|
|
37
|
+
});
|
|
38
|
+
export type SelectTriggerProps = {
|
|
39
|
+
triggerProps: AriaButtonProps<'button'>;
|
|
40
|
+
valueProps: DOMAttributes<FocusableElement>;
|
|
41
|
+
buttonRef: React.MutableRefObject<HTMLButtonElement | null>;
|
|
42
|
+
};
|
|
43
|
+
export type ComboBoxTriggerProps = {
|
|
44
|
+
inputProps: React.InputHTMLAttributes<HTMLInputElement>;
|
|
45
|
+
inputRef: React.MutableRefObject<HTMLInputElement | null>;
|
|
46
|
+
buttonProps: AriaButtonProps<'button'>;
|
|
47
|
+
buttonRef: React.MutableRefObject<HTMLButtonElement | null>;
|
|
48
|
+
triggerWrapperRef: React.MutableRefObject<HTMLDivElement | null>;
|
|
49
|
+
clearButtonRef: React.MutableRefObject<HTMLButtonElement | null>;
|
|
50
|
+
};
|
|
51
|
+
export type ChevronButtonProps = AriaButtonProps<'button'> & {
|
|
52
|
+
buttonRef: React.MutableRefObject<HTMLButtonElement | null>;
|
|
15
53
|
};
|
|
16
|
-
export type
|
|
17
|
-
|
|
54
|
+
export type ClearButtonProps = {
|
|
55
|
+
clearButtonRef: React.RefObject<HTMLButtonElement>;
|
|
56
|
+
inputRef: React.RefObject<HTMLInputElement>;
|
|
18
57
|
};
|
|
19
|
-
export type PopoverProps = {
|
|
20
|
-
|
|
58
|
+
export type PopoverProps<T extends SelectItem> = AriaPopoverProps & {
|
|
59
|
+
state: ComboBoxState<T> | SelectState<T>;
|
|
60
|
+
triggerRef: React.RefObject<HTMLElement>;
|
|
21
61
|
popoverRef: React.RefObject<HTMLDivElement>;
|
|
22
|
-
|
|
62
|
+
clearButtonRef?: React.RefObject<HTMLButtonElement>;
|
|
63
|
+
children: React.ReactNode;
|
|
23
64
|
};
|
|
24
65
|
type PositionDataProp = number | string | undefined;
|
|
25
66
|
export type PositionData = {
|
|
@@ -42,4 +83,20 @@ export type UsePopoverPositioningProps = {
|
|
|
42
83
|
offset?: number;
|
|
43
84
|
preferredPlacement?: 'top' | 'bottom';
|
|
44
85
|
};
|
|
86
|
+
export type ListProps<T extends SelectItem> = {
|
|
87
|
+
state: ComboBoxState<T> | SelectState<T>;
|
|
88
|
+
listBoxOptions: AriaListBoxOptions<T>;
|
|
89
|
+
listBoxRef: React.RefObject<HTMLUListElement>;
|
|
90
|
+
};
|
|
91
|
+
export type ListItemProps<T extends SelectItem> = {
|
|
92
|
+
item: Node<T>;
|
|
93
|
+
state: ListState<T>;
|
|
94
|
+
selectedIcon?: 'check' | 'radio';
|
|
95
|
+
selectedPosition?: 'start' | 'end';
|
|
96
|
+
className?: string;
|
|
97
|
+
};
|
|
98
|
+
export type ListSectionProps<T extends SelectItem> = {
|
|
99
|
+
section: Node<T>;
|
|
100
|
+
state: ComboBoxState<T> | SelectState<T>;
|
|
101
|
+
};
|
|
45
102
|
export {};
|
package/locales/en.json
CHANGED
|
@@ -185,8 +185,16 @@
|
|
|
185
185
|
"description": "Prompts user to interact with button to hide information",
|
|
186
186
|
"message": "Hide information:"
|
|
187
187
|
},
|
|
188
|
+
"singleSelect.chevronButton": {
|
|
189
|
+
"description": "Aria label text for the SingleSelect button to open and close suggestions list",
|
|
190
|
+
"message": "Show suggestions for {field}"
|
|
191
|
+
},
|
|
192
|
+
"singleSelect.clearButtonAlt": {
|
|
193
|
+
"description": "Alt text for the clear selection button",
|
|
194
|
+
"message": "Clear {field} selection"
|
|
195
|
+
},
|
|
188
196
|
"splitButton.dropdownButton.label": {
|
|
189
197
|
"description": "Label for a dropdown menu holding additional actions",
|
|
190
198
|
"message": "Additional actions"
|
|
191
199
|
}
|
|
192
|
-
}
|
|
200
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaizen/components",
|
|
3
|
-
"version": "0.0.0-canary-
|
|
3
|
+
"version": "0.0.0-canary-alpha-release-20250918043833",
|
|
4
4
|
"description": "Kaizen component library",
|
|
5
5
|
"author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
|
|
6
6
|
"homepage": "https://cultureamp.design",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"bin",
|
|
17
17
|
"codemods",
|
|
18
|
+
"alpha",
|
|
18
19
|
"future",
|
|
19
20
|
"next",
|
|
20
21
|
"libs",
|
|
@@ -36,6 +37,11 @@
|
|
|
36
37
|
"import": "./dist/esm/index.mjs",
|
|
37
38
|
"require": "./dist/cjs/index.cjs"
|
|
38
39
|
},
|
|
40
|
+
"./alpha": {
|
|
41
|
+
"types": "./dist/types/__alpha__/index.d.ts",
|
|
42
|
+
"import": "./dist/esm/alpha.mjs",
|
|
43
|
+
"require": "./dist/cjs/alpha.cjs"
|
|
44
|
+
},
|
|
39
45
|
"./next": {
|
|
40
46
|
"types": "./dist/types/__next__/index.d.ts",
|
|
41
47
|
"import": "./dist/esm/next.mjs",
|
|
@@ -86,6 +92,7 @@
|
|
|
86
92
|
"@react-aria/select": "^3.15.7",
|
|
87
93
|
"@react-aria/utils": "^3.29.1",
|
|
88
94
|
"@react-stately/collections": "^3.12.5",
|
|
95
|
+
"@react-stately/combobox": "^3.11.1",
|
|
89
96
|
"@react-stately/datepicker": "^3.14.2",
|
|
90
97
|
"@react-stately/list": "^3.12.3",
|
|
91
98
|
"@react-stately/menu": "^3.9.5",
|
|
@@ -152,10 +159,10 @@
|
|
|
152
159
|
"sass": "1.79.6",
|
|
153
160
|
"serialize-query-params": "^2.0.2",
|
|
154
161
|
"svgo": "^3.3.2",
|
|
155
|
-
"tslib": "^2.8.1",
|
|
156
162
|
"ts-patch": "^3.3.0",
|
|
163
|
+
"tslib": "^2.8.1",
|
|
157
164
|
"tsx": "^4.20.3",
|
|
158
|
-
"@kaizen/design-tokens": "
|
|
165
|
+
"@kaizen/design-tokens": "11.0.0"
|
|
159
166
|
},
|
|
160
167
|
"devDependenciesComments": {
|
|
161
168
|
"sass": "Prevent deprecation warnings introduced in 1.80 as we plan to move away from sass",
|
|
@@ -71,7 +71,7 @@ describe('<GenericModal />', () => {
|
|
|
71
71
|
|
|
72
72
|
it('closes the modal when a click is outside of the modal content', async () => {
|
|
73
73
|
const handleDismiss = vi.fn()
|
|
74
|
-
render(<GenericModalWrapper onOutsideModalClick={handleDismiss} />)
|
|
74
|
+
render(<GenericModalWrapper onOutsideModalClick={handleDismiss()} />)
|
|
75
75
|
|
|
76
76
|
await user.click(screen.getByTestId('GenericModalTestId-scrollLayer'))
|
|
77
77
|
await waitFor(() => {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useId, useRef } from 'react'
|
|
2
2
|
import { createPortal } from 'react-dom'
|
|
3
3
|
import { Transition } from '@headlessui/react'
|
|
4
4
|
import classnames from 'classnames'
|
|
5
|
-
import
|
|
5
|
+
import { FocusOn } from 'react-focus-on'
|
|
6
6
|
import { useIsClientReady } from '../../utils/useIsClientReady'
|
|
7
|
+
|
|
7
8
|
import { warn } from '../util/console'
|
|
8
9
|
import { ModalContext } from './context/ModalContext'
|
|
9
10
|
import styles from './GenericModal.module.scss'
|
|
@@ -38,39 +39,22 @@ export const GenericModal = ({
|
|
|
38
39
|
|
|
39
40
|
const labelledByID = useId()
|
|
40
41
|
const describedByID = useId()
|
|
41
|
-
|
|
42
42
|
const isClientReady = useIsClientReady()
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
const
|
|
44
|
+
const scrollLayerRef = useRef<HTMLDivElement | null>(null)
|
|
45
|
+
const modalLayerRef = useRef<HTMLDivElement | null>(null)
|
|
46
46
|
|
|
47
47
|
const scrollModalToTop = (): void => {
|
|
48
48
|
// If we have a really long modal, the autofocus could land on an element down below
|
|
49
49
|
// causing the modal to scroll down and skipping over the content near the modal's top.
|
|
50
50
|
// Ensure that when the modal opens, we are at the top of its content.
|
|
51
51
|
requestAnimationFrame(() => {
|
|
52
|
-
|
|
53
|
-
scrollLayer.scrollTop = 0
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const outsideModalClickHandler = (event: React.MouseEvent): void => {
|
|
58
|
-
if (event.target === scrollLayer || event.target === modalLayer) {
|
|
59
|
-
onOutsideModalClick?.(event)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const focusOnAccessibleLabel = (): void => {
|
|
64
|
-
if (!isClientReady) return
|
|
65
|
-
|
|
66
|
-
// Check if focus already exists within the modal
|
|
67
|
-
if (modalLayer?.contains(document.activeElement)) {
|
|
68
|
-
return
|
|
69
|
-
}
|
|
52
|
+
const scrollElement = scrollLayerRef.current
|
|
70
53
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
54
|
+
// This little verbose of a check but this ensures that the element is attached to the DOM as it animates in. This additional check aims to avoid race conditions
|
|
55
|
+
if (!scrollElement?.isConnected) return
|
|
56
|
+
scrollElement.scrollTop = 0
|
|
57
|
+
})
|
|
74
58
|
}
|
|
75
59
|
|
|
76
60
|
const a11yWarn = (): void => {
|
|
@@ -86,60 +70,46 @@ export const GenericModal = ({
|
|
|
86
70
|
}
|
|
87
71
|
}
|
|
88
72
|
|
|
89
|
-
const
|
|
73
|
+
const focusOnAccessibleLabel = (): void => {
|
|
90
74
|
if (!isClientReady) return
|
|
75
|
+
const modalElement = modalLayerRef.current
|
|
76
|
+
if (!modalElement?.isConnected) return
|
|
91
77
|
|
|
92
|
-
|
|
93
|
-
|
|
78
|
+
// Check if focus already exists within the modal
|
|
79
|
+
if (modalElement.contains(document.activeElement)) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const labelElement: HTMLElement | null = document.getElementById(labelledByID)
|
|
94
84
|
|
|
95
|
-
if (
|
|
96
|
-
|
|
85
|
+
if (labelElement?.isConnected) {
|
|
86
|
+
labelElement.focus()
|
|
97
87
|
}
|
|
88
|
+
}
|
|
98
89
|
|
|
99
|
-
|
|
90
|
+
const onEscapeKeyHandler = (e: Event): void => {
|
|
91
|
+
if (e instanceof KeyboardEvent) {
|
|
92
|
+
onEscapeKeyup?.(e)
|
|
93
|
+
}
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
const onAfterEnterHandler = (): void => {
|
|
103
97
|
scrollModalToTop()
|
|
104
|
-
|
|
98
|
+
const modalElement = modalLayerRef.current
|
|
99
|
+
if (modalElement) {
|
|
105
100
|
onAfterEnter?.()
|
|
106
101
|
focusOnAccessibleLabel()
|
|
107
102
|
a11yWarn()
|
|
108
103
|
}
|
|
109
104
|
}
|
|
110
105
|
|
|
111
|
-
const
|
|
112
|
-
(
|
|
113
|
-
|
|
114
|
-
onEscapeKeyup?.(event)
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
[onEscapeKeyup],
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
const onBeforeEnterHandler = (): void => {
|
|
121
|
-
preventBodyScroll()
|
|
122
|
-
|
|
123
|
-
if (onEscapeKeyup && isClientReady) {
|
|
124
|
-
document.addEventListener('keyup', escapeKeyHandler)
|
|
106
|
+
const outsideModalClickHandler = (e: React.MouseEvent): void => {
|
|
107
|
+
if (e.target === scrollLayerRef.current || e.target === modalLayerRef.current) {
|
|
108
|
+
onOutsideModalClick?.(e)
|
|
125
109
|
}
|
|
126
110
|
}
|
|
127
111
|
|
|
128
|
-
const cleanUpAfterClose = useCallback(() => {
|
|
129
|
-
if (!isClientReady) return
|
|
130
|
-
|
|
131
|
-
document.documentElement.classList.remove(styles.unscrollable, styles.pseudoScrollbar)
|
|
132
|
-
|
|
133
|
-
if (onEscapeKeyup) {
|
|
134
|
-
document.removeEventListener('keyup', escapeKeyHandler)
|
|
135
|
-
}
|
|
136
|
-
}, [escapeKeyHandler, onEscapeKeyup, isClientReady])
|
|
137
|
-
|
|
138
|
-
/* Ensure sure add-on styles (e.g. unscrollable) and key event is cleaned up when the modal is unmounted*/
|
|
139
|
-
useEffect(() => () => cleanUpAfterClose(), [cleanUpAfterClose])
|
|
140
|
-
|
|
141
112
|
const onAfterLeaveHandler = (): void => {
|
|
142
|
-
cleanUpAfterClose()
|
|
143
113
|
propsOnAfterLeave?.()
|
|
144
114
|
}
|
|
145
115
|
|
|
@@ -152,7 +122,6 @@ export const GenericModal = ({
|
|
|
152
122
|
<Transition
|
|
153
123
|
appear={true}
|
|
154
124
|
show={isOpen}
|
|
155
|
-
beforeEnter={onBeforeEnterHandler}
|
|
156
125
|
afterEnter={onAfterEnterHandler}
|
|
157
126
|
afterLeave={onAfterLeaveHandler}
|
|
158
127
|
data-generic-modal-transition-wrapper
|
|
@@ -161,9 +130,10 @@ export const GenericModal = ({
|
|
|
161
130
|
as="div"
|
|
162
131
|
className={classnames(styles.transitionLayer, className)}
|
|
163
132
|
>
|
|
164
|
-
<
|
|
165
|
-
|
|
133
|
+
<FocusOn
|
|
134
|
+
focusLock={focusLockDisabled}
|
|
166
135
|
returnFocus={true}
|
|
136
|
+
onEscapeKey={onEscapeKeyHandler}
|
|
167
137
|
// Disabling false positive
|
|
168
138
|
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
169
139
|
autoFocus={false}
|
|
@@ -174,11 +144,9 @@ export const GenericModal = ({
|
|
|
174
144
|
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
|
|
175
145
|
<div
|
|
176
146
|
className={styles.scrollLayer}
|
|
177
|
-
ref={
|
|
178
|
-
setScrollLayer(scrollLayerRef)
|
|
179
|
-
}}
|
|
180
|
-
onClick={outsideModalClickHandler}
|
|
147
|
+
ref={scrollLayerRef}
|
|
181
148
|
data-testid={`${id}-scrollLayer`}
|
|
149
|
+
onClick={outsideModalClickHandler}
|
|
182
150
|
>
|
|
183
151
|
<ModalContext.Provider
|
|
184
152
|
value={{
|
|
@@ -191,14 +159,14 @@ export const GenericModal = ({
|
|
|
191
159
|
className={styles.modalLayer}
|
|
192
160
|
aria-labelledby={labelledByID}
|
|
193
161
|
aria-describedby={describedByID}
|
|
194
|
-
ref={
|
|
162
|
+
ref={modalLayerRef}
|
|
195
163
|
data-testid={id}
|
|
196
164
|
>
|
|
197
165
|
{children}
|
|
198
166
|
</div>
|
|
199
167
|
</ModalContext.Provider>
|
|
200
168
|
</div>
|
|
201
|
-
</
|
|
169
|
+
</FocusOn>
|
|
202
170
|
</Transition>,
|
|
203
171
|
document.body,
|
|
204
172
|
)
|
|
@@ -35,7 +35,7 @@ export const InlineNotification = forwardRef<HTMLDivElement, InlineNotificationP
|
|
|
35
35
|
): JSX.Element => (
|
|
36
36
|
<GenericNotification
|
|
37
37
|
style="inline"
|
|
38
|
-
persistent={persistent
|
|
38
|
+
persistent={persistent || hideCloseIcon}
|
|
39
39
|
classNameOverride={classnames(classNameOverride, [isSubtle && styles.subtle])}
|
|
40
40
|
ref={ref}
|
|
41
41
|
{...otherProps}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { type StoryObj } from '@storybook/react'
|
|
3
|
-
import { expect, userEvent, within } from '@storybook/test'
|
|
3
|
+
import { expect, userEvent, waitFor, within } from '@storybook/test'
|
|
4
4
|
import { type EditorContentArray } from '../types'
|
|
5
5
|
import { RichTextEditor, type RichTextEditorProps } from './RichTextEditor'
|
|
6
6
|
|
|
@@ -149,8 +149,9 @@ export const CreateALink: Story = {
|
|
|
149
149
|
name: 'Create a link',
|
|
150
150
|
play: async (context) => {
|
|
151
151
|
const { canvasElement, step } = context
|
|
152
|
-
const { getByRole, getByText } = within(canvasElement)
|
|
152
|
+
const { getByRole, getByText, queryByRole, findByRole } = within(canvasElement)
|
|
153
153
|
const editor = getByRole('textbox')
|
|
154
|
+
|
|
154
155
|
await step('Focus on editor', async () => {
|
|
155
156
|
await userEvent.click(editor)
|
|
156
157
|
expect(editor).toHaveFocus()
|
|
@@ -185,8 +186,14 @@ export const CreateALink: Story = {
|
|
|
185
186
|
await userEvent.keyboard('{Tab}{Enter}')
|
|
186
187
|
})
|
|
187
188
|
|
|
189
|
+
await step('The Link Modal closes', async () => {
|
|
190
|
+
await waitFor(() => {
|
|
191
|
+
expect(queryByRole('dialog')).not.toBeInTheDocument()
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
188
195
|
await step('Link exists in the RTE', async () => {
|
|
189
|
-
const link =
|
|
196
|
+
const link = await findByRole('link', { name: 'Link' })
|
|
190
197
|
expect(link).toBeInTheDocument()
|
|
191
198
|
})
|
|
192
199
|
},
|
|
@@ -1,92 +1,39 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
children,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const state = useSelectState({
|
|
22
|
-
items,
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
const handleOnSelectionChange = React.useCallback(
|
|
26
|
-
(keys: Selection): void => {
|
|
27
|
-
let key: Key | null = null
|
|
28
|
-
|
|
29
|
-
if (keys instanceof Set && keys.size > 0) {
|
|
30
|
-
key = Array.from(keys)[0]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
state.setSelectedKey(key)
|
|
34
|
-
if (onSelectionChange) {
|
|
35
|
-
onSelectionChange(key)
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
[state, onSelectionChange],
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
// Cloning children here to allow users to pass in a custom ListItem or ListSection
|
|
42
|
-
// and still have the SingleSelect handle selection state
|
|
43
|
-
const injectedChildren = useMemo(() => {
|
|
44
|
-
if (!isValidElement(children)) return null
|
|
45
|
-
|
|
46
|
-
const selectedKeys: Iterable<Key> = state.selectedKey
|
|
47
|
-
? new Set<Key>([state.selectedKey])
|
|
48
|
-
: new Set()
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Item as StatelyItem, Section } from '@react-stately/collections'
|
|
3
|
+
import { ComboBox, Select } from './subcomponents'
|
|
4
|
+
import {
|
|
5
|
+
type ComboBoxProps,
|
|
6
|
+
type SelectItem,
|
|
7
|
+
type SelectProps,
|
|
8
|
+
type SingleSelectProps,
|
|
9
|
+
} from './types'
|
|
10
|
+
|
|
11
|
+
export const SingleSelect = <T extends SelectItem>(props: SingleSelectProps<T>): JSX.Element => {
|
|
12
|
+
const { isComboBox, children, ...rest } = props
|
|
13
|
+
|
|
14
|
+
if (isComboBox) {
|
|
15
|
+
return <ComboBox {...(rest as ComboBoxProps<T>)}>{children}</ComboBox>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return <Select {...(rest as SelectProps<T>)}>{children}</Select>
|
|
19
|
+
}
|
|
49
20
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
21
|
+
type CustomItemProps = {
|
|
22
|
+
selectedIcon?: 'check' | 'radio'
|
|
23
|
+
selectedPosition?: 'start' | 'end'
|
|
24
|
+
key: string
|
|
25
|
+
children?: React.ReactNode
|
|
26
|
+
[key: string]: any
|
|
27
|
+
}
|
|
57
28
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
setOpen: state.setOpen,
|
|
63
|
-
selectedKey: state.selectedKey,
|
|
64
|
-
items: items,
|
|
65
|
-
anchorName,
|
|
66
|
-
}}
|
|
67
|
-
>
|
|
68
|
-
<RACSelect
|
|
69
|
-
// TODO: allow user to pass in label
|
|
70
|
-
aria-label={'single-select'}
|
|
71
|
-
onSelectionChange={(key) =>
|
|
72
|
-
handleOnSelectionChange(key != null ? new Set([key]) : new Set())
|
|
73
|
-
}
|
|
74
|
-
placeholder=""
|
|
75
|
-
{...restProps}
|
|
76
|
-
>
|
|
77
|
-
<Trigger buttonRef={buttonRef} />
|
|
29
|
+
export const Item = React.forwardRef<HTMLElement, CustomItemProps>((props, ref) => {
|
|
30
|
+
// @ts-expect-error: StatelyItem doesn't know about our internal item props
|
|
31
|
+
return <StatelyItem {...props} ref={ref} />
|
|
32
|
+
})
|
|
78
33
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
</Popover>
|
|
83
|
-
)}
|
|
84
|
-
</RACSelect>
|
|
85
|
-
</SingleSelectContext.Provider>
|
|
86
|
-
)
|
|
87
|
-
}
|
|
34
|
+
// @ts-expect-error: doesn't know that the Item can have this static property
|
|
35
|
+
Item.getCollectionNode = StatelyItem.getCollectionNode
|
|
36
|
+
Item.displayName = 'SingleSelectItem'
|
|
88
37
|
|
|
89
|
-
SingleSelect.
|
|
90
|
-
SingleSelect.
|
|
91
|
-
SingleSelect.ListItem = ListItem
|
|
92
|
-
SingleSelect.ListSection = ListSection
|
|
38
|
+
SingleSelect.Item = Item
|
|
39
|
+
SingleSelect.Section = Section
|