reshaped 3.4.6 → 3.5.0-rc.0

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 (76) hide show
  1. package/CHANGELOG.md +0 -13
  2. package/dist/bundle.css +1 -1
  3. package/dist/bundle.d.ts +2 -0
  4. package/dist/bundle.js +11 -11
  5. package/dist/components/Autocomplete/Autocomplete.js +25 -8
  6. package/dist/components/Autocomplete/Autocomplete.module.css +1 -0
  7. package/dist/components/Autocomplete/tests/Autocomplete.stories.d.ts +14 -1
  8. package/dist/components/Autocomplete/tests/Autocomplete.stories.js +181 -64
  9. package/dist/components/Badge/Badge.types.d.ts +4 -2
  10. package/dist/components/Button/Button.types.d.ts +4 -2
  11. package/dist/components/Calendar/Calendar.module.css +1 -1
  12. package/dist/components/Checkbox/Checkbox.js +17 -4
  13. package/dist/components/Checkbox/Checkbox.module.css +1 -1
  14. package/dist/components/Checkbox/Checkbox.types.d.ts +1 -0
  15. package/dist/components/Checkbox/tests/Checkbox.stories.d.ts +1 -0
  16. package/dist/components/Checkbox/tests/Checkbox.stories.js +40 -0
  17. package/dist/components/Dismissible/Dismissible.js +1 -0
  18. package/dist/components/DropdownMenu/DropdownMenu.js +1 -1
  19. package/dist/components/FormControl/FormControlLabel.js +2 -7
  20. package/dist/components/MenuItem/MenuItem.types.d.ts +4 -2
  21. package/dist/components/NumberField/NumberField.d.ts +6 -0
  22. package/dist/components/NumberField/NumberField.js +11 -0
  23. package/dist/components/NumberField/NumberField.module.css +1 -0
  24. package/dist/components/NumberField/NumberField.types.d.ts +19 -0
  25. package/dist/components/NumberField/NumberField.types.js +1 -0
  26. package/dist/components/NumberField/NumberFieldControlled.d.ts +6 -0
  27. package/dist/components/NumberField/NumberFieldControlled.js +162 -0
  28. package/dist/components/NumberField/NumberFieldUncontrolled.d.ts +6 -0
  29. package/dist/components/NumberField/NumberFieldUncontrolled.js +16 -0
  30. package/dist/components/NumberField/index.d.ts +2 -0
  31. package/dist/components/NumberField/index.js +1 -0
  32. package/dist/components/NumberField/tests/NumberField.stories.d.ts +30 -0
  33. package/dist/components/NumberField/tests/NumberField.stories.js +240 -0
  34. package/dist/components/Overlay/Overlay.js +2 -2
  35. package/dist/components/PinField/PinField.module.css +1 -1
  36. package/dist/components/PinField/PinField.types.d.ts +1 -1
  37. package/dist/components/PinField/PinFieldControlled.js +1 -0
  38. package/dist/components/PinField/tests/PinField.stories.js +8 -0
  39. package/dist/components/Radio/Radio.js +11 -4
  40. package/dist/components/Radio/Radio.module.css +1 -1
  41. package/dist/components/Radio/Radio.types.d.ts +1 -0
  42. package/dist/components/Radio/tests/Radio.stories.d.ts +1 -0
  43. package/dist/components/Radio/tests/Radio.stories.js +31 -0
  44. package/dist/components/Select/Select.module.css +1 -1
  45. package/dist/components/Select/Select.types.d.ts +1 -1
  46. package/dist/components/Select/tests/Select.stories.js +42 -16
  47. package/dist/components/Switch/Switch.js +10 -4
  48. package/dist/components/Switch/Switch.module.css +1 -1
  49. package/dist/components/Switch/Switch.types.d.ts +1 -1
  50. package/dist/components/Switch/tests/Switch.stories.js +30 -15
  51. package/dist/components/TextField/TextField.js +1 -1
  52. package/dist/components/TextField/TextField.module.css +1 -1
  53. package/dist/components/TextField/TextField.types.d.ts +2 -2
  54. package/dist/components/TextField/index.d.ts +1 -1
  55. package/dist/components/TextField/tests/TextField.stories.js +4 -0
  56. package/dist/components/Toast/ToastContainer.js +2 -2
  57. package/dist/components/_private/Flyout/FlyoutControlled.js +2 -2
  58. package/dist/hooks/tests/useScrollLock.stories.d.ts +1 -0
  59. package/dist/hooks/tests/useScrollLock.stories.js +29 -0
  60. package/dist/icons/ChevronUp.d.ts +2 -0
  61. package/dist/icons/ChevronUp.js +5 -0
  62. package/dist/icons/Minus.d.ts +2 -0
  63. package/dist/icons/Minus.js +3 -0
  64. package/dist/icons/Plus.d.ts +2 -2
  65. package/dist/icons/Plus.js +2 -2
  66. package/dist/index.d.ts +2 -0
  67. package/dist/index.js +1 -0
  68. package/dist/utilities/a11y/TrapFocus.d.ts +3 -17
  69. package/dist/utilities/a11y/TrapFocus.js +54 -49
  70. package/dist/utilities/a11y/tests/TrapFocus.stories.js +20 -20
  71. package/dist/utilities/scroll/lock.js +15 -7
  72. package/dist/utilities/scroll/lockStandard.d.ts +1 -2
  73. package/dist/utilities/scroll/lockStandard.js +2 -9
  74. package/package.json +31 -31
  75. package/dist/components/Autocomplete/tests/Autocomplete.test.stories.d.ts +0 -27
  76. package/dist/components/Autocomplete/tests/Autocomplete.test.stories.js +0 -86
@@ -1,3 +1,4 @@
1
+ var _a;
1
2
  import Chain from "../Chain.js";
2
3
  import * as keys from "../../constants/keys.js";
3
4
  import TrapScreenReader from "./TrapScreenReader.js";
@@ -6,26 +7,25 @@ import { getShadowRoot } from "../dom/index.js";
6
7
  import { checkKeyboardMode } from "./keyboardMode.js";
7
8
  class TrapFocus {
8
9
  static chain = new Chain();
9
- chainId;
10
- root;
11
- trigger = null;
12
- options = {};
10
+ #chainId;
11
+ #root = null;
12
+ #trigger = null;
13
+ #options = {};
13
14
  trapped;
14
- screenReaderTrap;
15
- mutationObserver = null;
16
- constructor(root) {
17
- this.root = root;
18
- this.screenReaderTrap = new TrapScreenReader(root);
19
- }
15
+ #screenReaderTrap = null;
16
+ #mutationObserver = null;
17
+ constructor() { }
20
18
  /**
21
19
  * Handle keyboard navigation while focus is trapped
22
20
  */
23
- handleKeyDown = (event) => {
21
+ #handleKeyDown = (event) => {
24
22
  if (event.defaultPrevented)
25
23
  return;
26
- if (TrapFocus.chain.tailId !== this.chainId)
24
+ if (_a.chain.tailId !== this.#chainId)
27
25
  return;
28
- const { mode, onRelease, pseudoFocus, includeTrigger } = this.options;
26
+ if (!this.#root)
27
+ return;
28
+ const { mode, onRelease, pseudoFocus, includeTrigger } = this.#options;
29
29
  let navigationMode = "tabs";
30
30
  if (mode === "action-menu" || mode === "selection-menu" || mode === "action-bar") {
31
31
  navigationMode = "arrows";
@@ -39,12 +39,12 @@ class TrapFocus {
39
39
  const isNextArrow = navigationMode === "arrows" && key === (mode === "action-bar" ? keys.RIGHT : keys.DOWN);
40
40
  const isPrev = (isPrevTab && navigationMode === "tabs") || isPrevArrow;
41
41
  const isNext = (isNextTab && navigationMode === "tabs") || isNextArrow;
42
- const isFocusedOnTrigger = getActiveElement(this.root) === this.trigger;
42
+ const isFocusedOnTrigger = getActiveElement(this.#root) === this.#trigger;
43
43
  const focusData = getFocusData({
44
- root: this.root,
44
+ root: this.#root,
45
45
  target: isPrev ? "prev" : "next",
46
46
  options: {
47
- additionalElement: includeTrigger ? this.trigger : undefined,
47
+ additionalElement: includeTrigger ? this.#trigger : undefined,
48
48
  circular: mode !== "action-menu" && mode !== "action-bar",
49
49
  },
50
50
  });
@@ -70,53 +70,57 @@ class TrapFocus {
70
70
  return;
71
71
  focusElement(focusData.el, { pseudoFocus });
72
72
  };
73
- addListeners = () => {
74
- const shadowRoot = getShadowRoot(this.root);
73
+ #addListeners = () => {
74
+ const shadowRoot = getShadowRoot(this.#root);
75
75
  const el = shadowRoot ?? document;
76
- el.addEventListener("keydown", this.handleKeyDown);
76
+ el.addEventListener("keydown", this.#handleKeyDown);
77
77
  };
78
- removeListeners = () => {
79
- const shadowRoot = getShadowRoot(this.root);
78
+ #removeListeners = () => {
79
+ const shadowRoot = getShadowRoot(this.#root);
80
80
  const el = shadowRoot ?? document;
81
- el.removeEventListener("keydown", this.handleKeyDown);
81
+ el.removeEventListener("keydown", this.#handleKeyDown);
82
82
  };
83
83
  /**
84
84
  * Trap the focus, add observer and keyboard event listeners
85
85
  * and create a chain item
86
86
  */
87
- trap = (options = {}) => {
87
+ trap = (root, options = {}) => {
88
88
  const { mode = "dialog", includeTrigger, initialFocusEl } = options;
89
- const trigger = getActiveElement(this.root);
90
- const focusable = getFocusableElements(this.root, {
89
+ this.#root = root;
90
+ this.#screenReaderTrap = new TrapScreenReader(root);
91
+ const trigger = getActiveElement(this.#root);
92
+ const focusable = getFocusableElements(this.#root, {
91
93
  additionalElement: includeTrigger ? trigger : undefined,
92
94
  });
93
95
  const pseudoFocus = mode === "selection-menu";
94
- this.options = { ...options, pseudoFocus };
95
- this.trigger = trigger;
96
- this.mutationObserver = new MutationObserver(() => {
97
- const currentActiveElement = getActiveElement(this.root);
96
+ this.#options = { ...options, pseudoFocus };
97
+ this.#trigger = trigger;
98
+ this.#mutationObserver = new MutationObserver(() => {
99
+ if (!this.#root)
100
+ return;
101
+ const currentActiveElement = getActiveElement(this.#root);
98
102
  // Focus stayed inside the wrapper, no need to refocus
99
- if (this.root.contains(currentActiveElement))
103
+ if (this.#root.contains(currentActiveElement))
100
104
  return;
101
- const focusable = getFocusableElements(this.root, {
105
+ const focusable = getFocusableElements(this.#root, {
102
106
  additionalElement: includeTrigger ? trigger : undefined,
103
107
  });
104
108
  if (!focusable.length)
105
109
  return;
106
110
  focusElement(focusable[0], { pseudoFocus });
107
111
  });
108
- this.removeListeners();
109
- this.mutationObserver.observe(this.root, { childList: true, subtree: true });
112
+ this.#removeListeners();
113
+ this.#mutationObserver.observe(this.#root, { childList: true, subtree: true });
110
114
  // Don't trap in case there is nothing to focus inside
111
115
  if (!focusable.length && !initialFocusEl)
112
116
  return;
113
- this.addListeners();
117
+ this.#addListeners();
114
118
  if (mode === "dialog")
115
- this.screenReaderTrap.trap();
119
+ this.#screenReaderTrap.trap();
116
120
  // Don't add back to the chain if we're traversing back
117
- const tailItem = TrapFocus.chain.tailId && TrapFocus.chain.get(TrapFocus.chain.tailId);
118
- if (!tailItem || this.root !== tailItem.data.root) {
119
- this.chainId = TrapFocus.chain.add(this);
121
+ const tailItem = _a.chain.tailId && _a.chain.get(_a.chain.tailId);
122
+ if (!tailItem || this.#root !== tailItem.data.#root) {
123
+ this.#chainId = _a.chain.add(this);
120
124
  focusElement(initialFocusEl || focusable[0], { pseudoFocus });
121
125
  }
122
126
  this.trapped = true;
@@ -127,21 +131,22 @@ class TrapFocus {
127
131
  */
128
132
  release = (releaseOptions = {}) => {
129
133
  const { withoutFocusReturn } = releaseOptions;
130
- if (!this.trapped || !this.chainId)
134
+ if (!this.trapped || !this.#chainId || !this.#root)
131
135
  return;
132
136
  this.trapped = false;
133
- if (this.trigger && !withoutFocusReturn) {
134
- this.trigger.focus({ preventScroll: !checkKeyboardMode() });
137
+ if (this.#trigger && !withoutFocusReturn) {
138
+ this.#trigger.focus({ preventScroll: !checkKeyboardMode() });
135
139
  }
136
- TrapFocus.chain.removePreviousTill(this.chainId, (item) => document.body.contains(item.data.trigger));
137
- this.mutationObserver?.disconnect();
138
- this.removeListeners();
139
- this.screenReaderTrap.release();
140
- const previousItem = TrapFocus.chain.tailId && TrapFocus.chain.get(TrapFocus.chain.tailId);
141
- if (previousItem) {
142
- const trapInstance = new TrapFocus(previousItem.data.root);
143
- trapInstance.trap(previousItem.data.options);
140
+ _a.chain.removePreviousTill(this.#chainId, (item) => document.body.contains(item.data.#trigger));
141
+ this.#mutationObserver?.disconnect();
142
+ this.#removeListeners();
143
+ this.#screenReaderTrap?.release();
144
+ const previousItem = _a.chain.tailId && _a.chain.get(_a.chain.tailId);
145
+ if (previousItem && previousItem.data.#root) {
146
+ const trapInstance = new _a();
147
+ trapInstance.trap(previousItem.data.#root, previousItem.data.#options);
144
148
  }
145
149
  };
146
150
  }
151
+ _a = TrapFocus;
147
152
  export default TrapFocus;
@@ -34,8 +34,8 @@ export const modeDialog = {
34
34
  return;
35
35
  if (!rootRef.current)
36
36
  return;
37
- const trapFocus = new TrapFocus(rootRef.current);
38
- trapFocus.trap({ mode: "dialog" });
37
+ const trapFocus = new TrapFocus();
38
+ trapFocus.trap(rootRef.current, { mode: "dialog" });
39
39
  return () => trapFocus.release();
40
40
  }, [trapToggle.active]);
41
41
  return (<Example>
@@ -92,8 +92,8 @@ export const modeActionMenu = {
92
92
  return;
93
93
  if (!rootRef.current)
94
94
  return;
95
- const trapFocus = new TrapFocus(rootRef.current);
96
- trapFocus.trap({
95
+ const trapFocus = new TrapFocus();
96
+ trapFocus.trap(rootRef.current, {
97
97
  mode: "action-menu",
98
98
  onRelease: () => {
99
99
  trapToggle.deactivate();
@@ -154,8 +154,8 @@ export const modeActionBar = {
154
154
  return;
155
155
  if (!rootRef.current)
156
156
  return;
157
- const trapFocus = new TrapFocus(rootRef.current);
158
- trapFocus.trap({
157
+ const trapFocus = new TrapFocus();
158
+ trapFocus.trap(rootRef.current, {
159
159
  mode: "action-bar",
160
160
  onRelease: () => {
161
161
  trapToggle.deactivate();
@@ -216,8 +216,8 @@ export const modeContentMenu = {
216
216
  return;
217
217
  if (!rootRef.current)
218
218
  return;
219
- const trapFocus = new TrapFocus(rootRef.current);
220
- trapFocus.trap({
219
+ const trapFocus = new TrapFocus();
220
+ trapFocus.trap(rootRef.current, {
221
221
  mode: "content-menu",
222
222
  onRelease: () => {
223
223
  trapToggle.deactivate();
@@ -279,8 +279,8 @@ export const includeTrigger = {
279
279
  return;
280
280
  if (!rootRef.current)
281
281
  return;
282
- const trapFocus = new TrapFocus(rootRef.current);
283
- trapFocus.trap({
282
+ const trapFocus = new TrapFocus();
283
+ trapFocus.trap(rootRef.current, {
284
284
  mode: "dialog",
285
285
  includeTrigger: true,
286
286
  });
@@ -327,8 +327,8 @@ export const initialFocusEl = {
327
327
  return;
328
328
  if (!rootRef.current)
329
329
  return;
330
- const trapFocus = new TrapFocus(rootRef.current);
331
- trapFocus.trap({
330
+ const trapFocus = new TrapFocus();
331
+ trapFocus.trap(rootRef.current, {
332
332
  mode: "dialog",
333
333
  initialFocusEl: initialFocusRef.current,
334
334
  });
@@ -388,8 +388,8 @@ export const focusableElements = {
388
388
  return;
389
389
  if (!rootRef.current)
390
390
  return;
391
- const trapFocus = new TrapFocus(rootRef.current);
392
- trapFocus.trap();
391
+ const trapFocus = new TrapFocus();
392
+ trapFocus.trap(rootRef.current);
393
393
  return () => trapFocus.release();
394
394
  }, [trapToggle.active]);
395
395
  return (<Example>
@@ -498,8 +498,8 @@ export const nested = {
498
498
  return;
499
499
  if (!rootRef.current)
500
500
  return;
501
- const trapFocus = new TrapFocus(rootRef.current);
502
- trapFocus.trap({
501
+ const trapFocus = new TrapFocus();
502
+ trapFocus.trap(rootRef.current, {
503
503
  mode: "dialog",
504
504
  });
505
505
  return () => trapFocus.release();
@@ -509,8 +509,8 @@ export const nested = {
509
509
  return;
510
510
  if (!innerRootRef.current)
511
511
  return;
512
- const trapFocus = new TrapFocus(innerRootRef.current);
513
- trapFocus.trap({
512
+ const trapFocus = new TrapFocus();
513
+ trapFocus.trap(innerRootRef.current, {
514
514
  mode: "dialog",
515
515
  });
516
516
  return () => trapFocus.release();
@@ -567,8 +567,8 @@ export const mutationObserver = {
567
567
  return;
568
568
  if (!rootRef.current)
569
569
  return;
570
- const trapFocus = new TrapFocus(rootRef.current);
571
- trapFocus.trap({
570
+ const trapFocus = new TrapFocus();
571
+ trapFocus.trap(rootRef.current, {
572
572
  mode: "dialog",
573
573
  });
574
574
  setTimeout(() => {
@@ -1,23 +1,31 @@
1
1
  import { isIOS } from "../platform.js";
2
+ import { findClosestRenderContainer } from "../dom/index.js";
2
3
  import lockSafariScroll from "./lockSafari.js";
3
4
  import lockStandardScroll from "./lockStandard.js";
4
- let lockedCount = 0;
5
+ let bodyLockedCount = 0;
5
6
  let reset = () => { };
6
7
  export const lockScroll = (args) => {
7
- lockedCount += 1;
8
- if (lockedCount > 1)
8
+ const isIOSLock = isIOS() && !args.containerEl && !args.originEl;
9
+ let container = document.body;
10
+ if (args.originEl && !isIOSLock)
11
+ container = findClosestRenderContainer({ el: args.originEl }).el;
12
+ if (args.containerEl && !isIOSLock)
13
+ container = args.containerEl;
14
+ if (container === document.body)
15
+ bodyLockedCount += 1;
16
+ if (bodyLockedCount > 1)
9
17
  return;
10
- if (isIOS() && !args.containerEl && !args.originEl) {
18
+ if (isIOSLock) {
11
19
  reset = lockSafariScroll();
12
20
  }
13
21
  else {
14
- reset = lockStandardScroll({ containerEl: args.containerEl, originEl: args.originEl });
22
+ reset = lockStandardScroll({ container });
15
23
  }
16
24
  args.cb?.();
17
25
  };
18
26
  export const unlockScroll = (cb) => {
19
- lockedCount -= 1;
20
- if (lockedCount > 0)
27
+ bodyLockedCount -= 1;
28
+ if (bodyLockedCount > 0)
21
29
  return;
22
30
  reset();
23
31
  cb?.();
@@ -1,5 +1,4 @@
1
1
  declare const lockStandardScroll: (args: {
2
- containerEl?: HTMLElement | null;
3
- originEl?: HTMLElement | null;
2
+ container: HTMLElement;
4
3
  }) => () => void;
5
4
  export default lockStandardScroll;
@@ -1,13 +1,8 @@
1
- import { findClosestRenderContainer } from "../dom/index.js";
2
1
  import { getScrollbarWidth } from "./helpers.js";
3
2
  import { StyleCache } from "../css.js";
4
3
  const styleCache = new StyleCache();
5
4
  const lockStandardScroll = (args) => {
6
- let container = document.body;
7
- if (args.originEl)
8
- container = findClosestRenderContainer({ el: args.originEl }).el;
9
- if (args.containerEl)
10
- container = args.containerEl;
5
+ const { container } = args;
11
6
  const rect = container.getBoundingClientRect();
12
7
  const isOverflowing = rect.left + rect.right < window.innerWidth;
13
8
  styleCache.set(container, { overflow: "hidden" });
@@ -15,8 +10,6 @@ const lockStandardScroll = (args) => {
15
10
  const scrollBarWidth = getScrollbarWidth();
16
11
  styleCache.set(container, { paddingRight: `${scrollBarWidth}px` });
17
12
  }
18
- return () => {
19
- styleCache.reset();
20
- };
13
+ return () => styleCache.reset();
21
14
  };
22
15
  export default lockStandardScroll;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "reshaped",
3
3
  "description": "Professionally crafted design system in React & Figma for building products of any scale and complexity",
4
- "version": "3.4.6",
4
+ "version": "3.5.0-rc.0",
5
5
  "license": "MIT",
6
6
  "email": "hello@reshaped.so",
7
7
  "homepage": "https://reshaped.so",
@@ -98,35 +98,35 @@
98
98
  "@commitlint/cli": "19.8.0",
99
99
  "@commitlint/config-conventional": "19.8.0",
100
100
  "@commitlint/types": "19.8.0",
101
- "@eslint/js": "9.23.0",
101
+ "@eslint/js": "9.25.0",
102
102
  "@size-limit/preset-big-lib": "11.2.0",
103
- "@storybook/addon-a11y": "8.6.9",
104
- "@storybook/addon-actions": "8.6.9",
105
- "@storybook/addon-controls": "8.6.9",
106
- "@storybook/addon-docs": "8.6.9",
107
- "@storybook/addon-storysource": "8.6.9",
108
- "@storybook/experimental-addon-test": "8.6.9",
109
- "@storybook/react": "8.6.9",
110
- "@storybook/react-vite": "8.6.9",
103
+ "@storybook/addon-a11y": "8.6.12",
104
+ "@storybook/addon-actions": "8.6.12",
105
+ "@storybook/addon-controls": "8.6.12",
106
+ "@storybook/addon-docs": "8.6.12",
107
+ "@storybook/addon-storysource": "8.6.12",
108
+ "@storybook/experimental-addon-test": "8.6.12",
109
+ "@storybook/react": "8.6.12",
110
+ "@storybook/react-vite": "8.6.12",
111
111
  "@types/culori": "2.1.1",
112
112
  "@types/events": "3.0.3",
113
- "@types/node": "22.13.13",
114
- "@types/react": "19.0.12",
115
- "@types/react-dom": "19.0.4",
116
- "@vitejs/plugin-react": "4.3.4",
117
- "@vitest/browser": "3.0.9",
118
- "@vitest/coverage-istanbul": "3.0.9",
119
- "@vitest/coverage-v8": "3.0.9",
120
- "chromatic": "11.27.0",
113
+ "@types/node": "22.14.1",
114
+ "@types/react": "19.1.2",
115
+ "@types/react-dom": "19.1.2",
116
+ "@vitejs/plugin-react": "4.4.0",
117
+ "@vitest/browser": "3.1.1",
118
+ "@vitest/coverage-istanbul": "3.1.1",
119
+ "@vitest/coverage-v8": "3.1.1",
120
+ "chromatic": "11.28.2",
121
121
  "cz-conventional-changelog": "3.3.0",
122
- "eslint": "9.23.0",
123
- "eslint-config-prettier": "10.1.1",
122
+ "eslint": "9.25.0",
123
+ "eslint-config-prettier": "10.1.2",
124
124
  "eslint-plugin-jsx-a11y": "6.10.2",
125
- "eslint-plugin-prettier": "5.2.5",
126
- "eslint-plugin-react": "7.37.4",
125
+ "eslint-plugin-prettier": "5.2.6",
126
+ "eslint-plugin-react": "7.37.5",
127
127
  "eslint-plugin-react-hooks": "5.2.0",
128
- "lefthook": "1.11.5",
129
- "playwright": "1.51.1",
128
+ "lefthook": "1.11.10",
129
+ "playwright": "1.52.0",
130
130
  "postcss": "8.5.3",
131
131
  "postcss-cli": "11.0.1",
132
132
  "postcss-each": "1.1.0",
@@ -137,16 +137,16 @@
137
137
  "react-shadow": "20.6.0",
138
138
  "resolve-tspaths": "0.8.23",
139
139
  "size-limit": "11.2.0",
140
- "storybook": "8.6.9",
141
- "stylelint": "16.16.0",
140
+ "storybook": "8.6.12",
141
+ "stylelint": "16.18.0",
142
142
  "stylelint-config-prettier": "9.0.5",
143
- "stylelint-config-standard": "37.0.0",
143
+ "stylelint-config-standard": "38.0.0",
144
144
  "ts-node": "10.9.2",
145
- "typescript": "5.8.2",
146
- "typescript-eslint": "8.28.0",
147
- "vite": "6.2.3",
145
+ "typescript": "5.8.3",
146
+ "typescript-eslint": "8.30.1",
147
+ "vite": "6.3.2",
148
148
  "vite-tsconfig-paths": "5.1.4",
149
- "vitest": "3.0.9",
149
+ "vitest": "3.1.1",
150
150
  "vitest-browser-react": "0.1.1"
151
151
  },
152
152
  "peerDependencies": {
@@ -1,27 +0,0 @@
1
- import React from "react";
2
- import { StoryObj } from "@storybook/react";
3
- import { fn } from "@storybook/test";
4
- declare const _default: {
5
- title: string;
6
- component: {
7
- (props: import("./..").AutocompleteProps): React.JSX.Element;
8
- Item: {
9
- (props: import("../Autocomplete.types").ItemProps): React.JSX.Element;
10
- displayName: string;
11
- };
12
- displayName: string;
13
- };
14
- parameters: {
15
- iframe: {
16
- url: string;
17
- };
18
- chromatic: {
19
- disableSnapshot: boolean;
20
- };
21
- };
22
- };
23
- export default _default;
24
- export declare const base: StoryObj<{
25
- handleItemSelect: ReturnType<typeof fn>;
26
- handleBackspace: ReturnType<typeof fn>;
27
- }>;
@@ -1,86 +0,0 @@
1
- import React from "react";
2
- import { expect, waitFor, within, fn } from "@storybook/test";
3
- import { Example } from "../../../utilities/storybook/index.js";
4
- import Autocomplete from "../index.js";
5
- import FormControl from "../../FormControl/index.js";
6
- import { sleep } from "../../../utilities/helpers.js";
7
- import userEvent from "@testing-library/user-event";
8
- export default {
9
- title: "Components/Autocomplete/tests",
10
- component: Autocomplete,
11
- parameters: {
12
- iframe: {
13
- url: "https://reshaped.so/docs/components/autocomplete",
14
- },
15
- chromatic: { disableSnapshot: true },
16
- },
17
- };
18
- export const base = {
19
- name: "base",
20
- args: {
21
- handleBackspace: fn(),
22
- handleItemSelect: fn(),
23
- },
24
- render: (args) => {
25
- const [value, setValue] = React.useState("");
26
- return (<Example>
27
- <Example.Item title="Base">
28
- <FormControl>
29
- <FormControl.Label>Food</FormControl.Label>
30
- <Autocomplete name="fruit" placeholder="Pick your food" value={value} onChange={(args) => setValue(args.value)} onBackspace={args.handleBackspace} onItemSelect={args.handleItemSelect}>
31
- {["Pizza", "Pie", "Ice-cream"].map((v, i) => {
32
- return (<Autocomplete.Item key={v} value={v} data={i === 0 ? { foo: "bar" } : undefined}>
33
- {v}
34
- </Autocomplete.Item>);
35
- })}
36
- </Autocomplete>
37
- </FormControl>
38
- </Example.Item>
39
- </Example>);
40
- },
41
- play: async ({ canvasElement, args }) => {
42
- const canvas = within(canvasElement.ownerDocument.body);
43
- const input = canvas.getByRole("combobox");
44
- // Reset the focus
45
- document.body.focus();
46
- // Test keyboard selection after focusing the input
47
- input.focus();
48
- let options = [];
49
- await waitFor(() => {
50
- options = canvas.getAllByRole("option");
51
- });
52
- expect(options).toHaveLength(3);
53
- await waitFor(() => {
54
- expect(options[0]).toHaveAttribute("data-rs-focus");
55
- });
56
- expect(options[1]).not.toHaveAttribute("data-rs-focus");
57
- await userEvent.keyboard("{ArrowDown/}");
58
- await userEvent.keyboard("{Enter/}");
59
- expect(input).toHaveValue("Pie");
60
- expect(args.handleItemSelect).toHaveBeenCalledTimes(1);
61
- expect(args.handleItemSelect).toHaveBeenCalledWith({
62
- value: "Pie",
63
- data: undefined,
64
- });
65
- // Give browser time to focus on the input
66
- await sleep(100);
67
- // Test click selection after opening with down arrow
68
- await userEvent.keyboard("{ArrowDown/}");
69
- await waitFor(() => {
70
- options = canvas.getAllByRole("option");
71
- });
72
- await userEvent.click(options[0]);
73
- expect(input).toHaveValue("Pizza");
74
- expect(args.handleItemSelect).toHaveBeenCalledTimes(2);
75
- expect(args.handleItemSelect).toHaveBeenCalledWith({
76
- value: "Pizza",
77
- data: { foo: "bar" },
78
- });
79
- input.focus();
80
- await userEvent.keyboard("{Backspace/}");
81
- await waitFor(() => {
82
- expect(args.handleBackspace).toHaveBeenCalledTimes(1);
83
- expect(args.handleBackspace).toHaveBeenCalledWith();
84
- });
85
- },
86
- };