@lumx/react 3.18.1 → 3.18.2-alpha.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 (47) hide show
  1. package/_internal/index.js +236 -0
  2. package/_internal/index.js.map +1 -0
  3. package/index.d.ts +13 -8
  4. package/index.js +219 -226
  5. package/index.js.map +1 -1
  6. package/package.json +3 -3
  7. package/src/components/autocomplete/Autocomplete.tsx +5 -4
  8. package/src/components/autocomplete/AutocompleteMultiple.tsx +5 -3
  9. package/src/components/button/Button.stories.tsx +1 -0
  10. package/src/components/button/Button.test.tsx +41 -2
  11. package/src/components/button/ButtonRoot.tsx +10 -11
  12. package/src/components/checkbox/Checkbox.stories.tsx +13 -2
  13. package/src/components/checkbox/Checkbox.test.tsx +29 -0
  14. package/src/components/checkbox/Checkbox.tsx +8 -7
  15. package/src/components/chip/Chip.stories.tsx +17 -0
  16. package/src/components/chip/Chip.test.tsx +44 -0
  17. package/src/components/chip/Chip.tsx +10 -9
  18. package/src/components/date-picker/DatePickerField.stories.tsx +18 -0
  19. package/src/components/date-picker/DatePickerField.tsx +4 -4
  20. package/src/components/link/Link.stories.tsx +4 -1
  21. package/src/components/link/Link.test.tsx +45 -6
  22. package/src/components/link/Link.tsx +7 -6
  23. package/src/components/list/ListItem.stories.tsx +14 -48
  24. package/src/components/list/ListItem.test.tsx +78 -7
  25. package/src/components/list/ListItem.tsx +11 -9
  26. package/src/components/progress-tracker/ProgressTrackerStep.tsx +7 -7
  27. package/src/components/radio-button/RadioButton.stories.tsx +32 -0
  28. package/src/components/radio-button/RadioButton.test.tsx +30 -0
  29. package/src/components/radio-button/RadioButton.tsx +8 -7
  30. package/src/components/slider/Slider.tsx +6 -7
  31. package/src/components/switch/Switch.stories.tsx +11 -1
  32. package/src/components/switch/Switch.test.tsx +30 -0
  33. package/src/components/switch/Switch.tsx +8 -7
  34. package/src/components/table/TableRow.tsx +8 -6
  35. package/src/components/tabs/Tab.tsx +12 -9
  36. package/src/components/text-field/TextField.stories.tsx +22 -0
  37. package/src/components/text-field/TextField.test.tsx +56 -0
  38. package/src/components/text-field/TextField.tsx +12 -10
  39. package/src/utils/disabled/DisabledStateContext.tsx +29 -0
  40. package/src/utils/disabled/DisabledStateProvider.stories.tsx +88 -0
  41. package/src/utils/disabled/index.ts +2 -0
  42. package/src/utils/disabled/useDisableStateProps.tsx +37 -0
  43. package/src/utils/index.ts +1 -0
  44. package/src/utils/type/HasAriaDisabled.ts +6 -0
  45. package/utils/index.d.ts +20 -1
  46. package/utils/index.js +1 -134
  47. package/utils/index.js.map +1 -1
package/package.json CHANGED
@@ -6,8 +6,8 @@
6
6
  "url": "https://github.com/lumapps/design-system/issues"
7
7
  },
8
8
  "dependencies": {
9
- "@lumx/core": "^3.18.1",
10
- "@lumx/icons": "^3.18.1",
9
+ "@lumx/core": "^3.18.2-alpha.1",
10
+ "@lumx/icons": "^3.18.2-alpha.1",
11
11
  "@popperjs/core": "^2.5.4",
12
12
  "body-scroll-lock": "^3.1.5",
13
13
  "classnames": "^2.3.2",
@@ -105,5 +105,5 @@
105
105
  "build:storybook": "storybook build"
106
106
  },
107
107
  "sideEffects": false,
108
- "version": "3.18.1"
108
+ "version": "3.18.2-alpha.1"
109
109
  }
@@ -11,6 +11,8 @@ import { mergeRefs } from '@lumx/react/utils/react/mergeRefs';
11
11
  import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
12
12
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
13
13
 
14
+ import { useDisableStateProps } from '@lumx/react/utils/disabled/useDisableStateProps';
15
+
14
16
  /**
15
17
  * Defines the props of the component.
16
18
  */
@@ -203,6 +205,7 @@ const DEFAULT_PROPS: Partial<AutocompleteProps> = {
203
205
  */
204
206
  export const Autocomplete = forwardRef<AutocompleteProps, HTMLDivElement>((props, ref) => {
205
207
  const defaultTheme = useTheme();
208
+ const { disabledStateProps, otherProps } = useDisableStateProps(props);
206
209
  const {
207
210
  anchorToInput = DEFAULT_PROPS.anchorToInput,
208
211
  children,
@@ -211,7 +214,6 @@ export const Autocomplete = forwardRef<AutocompleteProps, HTMLDivElement>((props
211
214
  closeOnClick = DEFAULT_PROPS.closeOnClick,
212
215
  closeOnClickAway = DEFAULT_PROPS.closeOnClickAway,
213
216
  closeOnEscape = DEFAULT_PROPS.closeOnEscape,
214
- disabled,
215
217
  error,
216
218
  fitToAnchorWidth,
217
219
  hasError,
@@ -219,7 +221,6 @@ export const Autocomplete = forwardRef<AutocompleteProps, HTMLDivElement>((props
219
221
  icon,
220
222
  inputRef,
221
223
  clearButtonProps,
222
- isDisabled = disabled,
223
224
  isRequired,
224
225
  isOpen,
225
226
  isValid,
@@ -239,7 +240,7 @@ export const Autocomplete = forwardRef<AutocompleteProps, HTMLDivElement>((props
239
240
  textFieldProps = {},
240
241
  focusAnchorOnClose,
241
242
  ...forwardedProps
242
- } = props;
243
+ } = otherProps;
243
244
  const inputAnchorRef = useRef<HTMLElement>(null);
244
245
  const textFieldRef = useRef(null);
245
246
  useFocus(inputAnchorRef.current, !isOpen && shouldFocusOnClose);
@@ -255,7 +256,7 @@ export const Autocomplete = forwardRef<AutocompleteProps, HTMLDivElement>((props
255
256
  icon={icon}
256
257
  inputRef={mergeRefs(inputAnchorRef as React.RefObject<HTMLInputElement>, inputRef)}
257
258
  clearButtonProps={clearButtonProps}
258
- isDisabled={isDisabled}
259
+ {...disabledStateProps}
259
260
  isRequired={isRequired}
260
261
  isValid={isValid}
261
262
  label={label}
@@ -9,6 +9,8 @@ import { getRootClassName } from '@lumx/react/utils/className';
9
9
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
10
10
  import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
11
11
 
12
+ import { useDisableStateProps } from '@lumx/react/utils/disabled/useDisableStateProps';
13
+
12
14
  /**
13
15
  * Defines the props of the component.
14
16
  */
@@ -69,6 +71,7 @@ const DEFAULT_PROPS: Partial<AutocompleteMultipleProps> = {
69
71
  */
70
72
  export const AutocompleteMultiple = forwardRef<AutocompleteMultipleProps, HTMLDivElement>((props, ref) => {
71
73
  const defaultTheme = useTheme();
74
+ const { disabledStateProps, otherProps } = useDisableStateProps(props);
72
75
  const {
73
76
  anchorToInput,
74
77
  children,
@@ -84,7 +87,6 @@ export const AutocompleteMultiple = forwardRef<AutocompleteMultipleProps, HTMLDi
84
87
  icon,
85
88
  inputRef,
86
89
  clearButtonProps,
87
- isDisabled,
88
90
  isRequired,
89
91
  isOpen,
90
92
  isValid,
@@ -107,7 +109,7 @@ export const AutocompleteMultiple = forwardRef<AutocompleteMultipleProps, HTMLDi
107
109
  value,
108
110
  values = DEFAULT_PROPS.values,
109
111
  ...forwardedProps
110
- } = props;
112
+ } = otherProps;
111
113
 
112
114
  return (
113
115
  <Autocomplete
@@ -127,7 +129,7 @@ export const AutocompleteMultiple = forwardRef<AutocompleteMultipleProps, HTMLDi
127
129
  icon={icon}
128
130
  inputRef={inputRef}
129
131
  chips={values && values.map((chip: any, index: number) => selectedChipRender?.(chip, index, onClear))}
130
- isDisabled={isDisabled}
132
+ {...disabledStateProps}
131
133
  isRequired={isRequired}
132
134
  clearButtonProps={clearButtonProps}
133
135
  isValid={isValid}
@@ -98,6 +98,7 @@ export const LinkButton = {
98
98
  cols: {
99
99
  Default: {},
100
100
  Disabled: { isDisabled: true },
101
+ AriaDisabled: { 'aria-disabled': true },
101
102
  },
102
103
  },
103
104
  }),
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  import { mdiCheck, mdiPlus } from '@lumx/icons';
4
4
  import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
5
5
  import { render, screen, within } from '@testing-library/react';
6
+ import userEvent from '@testing-library/user-event';
6
7
  import { getByClassName, queryAllByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
7
8
  import { Emphasis, Icon } from '@lumx/react';
8
9
 
@@ -63,11 +64,49 @@ describe(`<${Button.displayName}>`, () => {
63
64
  expect(buttonWrapper).toBeInTheDocument();
64
65
  expect(button).toBe(within(buttonWrapper as any).queryByRole('button', { name: label }));
65
66
  });
67
+ });
68
+
69
+ describe('Disabled state', () => {
70
+ it('should render disabled button', async () => {
71
+ const onClick = jest.fn();
72
+ const { button } = setup({ children: 'Label', disabled: true, onClick });
73
+ expect(button).toHaveAttribute('disabled');
74
+ await userEvent.click(button);
75
+ expect(onClick).not.toHaveBeenCalled();
76
+ });
77
+
78
+ it('should render disabled link', async () => {
79
+ const onClick = jest.fn();
80
+ const { button } = setup({ children: 'Label', disabled: true, href: 'https://example.com', onClick });
81
+ // Disabled link do not exist so we fallback to a button
82
+ expect(screen.queryByRole('link')).not.toBeInTheDocument();
83
+ expect(button).toHaveAttribute('disabled');
84
+ await userEvent.click(button);
85
+ expect(onClick).not.toHaveBeenCalled();
86
+ });
66
87
 
67
- it('should prevent click when aria-disabled', () => {
88
+ it('should render aria-disabled button', async () => {
68
89
  const onClick = jest.fn();
69
90
  const { button } = setup({ children: 'Label', 'aria-disabled': true, onClick });
70
- expect(button.onclick).toBeFalsy();
91
+ expect(button).toHaveAttribute('aria-disabled');
92
+ await userEvent.click(button);
93
+ expect(onClick).not.toHaveBeenCalled();
94
+ });
95
+
96
+ it('should render aria-disabled link', async () => {
97
+ const onClick = jest.fn();
98
+ const { button } = setup({
99
+ children: 'Label',
100
+ 'aria-disabled': true,
101
+ href: 'https://example.com',
102
+ onClick,
103
+ });
104
+ expect(button).toHaveAccessibleName('Label');
105
+ // Disabled link do not exist so we fallback to a button
106
+ expect(screen.queryByRole('link')).not.toBeInTheDocument();
107
+ expect(button).toHaveAttribute('aria-disabled', 'true');
108
+ await userEvent.click(button);
109
+ expect(onClick).not.toHaveBeenCalled();
71
110
  });
72
111
  });
73
112
 
@@ -10,6 +10,8 @@ import { GenericProps, HasTheme } from '@lumx/react/utils/type';
10
10
  import { handleBasicClasses } from '@lumx/react/utils/className';
11
11
  import { renderLink } from '@lumx/react/utils/react/renderLink';
12
12
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
13
+ import { useDisableStateProps } from '@lumx/react/utils/disabled/useDisableStateProps';
14
+ import { HasAriaDisabled } from '@lumx/react/utils/type/HasAriaDisabled';
13
15
 
14
16
  type HTMLButtonProps = DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>;
15
17
 
@@ -20,8 +22,9 @@ export type ButtonSize = Extract<Size, 's' | 'm'>;
20
22
 
21
23
  export interface BaseButtonProps
22
24
  extends GenericProps,
23
- Pick<AriaAttributes, 'aria-expanded' | 'aria-haspopup' | 'aria-pressed' | 'aria-label' | 'aria-disabled'>,
24
- HasTheme {
25
+ Pick<AriaAttributes, 'aria-expanded' | 'aria-haspopup' | 'aria-pressed' | 'aria-label'>,
26
+ HasTheme,
27
+ HasAriaDisabled {
25
28
  /** Color variant. */
26
29
  color?: ColorPalette;
27
30
  /** Emphasis variant. */
@@ -96,17 +99,15 @@ const renderButtonWrapper: React.FC<ButtonRootProps> = (props) => {
96
99
  * @return React element.
97
100
  */
98
101
  export const ButtonRoot = forwardRef<ButtonRootProps, HTMLButtonElement | HTMLAnchorElement>((props, ref) => {
102
+ const { isAnyDisabled, disabledStateProps, otherProps } = useDisableStateProps(props);
99
103
  const {
100
104
  'aria-label': ariaLabel,
101
105
  children,
102
106
  className,
103
107
  color,
104
- disabled,
105
108
  emphasis,
106
109
  hasBackground,
107
110
  href,
108
- isDisabled = disabled,
109
- 'aria-disabled': ariaDisabled = isDisabled,
110
111
  isSelected,
111
112
  isActive,
112
113
  isFocused,
@@ -120,7 +121,7 @@ export const ButtonRoot = forwardRef<ButtonRootProps, HTMLButtonElement | HTMLAn
120
121
  type = 'button',
121
122
  fullWidth,
122
123
  ...forwardedProps
123
- } = props;
124
+ } = otherProps;
124
125
 
125
126
  const adaptedColor =
126
127
  color ||
@@ -138,7 +139,7 @@ export const ButtonRoot = forwardRef<ButtonRootProps, HTMLButtonElement | HTMLAn
138
139
  color: adaptedColor,
139
140
  emphasis,
140
141
  isSelected,
141
- isDisabled,
142
+ isDisabled: isAnyDisabled,
142
143
  isActive,
143
144
  isFocused,
144
145
  isHovered,
@@ -156,7 +157,7 @@ export const ButtonRoot = forwardRef<ButtonRootProps, HTMLButtonElement | HTMLAn
156
157
  *
157
158
  * However, in any case, if the component is disabled, we returned a <button> since disabled is not compatible with <a>.
158
159
  */
159
- if ((linkAs || !isEmpty(props.href)) && !isDisabled) {
160
+ if ((linkAs || !isEmpty(props.href)) && !isAnyDisabled) {
160
161
  return renderLink(
161
162
  {
162
163
  linkAs,
@@ -173,9 +174,7 @@ export const ButtonRoot = forwardRef<ButtonRootProps, HTMLButtonElement | HTMLAn
173
174
  return (
174
175
  <button
175
176
  {...forwardedProps}
176
- {...(ariaDisabled ? { onClick: undefined } : undefined)}
177
- disabled={isDisabled}
178
- aria-disabled={ariaDisabled}
177
+ {...disabledStateProps}
179
178
  aria-label={ariaLabel}
180
179
  ref={ref as RefObject<HTMLButtonElement>}
181
180
  className={buttonClassName}
@@ -1,6 +1,7 @@
1
1
  import { Checkbox } from '@lumx/react';
2
2
  import { withValueOnChange } from '@lumx/react/stories/decorators/withValueOnChange';
3
3
  import { loremIpsum } from '@lumx/react/stories/utils/lorem';
4
+ import { withCombinations } from '@lumx/react/stories/decorators/withCombinations';
4
5
 
5
6
  export default {
6
7
  title: 'LumX components/checkbox/Checkbox',
@@ -47,7 +48,17 @@ export const IntermediateState = {
47
48
  */
48
49
  export const Disabled = {
49
50
  args: {
50
- ...LabelAndHelper.args,
51
- isDisabled: true,
51
+ label: 'Checkbox label',
52
+ helper: 'Checkbox is disabled because...',
52
53
  },
54
+ decorators: [
55
+ withCombinations({
56
+ combinations: {
57
+ rows: {
58
+ disabled: { disabled: true },
59
+ 'aria-disabled': { 'aria-disabled': true },
60
+ },
61
+ },
62
+ }),
63
+ ],
53
64
  };
@@ -112,6 +112,35 @@ describe(`<${Checkbox.displayName}>`, () => {
112
112
  });
113
113
  });
114
114
 
115
+ describe('Disabled state', () => {
116
+ it('should be disabled with isDisabled', async () => {
117
+ const onChange = jest.fn();
118
+ const { checkbox, input } = setup({ isDisabled: true, onChange });
119
+
120
+ expect(checkbox).toHaveClass('lumx-checkbox--is-disabled');
121
+ expect(input).toBeDisabled();
122
+
123
+ // Should not trigger onChange.
124
+ await userEvent.click(input);
125
+ expect(onChange).not.toHaveBeenCalled();
126
+ });
127
+
128
+ it('should be disabled with aria-disabled', async () => {
129
+ const onChange = jest.fn();
130
+ const { checkbox, input } = setup({ 'aria-disabled': true, onChange });
131
+
132
+ expect(checkbox).toHaveClass('lumx-checkbox--is-disabled');
133
+ // Note: input is not disabled (so it can be focused) but it's readOnly.
134
+ expect(input).not.toBeDisabled();
135
+ expect(input).toHaveAttribute('aria-disabled', 'true');
136
+ expect(input).toHaveAttribute('readOnly');
137
+
138
+ // Should not trigger onChange.
139
+ await userEvent.click(input);
140
+ expect(onChange).not.toHaveBeenCalled();
141
+ });
142
+ });
143
+
115
144
  // Common tests suite.
116
145
  commonTestsSuiteRTL(setup, {
117
146
  baseClassName: CLASSNAME,
@@ -11,6 +11,8 @@ import { useId } from '@lumx/react/hooks/useId';
11
11
  import { useMergeRefs } from '@lumx/react/utils/react/mergeRefs';
12
12
  import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
13
13
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
14
+ import { useDisableStateProps } from '@lumx/react/utils/disabled/useDisableStateProps';
15
+ import { HasAriaDisabled } from '@lumx/react/utils/type/HasAriaDisabled';
14
16
 
15
17
  /**
16
18
  * Intermediate state of checkbox.
@@ -20,7 +22,7 @@ const INTERMEDIATE_STATE = 'intermediate';
20
22
  /**
21
23
  * Defines the props of the component.
22
24
  */
23
- export interface CheckboxProps extends GenericProps, HasTheme {
25
+ export interface CheckboxProps extends GenericProps, HasTheme, HasAriaDisabled {
24
26
  /** Helper text. */
25
27
  helper?: string;
26
28
  /** Native input id property. */
@@ -66,16 +68,15 @@ const DEFAULT_PROPS: Partial<CheckboxProps> = {};
66
68
  * @return React element.
67
69
  */
68
70
  export const Checkbox = forwardRef<CheckboxProps, HTMLDivElement>((props, ref) => {
71
+ const { isAnyDisabled, disabledStateProps, otherProps } = useDisableStateProps(props);
69
72
  const defaultTheme = useTheme() || Theme.light;
70
73
  const {
71
74
  checked,
72
75
  className,
73
- disabled,
74
76
  helper,
75
77
  id,
76
78
  inputRef,
77
79
  isChecked = checked,
78
- isDisabled = disabled,
79
80
  label,
80
81
  name,
81
82
  onChange,
@@ -83,7 +84,7 @@ export const Checkbox = forwardRef<CheckboxProps, HTMLDivElement>((props, ref) =
83
84
  value,
84
85
  inputProps = {},
85
86
  ...forwardedProps
86
- } = props;
87
+ } = otherProps;
87
88
  const localInputRef = React.useRef<HTMLInputElement>(null);
88
89
  const generatedInputId = useId();
89
90
  const inputId = id || generatedInputId;
@@ -110,7 +111,7 @@ export const Checkbox = forwardRef<CheckboxProps, HTMLDivElement>((props, ref) =
110
111
  handleBasicClasses({
111
112
  // Whether state is intermediate class name will "-checked"
112
113
  isChecked: intermediateState ? true : isChecked,
113
- isDisabled,
114
+ isDisabled: isAnyDisabled,
114
115
  isUnchecked: !isChecked,
115
116
  prefix: CLASSNAME,
116
117
  theme,
@@ -123,14 +124,14 @@ export const Checkbox = forwardRef<CheckboxProps, HTMLDivElement>((props, ref) =
123
124
  type="checkbox"
124
125
  id={inputId}
125
126
  className={`${CLASSNAME}__input-native`}
126
- disabled={isDisabled}
127
- tabIndex={isDisabled ? -1 : 0}
127
+ {...disabledStateProps}
128
128
  name={name}
129
129
  value={value}
130
130
  checked={isChecked}
131
131
  onChange={handleChange}
132
132
  aria-describedby={helper ? `${inputId}-helper` : undefined}
133
133
  aria-checked={intermediateState ? 'mixed' : Boolean(isChecked)}
134
+ readOnly={inputProps.readOnly || disabledStateProps['aria-disabled']}
134
135
  {...inputProps}
135
136
  />
136
137
 
@@ -147,3 +147,20 @@ export const Theming = {
147
147
  }),
148
148
  ],
149
149
  };
150
+
151
+ /**
152
+ * Test Chip disabled states
153
+ */
154
+ export const Disabled = {
155
+ args: {
156
+ children: 'Chip label',
157
+ },
158
+ decorators: [
159
+ withCombinations({
160
+ combinations: {
161
+ rows: { disabled: { disabled: true }, 'aria-disabled': { 'aria-disabled': true } },
162
+ cols: { button: { onClick: () => {} }, link: { href: 'https://example.com' } },
163
+ },
164
+ }),
165
+ ],
166
+ };
@@ -185,6 +185,50 @@ describe('<Chip />', () => {
185
185
  });
186
186
  });
187
187
 
188
+ describe('Disabled state', () => {
189
+ it('should render disabled chip button', async () => {
190
+ const onClick = jest.fn();
191
+ const { chip } = setup({ children: 'Label', isDisabled: true, onClick });
192
+ expect(chip).toHaveAttribute('aria-disabled', 'true');
193
+ await userEvent.click(chip);
194
+ expect(onClick).not.toHaveBeenCalled();
195
+ });
196
+
197
+ it('should render disabled chip link', async () => {
198
+ const onClick = jest.fn();
199
+ const { chip } = setup({ children: 'Label', isDisabled: true, href: 'https://example.com', onClick });
200
+ // Disabled link should not have an href.
201
+ expect(chip).not.toHaveAttribute('href');
202
+ expect(chip).toHaveAttribute('aria-disabled', 'true');
203
+ await userEvent.click(chip);
204
+ expect(onClick).not.toHaveBeenCalled();
205
+ });
206
+
207
+ it('should render aria-disabled chip button', async () => {
208
+ const onClick = jest.fn();
209
+ const { chip } = setup({ children: 'Label', 'aria-disabled': true, onClick });
210
+ expect(chip).toHaveAttribute('aria-disabled', 'true');
211
+ await userEvent.click(chip);
212
+ // userEvent doesn't dispatch click on aria-disabled elements, but we check just in case.
213
+ expect(onClick).not.toHaveBeenCalled();
214
+ });
215
+
216
+ it('should render aria-disabled chip link', async () => {
217
+ const onClick = jest.fn();
218
+ const { chip } = setup({
219
+ children: 'Label',
220
+ 'aria-disabled': true,
221
+ href: 'https://example.com',
222
+ onClick,
223
+ });
224
+ expect(chip).toHaveAttribute('href', 'https://example.com');
225
+ expect(chip).toHaveAttribute('aria-disabled', 'true');
226
+ await userEvent.click(chip);
227
+ // userEvent doesn't dispatch click on aria-disabled elements, but we check just in case.
228
+ expect(onClick).not.toHaveBeenCalled();
229
+ });
230
+ });
231
+
188
232
  commonTestsSuiteRTL(setup, {
189
233
  baseClassName: CLASSNAME,
190
234
  forwardClassName: 'chip',
@@ -11,6 +11,8 @@ import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/classNam
11
11
  import { onEnterPressed } from '@lumx/react/utils/browser/event';
12
12
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
13
13
  import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
14
+ import { useDisableStateProps } from '@lumx/react/utils/disabled/useDisableStateProps';
15
+ import { HasAriaDisabled } from '@lumx/react/utils/type/HasAriaDisabled';
14
16
 
15
17
  /**
16
18
  * Chip sizes.
@@ -20,7 +22,7 @@ type ChipSize = Extract<Size, 's' | 'm'>;
20
22
  /**
21
23
  * Defines the props of the component.
22
24
  */
23
- export interface ChipProps extends GenericProps, HasTheme {
25
+ export interface ChipProps extends GenericProps, HasTheme, HasAriaDisabled {
24
26
  /** A component to be rendered after the content. */
25
27
  after?: ReactNode;
26
28
  /** A component to be rendered before the content. */
@@ -71,15 +73,14 @@ const DEFAULT_PROPS: Partial<ChipProps> = {
71
73
  */
72
74
  export const Chip = forwardRef<ChipProps, HTMLAnchorElement>((props, ref) => {
73
75
  const defaultTheme = useTheme() || Theme.light;
76
+ const { isAnyDisabled, disabledStateProps, otherProps } = useDisableStateProps(props);
74
77
  const {
75
78
  after,
76
79
  before,
77
80
  children,
78
81
  className,
79
82
  color,
80
- disabled,
81
83
  isClickable: propIsClickable,
82
- isDisabled = disabled,
83
84
  isHighlighted,
84
85
  isSelected,
85
86
  onAfterClick,
@@ -90,10 +91,10 @@ export const Chip = forwardRef<ChipProps, HTMLAnchorElement>((props, ref) => {
90
91
  href,
91
92
  onKeyDown,
92
93
  ...forwardedProps
93
- } = props;
94
+ } = otherProps;
94
95
  const hasAfterClick = isFunction(onAfterClick);
95
96
  const hasBeforeClick = isFunction(onBeforeClick);
96
- const hasOnClick = isFunction(onClick);
97
+ const hasOnClick = isFunction(props.onClick);
97
98
  const isButton = hasOnClick && !href;
98
99
  const isClickable = Boolean(hasOnClick) || Boolean(href) || propIsClickable;
99
100
 
@@ -113,16 +114,16 @@ export const Chip = forwardRef<ChipProps, HTMLAnchorElement>((props, ref) => {
113
114
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
114
115
  <a
115
116
  role={isButton ? 'button' : undefined}
116
- tabIndex={isClickable ? 0 : undefined}
117
+ tabIndex={isClickable && !disabledStateProps.disabled ? 0 : undefined}
117
118
  {...forwardedProps}
118
- href={href}
119
+ href={!disabledStateProps.disabled ? href : undefined}
119
120
  ref={ref}
120
121
  className={classNames(
121
122
  className,
122
123
  handleBasicClasses({
123
124
  clickable: isClickable,
124
125
  color: chipColor,
125
- isDisabled,
126
+ isDisabled: isAnyDisabled,
126
127
  hasAfter: Boolean(after),
127
128
  hasBefore: Boolean(before),
128
129
  highlighted: Boolean(isHighlighted),
@@ -132,7 +133,7 @@ export const Chip = forwardRef<ChipProps, HTMLAnchorElement>((props, ref) => {
132
133
  unselected: Boolean(!isSelected),
133
134
  }),
134
135
  )}
135
- aria-disabled={(isClickable && isDisabled) || undefined}
136
+ aria-disabled={(isClickable && isAnyDisabled) || undefined}
136
137
  onClick={hasOnClick ? onClick : undefined}
137
138
  onKeyDown={handleKeyDown}
138
139
  >
@@ -2,6 +2,7 @@ import { DatePickerField } from '@lumx/react';
2
2
  import { withValueOnChange } from '@lumx/react/stories/decorators/withValueOnChange';
3
3
  import { withNestedProps } from '@lumx/react/stories/decorators/withNestedProps';
4
4
  import { loremIpsum } from '@lumx/react/stories/utils/lorem';
5
+ import { withCombinations } from '@lumx/react/stories/decorators/withCombinations';
5
6
 
6
7
  export default {
7
8
  title: 'LumX components/date-picker/DatePickerField',
@@ -80,3 +81,20 @@ export const DefaultMonth = {
80
81
  defaultMonth: new Date('2019-07-14'),
81
82
  },
82
83
  };
84
+
85
+ /**
86
+ * Disabled states
87
+ */
88
+ export const Disabled = {
89
+ args: DefaultValue.args,
90
+ decorators: [
91
+ withCombinations({
92
+ combinations: {
93
+ rows: {
94
+ disabled: { disabled: true },
95
+ 'aria-disabled': { 'aria-disabled': true },
96
+ },
97
+ },
98
+ }),
99
+ ],
100
+ };
@@ -5,6 +5,7 @@ import { GenericProps } from '@lumx/react/utils/type';
5
5
  import { getCurrentLocale } from '@lumx/react/utils/locale/getCurrentLocale';
6
6
  import { useBooleanState } from '@lumx/react/hooks/useBooleanState';
7
7
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
8
+ import { useDisableStateProps } from '@lumx/react/utils/disabled/useDisableStateProps';
8
9
 
9
10
  /**
10
11
  * Defines the props of the component.
@@ -42,10 +43,9 @@ const COMPONENT_NAME = 'DatePickerField';
42
43
  * @return React element.
43
44
  */
44
45
  export const DatePickerField = forwardRef<DatePickerFieldProps, HTMLDivElement>((props, ref) => {
46
+ const { disabledStateProps, otherProps } = useDisableStateProps(props);
45
47
  const {
46
48
  defaultMonth,
47
- disabled,
48
- isDisabled = disabled,
49
49
  locale = getCurrentLocale(),
50
50
  maxDate,
51
51
  minDate,
@@ -55,7 +55,7 @@ export const DatePickerField = forwardRef<DatePickerFieldProps, HTMLDivElement>(
55
55
  previousButtonProps,
56
56
  value,
57
57
  ...forwardedProps
58
- } = props;
58
+ } = otherProps;
59
59
  const anchorRef = useRef(null);
60
60
 
61
61
  const [isOpen, close, , toggleOpen] = useBooleanState(false);
@@ -101,7 +101,7 @@ export const DatePickerField = forwardRef<DatePickerFieldProps, HTMLDivElement>(
101
101
  onClick={toggleOpen}
102
102
  onChange={onTextFieldChange}
103
103
  onKeyPress={handleKeyboardNav}
104
- isDisabled={isDisabled}
104
+ {...disabledStateProps}
105
105
  readOnly
106
106
  aria-haspopup="dialog"
107
107
  />
@@ -76,13 +76,15 @@ export const WithCustomizableTypography = {
76
76
  export const AllStates = {
77
77
  argTypes: {
78
78
  isDisabled: { control: false },
79
+ onClick: { action: true },
79
80
  },
80
81
  decorators: [
81
82
  withThemedBackground(),
82
83
  withCombinations({
83
84
  combinations: {
84
85
  sections: {
85
- Default: {},
86
+ Default: { href: '#' },
87
+ 'As button': {},
86
88
  'with icon': {
87
89
  children: ['Link', <Icon key="icon" icon={mdiEarth} />, 'with icon'],
88
90
  },
@@ -91,6 +93,7 @@ export const AllStates = {
91
93
  Default: {},
92
94
  Disabled: { isDisabled: true },
93
95
  Focused: { 'data-focus-visible-added': true },
96
+ 'ARIA Disabled': { 'aria-disabled': true },
94
97
  Hovered: { 'data-lumx-hover': true },
95
98
  },
96
99
  rows: {