@kaizen/components 1.78.0 → 1.78.2
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/dist/cjs/src/Filter/FilterBar/subcomponents/FilterBarSelect/FilterBarSelect.cjs +1 -1
- package/dist/cjs/src/LikertScaleLegacy/LikertScaleLegacy.cjs +5 -3
- package/dist/cjs/src/Menu/subcomponents/StatelessMenu/StatelessMenu.cjs +0 -1
- package/dist/cjs/src/__next__/Select/Select.cjs +23 -15
- package/dist/esm/src/Filter/FilterBar/subcomponents/FilterBarSelect/FilterBarSelect.mjs +1 -1
- package/dist/esm/src/LikertScaleLegacy/LikertScaleLegacy.mjs +5 -3
- package/dist/esm/src/Menu/subcomponents/StatelessMenu/StatelessMenu.mjs +0 -1
- package/dist/esm/src/__next__/Select/Select.mjs +23 -15
- package/dist/styles.css +8950 -8951
- package/dist/types/LikertScaleLegacy/LikertScaleLegacy.d.ts +5 -1
- package/dist/types/Menu/subcomponents/StatelessMenu/StatelessMenu.d.ts +0 -1
- package/dist/types/__next__/Select/Select.d.ts +1 -1
- package/package.json +27 -26
- package/src/Avatar/Avatar.module.css +1 -1
- package/src/Badge/Badge.module.css +3 -3
- package/src/Filter/FilterBar/subcomponents/FilterBarSelect/FilterBarSelect.tsx +1 -1
- package/src/LikertScaleLegacy/LikertScaleLegacy.spec.tsx +1 -0
- package/src/LikertScaleLegacy/LikertScaleLegacy.tsx +7 -1
- package/src/LikertScaleLegacy/_docs/LikertScaleLegacy.mdx +8 -0
- package/src/LikertScaleLegacy/_docs/LikertScaleLegacy.stories.tsx +30 -1
- package/src/Link/Link.module.css +2 -2
- package/src/Menu/subcomponents/StatelessMenu/StatelessMenu.tsx +0 -2
- package/src/Pagination/subcomponents/TruncateIndicator/TruncateIndicator.module.css +1 -1
- package/src/Popover/Popover.module.scss +1 -1
- package/src/Radio/RadioField/RadioField.module.scss +2 -3
- package/src/Table/Table.module.scss +2 -2
- package/src/TextArea/TextArea.module.css +7 -7
- package/src/Workflow/subcomponents/Footer/components/Root/FooterRoot.module.css +1 -1
- package/src/__next__/Button/Button.module.css +11 -11
- package/src/__next__/Select/Select.tsx +5 -0
- package/src/__next__/Select/_docs/Select.mdx +8 -0
- package/src/__next__/Select/_docs/Select.stories.tsx +93 -0
- package/src/__next__/Select/subcomponents/SelectToggle/SelectToggle.module.scss +1 -1
- package/src/__next__/Tooltip/_docs/ApiSpecification.mdx +2 -2
- package/src/__next__/Tooltip/_docs/Tooltip.docs.stories.tsx +15 -30
- package/src/__next__/Tooltip/_docs/Tooltip.mdx +1 -1
- package/src/__next__/Tooltip/_docs/Tooltip.spec.stories.tsx +21 -58
- package/src/__next__/Tooltip/_docs/Tooltip.stories.tsx +2 -2
|
@@ -12,10 +12,14 @@ export type LikertScaleProps = {
|
|
|
12
12
|
'colorSchema'?: ColorSchema | 'classical';
|
|
13
13
|
'validationMessage'?: string;
|
|
14
14
|
'status'?: 'default' | 'error';
|
|
15
|
+
/**
|
|
16
|
+
* Sets aria-required value on radiogroup for assistive technologies. Validation must still be handled.
|
|
17
|
+
*/
|
|
18
|
+
'isRequired'?: boolean;
|
|
15
19
|
'onSelect': (value: ScaleItem | null) => void;
|
|
16
20
|
};
|
|
17
21
|
/**
|
|
18
22
|
* {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3082060201/Likert+Scale Guidance} |
|
|
19
23
|
* {@link https://cultureamp.design/?path=/docs/components-likertscalelegacy--docs Storybook}
|
|
20
24
|
*/
|
|
21
|
-
export declare const LikertScaleLegacy: ({ scale, selectedItem, reversed, colorSchema, "data-testid": dataTestId, onSelect, validationMessage, status, labelId, }: LikertScaleProps) => JSX.Element;
|
|
25
|
+
export declare const LikertScaleLegacy: ({ scale, selectedItem, reversed, colorSchema, "data-testid": dataTestId, onSelect, validationMessage, status, labelId, isRequired, }: LikertScaleProps) => JSX.Element;
|
|
@@ -41,7 +41,6 @@ export type StatelessMenuProps = {
|
|
|
41
41
|
'renderButton': (args: {
|
|
42
42
|
'onClick': (e: any) => void;
|
|
43
43
|
'onMouseDown': (e: any) => void;
|
|
44
|
-
'aria-haspopup': boolean;
|
|
45
44
|
'aria-expanded': boolean;
|
|
46
45
|
}) => React.ReactElement;
|
|
47
46
|
'onClick'?: (event: SyntheticEvent) => void;
|
|
@@ -51,7 +51,7 @@ export type SelectProps<Option extends SelectOption = SelectOption> = {
|
|
|
51
51
|
* {@link https://cultureamp.design/?path=/docs/components-select--docs Storybook}
|
|
52
52
|
*/
|
|
53
53
|
export declare const Select: {
|
|
54
|
-
<Option extends SelectOption = SelectOption>({ label, items, id: propsId, trigger, children, status, validationMessage, isReversed, isFullWidth, disabledValues, classNameOverride, selectedKey, description, placeholder, isDisabled, portalContainerId, ...restProps }: SelectProps<Option>): JSX.Element;
|
|
54
|
+
<Option extends SelectOption = SelectOption>({ label, items, id: propsId, trigger, children, status, validationMessage, isReversed, isRequired, isFullWidth, disabledValues, classNameOverride, selectedKey, description, placeholder, isDisabled, portalContainerId, onSelectionChange, ...restProps }: SelectProps<Option>): JSX.Element;
|
|
55
55
|
displayName: string;
|
|
56
56
|
Section: {
|
|
57
57
|
<Option extends SelectOption = SelectOption>({ section, }: import("./subcomponents").ListBoxSectionProps<Option>): JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaizen/components",
|
|
3
|
-
"version": "1.78.
|
|
3
|
+
"version": "1.78.2",
|
|
4
4
|
"description": "Kaizen component library",
|
|
5
5
|
"author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
|
|
6
6
|
"homepage": "https://cultureamp.design",
|
|
@@ -103,25 +103,25 @@
|
|
|
103
103
|
},
|
|
104
104
|
"dependencies": {
|
|
105
105
|
"@floating-ui/react-dom": "^2.1.2",
|
|
106
|
-
"@headlessui/react": "^2.2.
|
|
107
|
-
"@internationalized/date": "^3.8.
|
|
106
|
+
"@headlessui/react": "^2.2.4",
|
|
107
|
+
"@internationalized/date": "^3.8.1",
|
|
108
108
|
"@popperjs/core": "^2.11.8",
|
|
109
109
|
"@reach/tabs": "^0.18.0",
|
|
110
|
-
"@react-aria/button": "^3.13.
|
|
111
|
-
"@react-aria/datepicker": "^3.14.
|
|
112
|
-
"@react-aria/focus": "^3.20.
|
|
113
|
-
"@react-aria/i18n": "^3.12.
|
|
114
|
-
"@react-aria/listbox": "^3.14.
|
|
115
|
-
"@react-aria/menu": "^3.18.
|
|
116
|
-
"@react-aria/overlays": "^3.27.
|
|
117
|
-
"@react-aria/select": "^3.15.
|
|
118
|
-
"@react-aria/utils": "^3.
|
|
119
|
-
"@react-stately/collections": "^3.12.
|
|
120
|
-
"@react-stately/datepicker": "^3.14.
|
|
121
|
-
"@react-stately/list": "^3.12.
|
|
122
|
-
"@react-stately/menu": "^3.9.
|
|
123
|
-
"@react-stately/select": "^3.6.
|
|
124
|
-
"@react-types/shared": "^3.29.
|
|
110
|
+
"@react-aria/button": "^3.13.1",
|
|
111
|
+
"@react-aria/datepicker": "^3.14.3",
|
|
112
|
+
"@react-aria/focus": "^3.20.3",
|
|
113
|
+
"@react-aria/i18n": "^3.12.9",
|
|
114
|
+
"@react-aria/listbox": "^3.14.4",
|
|
115
|
+
"@react-aria/menu": "^3.18.3",
|
|
116
|
+
"@react-aria/overlays": "^3.27.1",
|
|
117
|
+
"@react-aria/select": "^3.15.5",
|
|
118
|
+
"@react-aria/utils": "^3.29.0",
|
|
119
|
+
"@react-stately/collections": "^3.12.4",
|
|
120
|
+
"@react-stately/datepicker": "^3.14.1",
|
|
121
|
+
"@react-stately/list": "^3.12.2",
|
|
122
|
+
"@react-stately/menu": "^3.9.4",
|
|
123
|
+
"@react-stately/select": "^3.6.13",
|
|
124
|
+
"@react-types/shared": "^3.29.1",
|
|
125
125
|
"classnames": "^2.5.1",
|
|
126
126
|
"date-fns": "^4.1.0",
|
|
127
127
|
"lodash.debounce": "^4.0.8",
|
|
@@ -136,10 +136,10 @@
|
|
|
136
136
|
"prosemirror-state": "^1.4.3",
|
|
137
137
|
"prosemirror-transform": "^1.10.4",
|
|
138
138
|
"prosemirror-utils": "^1.2.2",
|
|
139
|
-
"prosemirror-view": "^1.39.
|
|
139
|
+
"prosemirror-view": "^1.39.3",
|
|
140
140
|
"react-animate-height": "^3.2.3",
|
|
141
|
-
"react-aria": "^3.
|
|
142
|
-
"react-aria-components": "^1.
|
|
141
|
+
"react-aria": "^3.40.0",
|
|
142
|
+
"react-aria-components": "^1.9.0",
|
|
143
143
|
"react-day-picker": "9.6.7",
|
|
144
144
|
"react-focus-lock": "^2.13.6",
|
|
145
145
|
"react-focus-on": "^3.9.4",
|
|
@@ -154,9 +154,9 @@
|
|
|
154
154
|
"react-day-picker": "Version locked until a11y gets fixed (https://github.com/gpbl/react-day-picker/pull/1708)"
|
|
155
155
|
},
|
|
156
156
|
"devDependencies": {
|
|
157
|
-
"@cultureamp/frontend-apis": "
|
|
158
|
-
"@cultureamp/i18n-react-intl": "^2.
|
|
159
|
-
"@cultureamp/package-bundler": "^2.3.
|
|
157
|
+
"@cultureamp/frontend-apis": "13.3.0",
|
|
158
|
+
"@cultureamp/i18n-react-intl": "^2.9.1",
|
|
159
|
+
"@cultureamp/package-bundler": "^2.3.2",
|
|
160
160
|
"@testing-library/dom": "^10.4.0",
|
|
161
161
|
"@types/jest-axe": "^3.5.9",
|
|
162
162
|
"@types/lodash.debounce": "^4.0.9",
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
"postcss-import": "^16.1.0",
|
|
175
175
|
"postcss-preset-env": "^10.1.6",
|
|
176
176
|
"postcss-scss": "^4.0.9",
|
|
177
|
-
"query-string": "^9.
|
|
177
|
+
"query-string": "^9.2.0",
|
|
178
178
|
"react": "^19.0.0",
|
|
179
179
|
"react-dom": "^19.0.0",
|
|
180
180
|
"react-highlight": "^0.15.0",
|
|
@@ -190,7 +190,8 @@
|
|
|
190
190
|
},
|
|
191
191
|
"devDependenciesComments": {
|
|
192
192
|
"sass": "Prevent deprecation warnings introduced in 1.80 as we plan to move away from sass",
|
|
193
|
-
"typescript": "Installed in root"
|
|
193
|
+
"typescript": "Installed in root",
|
|
194
|
+
"frontend-apis": "There's an unresolved dep with 13.4.0 and onwards with node/msw, so we are locking to 13.3.0 for this update."
|
|
194
195
|
},
|
|
195
196
|
"peerDependencies": {
|
|
196
197
|
"@cultureamp/i18n-react-intl": "^2.5.9",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
.reversed {
|
|
18
|
-
--badge-background-color:
|
|
18
|
+
--badge-background-color: rgb(var(--color-white-rgb), 0.1);
|
|
19
19
|
|
|
20
20
|
color: var(--color-white);
|
|
21
21
|
}
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.dark {
|
|
53
|
-
--badge-background-color:
|
|
53
|
+
--badge-background-color: rgb(var(--color-purple-700-rgb), 0.1);
|
|
54
54
|
|
|
55
55
|
color: var(--color-purple-800);
|
|
56
56
|
}
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
.animationOn .badge.dark {
|
|
90
|
-
--badge-background-color:
|
|
90
|
+
--badge-background-color: rgb(var(--color-purple-700-rgb), 0.2);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
.animationOn .badge.active {
|
|
@@ -60,7 +60,7 @@ export const FilterBarSelect = <Option extends SelectOption = SelectOption>({
|
|
|
60
60
|
<FilterBarButton {...triggerProps} filterId={id} isRemovable={filterState.isRemovable} />
|
|
61
61
|
)}
|
|
62
62
|
onSelectionChange={(key): void => {
|
|
63
|
-
updateValue(id, key)
|
|
63
|
+
updateValue(id, key ?? undefined)
|
|
64
64
|
onSelectionChange?.(key)
|
|
65
65
|
}}
|
|
66
66
|
isOpen={filterState.isOpen}
|
|
@@ -25,6 +25,10 @@ export type LikertScaleProps = {
|
|
|
25
25
|
'colorSchema'?: ColorSchema | 'classical'
|
|
26
26
|
'validationMessage'?: string
|
|
27
27
|
'status'?: 'default' | 'error'
|
|
28
|
+
/**
|
|
29
|
+
* Sets aria-required value on radiogroup for assistive technologies. Validation must still be handled.
|
|
30
|
+
*/
|
|
31
|
+
'isRequired'?: boolean
|
|
28
32
|
'onSelect': (value: ScaleItem | null) => void
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -46,6 +50,7 @@ export const LikertScaleLegacy = ({
|
|
|
46
50
|
validationMessage,
|
|
47
51
|
status,
|
|
48
52
|
labelId,
|
|
53
|
+
isRequired,
|
|
49
54
|
}: LikertScaleProps): JSX.Element => {
|
|
50
55
|
const [hoveredItem, setHoveredItem] = useState<ScaleItem | null>(null)
|
|
51
56
|
const itemRefs: ItemRefs = scale.map((s) => ({
|
|
@@ -104,11 +109,12 @@ export const LikertScaleLegacy = ({
|
|
|
104
109
|
reversed && [styles.reversed],
|
|
105
110
|
hoveredItem !== null && styles.hovered,
|
|
106
111
|
)}
|
|
107
|
-
aria-labelledby={labelId}
|
|
112
|
+
aria-labelledby={isRequired ? `${labelId}` : labelId}
|
|
108
113
|
role="radiogroup"
|
|
109
114
|
tabIndex={-1}
|
|
110
115
|
aria-describedby={validationMessageId}
|
|
111
116
|
data-testid={dataTestId}
|
|
117
|
+
aria-required={isRequired}
|
|
112
118
|
>
|
|
113
119
|
<div className={styles.legend} data-testid={dataTestId && `${dataTestId}-legend`}>
|
|
114
120
|
<Text variant="small" color={reversed ? 'white' : 'dark'}>
|
|
@@ -21,3 +21,11 @@ Likert scale radio buttons let people select one option in a Likert scale rangin
|
|
|
21
21
|
|
|
22
22
|
<Canvas of={LikertScaleLegacyStories.Playground} />
|
|
23
23
|
<Controls of={LikertScaleLegacyStories.Playground} />
|
|
24
|
+
|
|
25
|
+
## API
|
|
26
|
+
|
|
27
|
+
### isRequired
|
|
28
|
+
|
|
29
|
+
Sets aria-required value on radiogroup for assistive technologies. An accessible label must be provided and validation must still be handled within implementations.
|
|
30
|
+
|
|
31
|
+
<Canvas of={LikertScaleLegacyStories.IsRequired} />
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
import { type Meta, type StoryObj } from '@storybook/react'
|
|
3
|
+
import { expect, within } from '@storybook/test'
|
|
4
|
+
import { VisuallyHidden } from '~components/VisuallyHidden'
|
|
3
5
|
import { LikertScaleLegacy } from '../index'
|
|
4
6
|
import { type Scale, type ScaleItem } from '../types'
|
|
5
7
|
|
|
@@ -57,7 +59,7 @@ export const Playground: Story = {
|
|
|
57
59
|
code: `
|
|
58
60
|
const SatisfactionExample = () => {
|
|
59
61
|
const [selectedItem, setSelectedItem] = useState<ScaleItem | null>(null)
|
|
60
|
-
|
|
62
|
+
|
|
61
63
|
return (
|
|
62
64
|
<LikertScaleLegacy
|
|
63
65
|
scale={[
|
|
@@ -82,3 +84,30 @@ export const Playground: Story = {
|
|
|
82
84
|
},
|
|
83
85
|
},
|
|
84
86
|
}
|
|
87
|
+
|
|
88
|
+
export const IsRequired: Story = {
|
|
89
|
+
render: (args) => {
|
|
90
|
+
const [selectedItem, setSelectedItem] = useState<ScaleItem | null>(null)
|
|
91
|
+
const labelId = React.useId()
|
|
92
|
+
return (
|
|
93
|
+
<div>
|
|
94
|
+
<VisuallyHidden id={labelId}>Likert scale label</VisuallyHidden>
|
|
95
|
+
<LikertScaleLegacy
|
|
96
|
+
{...args}
|
|
97
|
+
labelId={labelId}
|
|
98
|
+
selectedItem={selectedItem}
|
|
99
|
+
onSelect={setSelectedItem}
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
)
|
|
103
|
+
},
|
|
104
|
+
args: {
|
|
105
|
+
isRequired: true,
|
|
106
|
+
},
|
|
107
|
+
play: async ({ canvasElement }) => {
|
|
108
|
+
const canvas = within(canvasElement.parentElement!)
|
|
109
|
+
const likertScale = canvas.getByRole('radiogroup', { name: 'Likert scale label' })
|
|
110
|
+
|
|
111
|
+
expect(likertScale).toHaveAttribute('aria-required', 'true')
|
|
112
|
+
},
|
|
113
|
+
}
|
package/src/Link/Link.module.css
CHANGED
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
.white.isDisabled {
|
|
97
|
-
--link-text-color:
|
|
97
|
+
--link-text-color: rgb(var(--color-white-rgb), 0.2);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
.reversed {
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
.reversed.isDisabled {
|
|
113
|
-
--link-text-color:
|
|
113
|
+
--link-text-color: rgb(var(--color-white-rgb), 0.2);
|
|
114
114
|
}
|
|
@@ -46,7 +46,6 @@ export type StatelessMenuProps = {
|
|
|
46
46
|
'renderButton': (args: {
|
|
47
47
|
'onClick': (e: any) => void
|
|
48
48
|
'onMouseDown': (e: any) => void
|
|
49
|
-
'aria-haspopup': boolean
|
|
50
49
|
'aria-expanded': boolean
|
|
51
50
|
}) => React.ReactElement
|
|
52
51
|
'onClick'?: (event: SyntheticEvent) => void
|
|
@@ -76,7 +75,6 @@ export const StatelessMenu = ({
|
|
|
76
75
|
toggleMenuDropdown()
|
|
77
76
|
},
|
|
78
77
|
'onMouseDown': (e: React.MouseEvent<Element, MouseEvent>) => e.preventDefault(),
|
|
79
|
-
'aria-haspopup': true,
|
|
80
78
|
'aria-expanded': isMenuVisible,
|
|
81
79
|
})
|
|
82
80
|
|
|
@@ -46,7 +46,7 @@ $large-width: 450px;
|
|
|
46
46
|
--border-width: var(--border-width-1);
|
|
47
47
|
|
|
48
48
|
border: var(--border-width) var(--border-solid-border-style);
|
|
49
|
-
filter: drop-shadow(0 0 7px
|
|
49
|
+
filter: drop-shadow(0 0 7px rgb(0, 0, 0, 0.1));
|
|
50
50
|
border-radius: $border-solid-border-radius;
|
|
51
51
|
color: $color-purple-800;
|
|
52
52
|
text-align: start;
|
|
@@ -7,9 +7,8 @@ $dt-color-radio-background-color-hover: $color-gray-200;
|
|
|
7
7
|
|
|
8
8
|
.container {
|
|
9
9
|
position: relative;
|
|
10
|
-
overflow-wrap:
|
|
11
|
-
word-
|
|
12
|
-
word-break: break-word;
|
|
10
|
+
overflow-wrap: anywhere;
|
|
11
|
+
word-break: normal;
|
|
13
12
|
margin-bottom: $spacing-sm;
|
|
14
13
|
|
|
15
14
|
label {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
// Taken from design-tokens/sass/shadow
|
|
8
8
|
// we need control of the x and y offset in this component
|
|
9
|
-
$box-shadow-color-sm:
|
|
9
|
+
$box-shadow-color-sm: rgb(53, 55, 74, 0.09);
|
|
10
10
|
$row-height: 60px;
|
|
11
11
|
$row-height-data-variant: 48px;
|
|
12
12
|
|
|
@@ -170,7 +170,7 @@ $row-height-data-variant: 48px;
|
|
|
170
170
|
|
|
171
171
|
// This is an optical hack to stop the card shadow from overlapping over
|
|
172
172
|
// the proceeding cards
|
|
173
|
-
box-shadow: 0 4px 6px
|
|
173
|
+
box-shadow: 0 4px 6px rgb(53, 55, 74, 0.04);
|
|
174
174
|
border: solid 1px rgba($color-purple-700-rgb, 0.1);
|
|
175
175
|
transition:
|
|
176
176
|
box-shadow $animation-duration-rapid,
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
.default {
|
|
59
59
|
&:not(.error, .caution) {
|
|
60
60
|
&:disabled {
|
|
61
|
-
border-color:
|
|
61
|
+
border-color: rgb(var(--color-gray-500-rgb), 0.3);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -91,13 +91,13 @@
|
|
|
91
91
|
|
|
92
92
|
&.disabled {
|
|
93
93
|
background-color: var(--color-white);
|
|
94
|
-
border-color:
|
|
95
|
-
color:
|
|
94
|
+
border-color: rgb(var(--color-gray-500-rgb), 0.3);
|
|
95
|
+
color: rgb(var(--color-purple-800-rgb), 0.3);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
.reversed {
|
|
100
|
-
border-color:
|
|
100
|
+
border-color: rgb(var(--color-white-rgb), 0.65);
|
|
101
101
|
background: transparent;
|
|
102
102
|
color: var(--color-white);
|
|
103
103
|
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
&:focus:not([disabled]),
|
|
109
109
|
&:hover:not([disabled]),
|
|
110
110
|
&:hover:focus:not([disabled]) {
|
|
111
|
-
background:
|
|
111
|
+
background: rgb(var(--color-white-rgb), 0.1);
|
|
112
112
|
border-color: var(--color-white);
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
|
|
135
135
|
&.disabled {
|
|
136
136
|
background: transparent;
|
|
137
|
-
border-color:
|
|
138
|
-
color:
|
|
137
|
+
border-color: rgb(var(--color-white-rgb), 0.3);
|
|
138
|
+
color: rgb(var(--color-white-rgb), 0.3);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
padding: var(--spacing-24) var(--spacing-12);
|
|
9
9
|
background: var(--color-white);
|
|
10
10
|
gap: var(--spacing-16);
|
|
11
|
-
border-top: 2px solid
|
|
11
|
+
border-top: 2px solid rgb(var(--color-gray-600-rgb), 0.1);
|
|
12
12
|
|
|
13
13
|
@media (width >= 768px) {
|
|
14
14
|
grid-template-columns: 1fr 5fr 1fr;
|
|
@@ -185,9 +185,9 @@
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
&.isDisabled {
|
|
188
|
-
--button-bg-color:
|
|
188
|
+
--button-bg-color: rgb(var(--color-white-rgb), 0.2);
|
|
189
189
|
--button-border-color: transparent;
|
|
190
|
-
--button-text-color:
|
|
190
|
+
--button-text-color: rgb(var(--color-purple-800-rgb), 0.7);
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -197,24 +197,24 @@
|
|
|
197
197
|
--button-text-color: var(--color-white);
|
|
198
198
|
|
|
199
199
|
&[data-hovered] {
|
|
200
|
-
--button-bg-color:
|
|
200
|
+
--button-bg-color: rgb(var(--color-white-rgb), 0.2);
|
|
201
201
|
--button-border-color: var(--color-white);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
&[data-pressed] {
|
|
205
|
-
--button-bg-color:
|
|
205
|
+
--button-bg-color: rgb(var(--color-white-rgb), 0.1);
|
|
206
206
|
--button-border-color: var(--color-white);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
&[data-pending] {
|
|
210
|
-
--button-bg-color:
|
|
210
|
+
--button-bg-color: rgb(var(--color-white-rgb), 0.1);
|
|
211
211
|
--button-border-color: var(--color-white);
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
&.isDisabled {
|
|
215
215
|
--button-bg-color: transparent;
|
|
216
|
-
--button-border-color:
|
|
217
|
-
--button-text-color:
|
|
216
|
+
--button-border-color: rgb(var(--color-white), 0.2);
|
|
217
|
+
--button-text-color: rgb(var(--color-white-rgb), 0.2);
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
@@ -224,24 +224,24 @@
|
|
|
224
224
|
--button-text-color: var(--color-white);
|
|
225
225
|
|
|
226
226
|
&[data-hovered] {
|
|
227
|
-
--button-bg-color:
|
|
227
|
+
--button-bg-color: rgb(var(--color-white-rgb), 0.2);
|
|
228
228
|
--button-border-color: transparent;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
&[data-pressed] {
|
|
232
|
-
--button-bg-color:
|
|
232
|
+
--button-bg-color: rgb(var(--color-white-rgb), 0.1);
|
|
233
233
|
--button-border-color: transparent;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
&[data-pending] {
|
|
237
|
-
--button-bg-color:
|
|
237
|
+
--button-bg-color: rgb(var(--color-white-rgb), 0.1);
|
|
238
238
|
--button-border-color: transparent;
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
&.isDisabled {
|
|
242
242
|
--button-bg-color: transparent;
|
|
243
243
|
--button-border-color: transparent;
|
|
244
|
-
--button-text-color:
|
|
244
|
+
--button-text-color: rgb(var(--color-white-rgb), 0.2);
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
|
|
@@ -85,6 +85,7 @@ export const Select = <Option extends SelectOption = SelectOption>({
|
|
|
85
85
|
status,
|
|
86
86
|
validationMessage,
|
|
87
87
|
isReversed,
|
|
88
|
+
isRequired = false,
|
|
88
89
|
isFullWidth,
|
|
89
90
|
disabledValues,
|
|
90
91
|
classNameOverride,
|
|
@@ -93,6 +94,7 @@ export const Select = <Option extends SelectOption = SelectOption>({
|
|
|
93
94
|
placeholder = '',
|
|
94
95
|
isDisabled,
|
|
95
96
|
portalContainerId,
|
|
97
|
+
onSelectionChange,
|
|
96
98
|
...restProps
|
|
97
99
|
}: SelectProps<Option>): JSX.Element => {
|
|
98
100
|
const { refs } = useFloating<HTMLButtonElement>()
|
|
@@ -114,6 +116,8 @@ export const Select = <Option extends SelectOption = SelectOption>({
|
|
|
114
116
|
description,
|
|
115
117
|
placeholder,
|
|
116
118
|
isDisabled,
|
|
119
|
+
isRequired,
|
|
120
|
+
onSelectionChange: onSelectionChange ? (key) => onSelectionChange(key!) : undefined,
|
|
117
121
|
...restProps,
|
|
118
122
|
}
|
|
119
123
|
|
|
@@ -153,6 +157,7 @@ export const Select = <Option extends SelectOption = SelectOption>({
|
|
|
153
157
|
isReversed,
|
|
154
158
|
'ref': refs.setReference,
|
|
155
159
|
'aria-describedby': classnames(validationMessage && validationId, description && descriptionId),
|
|
160
|
+
'aria-required': isRequired,
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
const [portalContainer, setPortalContainer] = useState<HTMLElement>()
|
|
@@ -92,6 +92,14 @@ Add validation messages using `status` and `validationMessage`.
|
|
|
92
92
|
|
|
93
93
|
<Canvas of={SelectStories.Validation} />
|
|
94
94
|
|
|
95
|
+
#### isRequired and validationBehavior
|
|
96
|
+
|
|
97
|
+
When using the `isRequired` property you can also specify the `validationBehavior` to change from `aria` to `native` form validation.
|
|
98
|
+
|
|
99
|
+
<Canvas of={SelectStories.SelectNativeValidationBehavior} />
|
|
100
|
+
|
|
101
|
+
While both use `aria-required` to announce whether the field has to have a value to assistive technologies, the `native` will option will prevent form submissions if the `selectedKey` is `undefined`.
|
|
102
|
+
|
|
95
103
|
### Full width
|
|
96
104
|
|
|
97
105
|
Set `isFullWidth` to `true` to have the Select span the full width of its container.
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { type Meta, type StoryObj } from '@storybook/react'
|
|
3
|
+
import { expect, userEvent, waitFor, within } from '@storybook/test'
|
|
4
|
+
import { FieldMessage } from '~components/FieldMessage'
|
|
3
5
|
import { ContextModal } from '~components/Modal'
|
|
4
6
|
import { RadioField, RadioGroup } from '~components/Radio'
|
|
7
|
+
import { Button } from '~components/__next__'
|
|
5
8
|
import { Select } from '../Select'
|
|
6
9
|
import { type SelectOption } from '../types'
|
|
7
10
|
import { groupedMockItems, mixedMockItemsDisabled, singleMockItems } from './mockData'
|
|
@@ -229,3 +232,93 @@ export const TouchDeviceTest: Story = {
|
|
|
229
232
|
)
|
|
230
233
|
},
|
|
231
234
|
}
|
|
235
|
+
|
|
236
|
+
export const RequiredSelect: Story = {
|
|
237
|
+
args: {
|
|
238
|
+
label: 'Required Select',
|
|
239
|
+
isRequired: true,
|
|
240
|
+
validationBehavior: 'native',
|
|
241
|
+
},
|
|
242
|
+
render: (args) => <Select {...args} />,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export const SelectNativeValidationBehavior: Story = {
|
|
246
|
+
parameters: {
|
|
247
|
+
name: 'Required Select with native form validation',
|
|
248
|
+
},
|
|
249
|
+
args: {
|
|
250
|
+
label: 'Required Select',
|
|
251
|
+
isRequired: true,
|
|
252
|
+
validationBehavior: 'native',
|
|
253
|
+
},
|
|
254
|
+
render: (args) => {
|
|
255
|
+
const [hasSubmitted, setHasSubmitted] = React.useState(false)
|
|
256
|
+
return (
|
|
257
|
+
<div>
|
|
258
|
+
<form
|
|
259
|
+
className="flex flex-col gap-16"
|
|
260
|
+
name="form-with-required-select"
|
|
261
|
+
aria-describedby={hasSubmitted ? 'id--field-message-form' : undefined}
|
|
262
|
+
onSubmit={(e) => {
|
|
263
|
+
e.preventDefault()
|
|
264
|
+
setHasSubmitted(true)
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
<Select {...args} isRequired />
|
|
268
|
+
<div>
|
|
269
|
+
<Button type="submit">Submit</Button>
|
|
270
|
+
</div>
|
|
271
|
+
</form>
|
|
272
|
+
{hasSubmitted && (
|
|
273
|
+
<FieldMessage
|
|
274
|
+
id="id--field-message-form"
|
|
275
|
+
classNameOverride="mt-8"
|
|
276
|
+
status="success"
|
|
277
|
+
message={'Form submitted!'}
|
|
278
|
+
/>
|
|
279
|
+
)}
|
|
280
|
+
</div>
|
|
281
|
+
)
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export const NativeFormValidationWithoutSelectedVal: Story = {
|
|
286
|
+
...SelectNativeValidationBehavior,
|
|
287
|
+
play: async ({ canvasElement, step }) => {
|
|
288
|
+
const canvas = within(canvasElement.parentElement!)
|
|
289
|
+
const submitButton = canvas.getByRole('button', { name: 'Submit' })
|
|
290
|
+
const requiredSelect = canvas.getByRole('combobox', { name: 'Required Select' })
|
|
291
|
+
const form = await canvas.findByRole('form')
|
|
292
|
+
|
|
293
|
+
await step('Select has aria-required attribute', async () => {
|
|
294
|
+
expect(requiredSelect).toHaveAttribute('aria-required', 'true')
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
await step('Submit will not call onSubmit without a selected value', async () => {
|
|
298
|
+
await userEvent.click(submitButton)
|
|
299
|
+
await waitFor(() => {
|
|
300
|
+
expect(form).toHaveAccessibleDescription('')
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
},
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export const NativeFormValidationWithSelectedVal: Story = {
|
|
307
|
+
...SelectNativeValidationBehavior,
|
|
308
|
+
args: {
|
|
309
|
+
selectedKey: 'short-black',
|
|
310
|
+
},
|
|
311
|
+
play: async ({ canvasElement, step }) => {
|
|
312
|
+
const canvas = within(canvasElement.parentElement!)
|
|
313
|
+
const submitButton = canvas.getByRole('button', { name: 'Submit' })
|
|
314
|
+
const form = await canvas.findByRole('form')
|
|
315
|
+
|
|
316
|
+
await step('Submit will call onSubmit with a selected value', async () => {
|
|
317
|
+
await userEvent.click(submitButton)
|
|
318
|
+
|
|
319
|
+
await waitFor(() => {
|
|
320
|
+
expect(form).toHaveAccessibleDescription('Form submitted!')
|
|
321
|
+
})
|
|
322
|
+
})
|
|
323
|
+
},
|
|
324
|
+
}
|