reshaped 3.8.0-canary.0 → 3.8.0-canary.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.
@@ -1,5 +1,7 @@
1
+ import { expect, fn, userEvent, waitFor } from "storybook/internal/test";
1
2
  import { Example } from "../../../utilities/storybook/index.js";
2
3
  import PinField from "../index.js";
4
+ import FormControl from "../../FormControl/index.js";
3
5
  export default {
4
6
  title: "Components/PinField",
5
7
  component: PinField,
@@ -9,54 +11,199 @@ export default {
9
11
  },
10
12
  },
11
13
  };
12
- export const base = () => (<Example>
13
- <Example.Item title="no value">
14
- <PinField name="pin" inputAttributes={{ "aria-label": "Pin" }}/>
15
- </Example.Item>
16
-
17
- <Example.Item title="defaultValue: 1234">
18
- <PinField name="pin2" defaultValue="1234" inputAttributes={{ "aria-label": "Pin" }}/>
19
- </Example.Item>
20
-
21
- <Example.Item title="value: 12">
22
- <PinField name="pin3" value="12" inputAttributes={{ "aria-label": "Pin" }}/>
23
- </Example.Item>
24
-
25
- <Example.Item title="defaultValue: 12, valueLength: 6">
26
- <PinField name="pin4" defaultValue="12" valueLength={6} inputAttributes={{ "aria-label": "Pin" }}/>
27
- </Example.Item>
28
-
29
- <Example.Item title="defaultValue: ab, charPattern: alphabetic">
30
- <PinField name="pin5" defaultValue="ab" pattern="alphabetic" inputAttributes={{ "aria-label": "Pin" }}/>
31
- </Example.Item>
32
-
33
- <Example.Item title="defaultValue: ab, charPattern: alphanumeric">
34
- <PinField name="pin6" defaultValue="ab" pattern="alphanumeric" inputAttributes={{ "aria-label": "Pin" }}/>
35
- </Example.Item>
36
- </Example>);
37
- export const variant = () => (<Example>
38
- <Example.Item title="variant: faded">
39
- <PinField name="pin" variant="faded" inputAttributes={{ "aria-label": "Pin" }}/>
40
- </Example.Item>
41
- </Example>);
42
- export const size = () => (<Example>
43
- <Example.Item title="size: small">
44
- <PinField name="pin" size="small" inputAttributes={{ "aria-label": "Pin" }}/>
45
- </Example.Item>
14
+ export const base = {
15
+ name: "base",
16
+ render: () => <PinField name="test-name" inputAttributes={{ "aria-label": "Label" }}/>,
17
+ play: async ({ canvas }) => {
18
+ const elInput = canvas.getByRole("textbox");
19
+ expect(elInput).toBeInTheDocument();
20
+ expect(elInput).toHaveAttribute("name", "test-name");
21
+ expect(elInput).toHaveAttribute("autocomplete", "one-time-code");
22
+ expect(elInput).toHaveAttribute("inputmode", "numeric");
23
+ },
24
+ };
25
+ export const variant = {
26
+ name: "variant",
27
+ render: () => (<Example>
28
+ <Example.Item title="variant: faded">
29
+ <PinField name="pin" variant="faded" inputAttributes={{ "aria-label": "Pin" }}/>
30
+ </Example.Item>
31
+ </Example>),
32
+ };
33
+ export const size = {
34
+ name: "size",
35
+ render: () => (<Example>
36
+ <Example.Item title="size: small">
37
+ <PinField name="pin" size="small" inputAttributes={{ "aria-label": "Pin" }}/>
38
+ </Example.Item>
46
39
 
47
- <Example.Item title="size: medium">
48
- <PinField name="pin" size="medium" inputAttributes={{ "aria-label": "Pin" }}/>
49
- </Example.Item>
40
+ <Example.Item title="size: medium">
41
+ <PinField name="pin" size="medium" inputAttributes={{ "aria-label": "Pin" }}/>
42
+ </Example.Item>
50
43
 
51
- <Example.Item title="size: large">
52
- <PinField name="pin" size="large" inputAttributes={{ "aria-label": "Pin" }}/>
53
- </Example.Item>
44
+ <Example.Item title="size: large">
45
+ <PinField name="pin" size="large" inputAttributes={{ "aria-label": "Pin" }}/>
46
+ </Example.Item>
54
47
 
55
- <Example.Item title="size: xlarge">
56
- <PinField name="pin" size="xlarge" inputAttributes={{ "aria-label": "Pin" }}/>
57
- </Example.Item>
48
+ <Example.Item title="size: xlarge">
49
+ <PinField name="pin" size="xlarge" inputAttributes={{ "aria-label": "Pin" }}/>
50
+ </Example.Item>
58
51
 
59
- <Example.Item title="size: responsive, s: medium, m+: xlarge">
60
- <PinField name="pin" size={{ s: "medium", m: "xlarge" }} inputAttributes={{ "aria-label": "Pin" }}/>
61
- </Example.Item>
62
- </Example>);
52
+ <Example.Item title="size: responsive, s: medium, m+: xlarge">
53
+ <PinField name="pin" size={{ s: "medium", m: "xlarge" }} inputAttributes={{ "aria-label": "Pin" }}/>
54
+ </Example.Item>
55
+ </Example>),
56
+ };
57
+ export const valueLength = {
58
+ name: "valueLength",
59
+ render: () => (<PinField name="test-name" valueLength={6} inputAttributes={{ "aria-label": "Label" }}/>),
60
+ play: async ({ canvas }) => {
61
+ const elInput = canvas.getByRole("textbox");
62
+ expect(elInput).toHaveAttribute("maxlength", "6");
63
+ },
64
+ };
65
+ export const defaultValue = {
66
+ name: "defaultValue, uncontrolled",
67
+ args: {
68
+ handleChange: fn(),
69
+ },
70
+ render: (args) => (<PinField name="test-name" onChange={args.handleChange} defaultValue="12" inputAttributes={{ "aria-label": "Label" }}/>),
71
+ play: async ({ canvas, args }) => {
72
+ const elInput = canvas.getByRole("textbox");
73
+ elInput.focus();
74
+ await userEvent.keyboard("3");
75
+ expect(args.handleChange).toHaveBeenCalledTimes(1);
76
+ expect(args.handleChange).toHaveBeenCalledWith({
77
+ value: "123",
78
+ name: "test-name",
79
+ event: expect.objectContaining({ target: elInput }),
80
+ });
81
+ expect(elInput).toHaveValue("123");
82
+ },
83
+ };
84
+ export const value = {
85
+ name: "value, controlled",
86
+ args: {
87
+ handleChange: fn(),
88
+ },
89
+ render: (args) => (<PinField name="test-name" onChange={args.handleChange} value="12" inputAttributes={{ "aria-label": "Label" }}/>),
90
+ play: async ({ canvas, args }) => {
91
+ const elInput = canvas.getByRole("textbox");
92
+ elInput.focus();
93
+ await userEvent.keyboard("3");
94
+ expect(args.handleChange).toHaveBeenCalledTimes(1);
95
+ expect(args.handleChange).toHaveBeenCalledWith({
96
+ value: "123",
97
+ name: "test-name",
98
+ event: expect.objectContaining({ target: elInput }),
99
+ });
100
+ expect(elInput).toHaveValue("12");
101
+ },
102
+ };
103
+ export const pattern = {
104
+ name: "pattern",
105
+ args: {
106
+ handleChange: fn(),
107
+ },
108
+ render: (args) => (<PinField name="test-name" pattern="alphabetic" defaultValue="ab" onChange={args.handleChange} inputAttributes={{ "aria-label": "Label" }}/>),
109
+ play: async ({ canvas, args }) => {
110
+ const elInput = canvas.getByRole("textbox");
111
+ elInput.focus();
112
+ await userEvent.keyboard("3");
113
+ expect(elInput).toHaveValue("ab");
114
+ expect(args.handleChange).toHaveBeenCalledTimes(0);
115
+ await userEvent.keyboard("c");
116
+ expect(elInput).toHaveValue("abc");
117
+ expect(args.handleChange).toHaveBeenCalledTimes(1);
118
+ },
119
+ };
120
+ export const className = {
121
+ name: "className, attributes",
122
+ render: () => (<div data-testid="root">
123
+ <PinField name="test-name" className="test-classname" attributes={{ id: "test-id" }} inputAttributes={{ "aria-label": "Label", id: "test-input-id" }}/>
124
+ </div>),
125
+ play: async ({ canvas }) => {
126
+ const root = canvas.getByTestId("root").firstChild;
127
+ const input = canvas.queryByLabelText("Label");
128
+ expect(root).toHaveClass("test-classname");
129
+ expect(root).toHaveAttribute("id", "test-id");
130
+ expect(input).toHaveAttribute("id", "test-input-id");
131
+ },
132
+ };
133
+ export const formControl = {
134
+ name: "test: with FormControl",
135
+ render: () => (<FormControl>
136
+ <FormControl.Label>Label</FormControl.Label>
137
+ <PinField name="test-name"/>
138
+ </FormControl>),
139
+ play: async ({ canvas }) => {
140
+ const elInput = canvas.getByRole("textbox");
141
+ expect(elInput).toHaveAccessibleName("Label");
142
+ },
143
+ };
144
+ export const keyboard = {
145
+ name: "test: keyboard navigation",
146
+ render: () => <PinField name="test-name" inputAttributes={{ "aria-label": "Label" }}/>,
147
+ play: async ({ canvas }) => {
148
+ const elInput = canvas.getByRole("textbox");
149
+ elInput.focus();
150
+ expect(elInput.selectionStart).toEqual(0);
151
+ expect(elInput.selectionEnd).toEqual(0);
152
+ await userEvent.keyboard("1");
153
+ await waitFor(() => {
154
+ expect(elInput.selectionStart).toEqual(1);
155
+ expect(elInput.selectionEnd).toEqual(1);
156
+ });
157
+ await userEvent.keyboard("234");
158
+ await waitFor(() => {
159
+ expect(elInput.selectionStart).toEqual(3);
160
+ expect(elInput.selectionEnd).toEqual(4);
161
+ });
162
+ // Move back to the first character
163
+ await userEvent.keyboard("{ArrowLeft/}");
164
+ await waitFor(() => {
165
+ expect(elInput.selectionStart).toEqual(2);
166
+ expect(elInput.selectionEnd).toEqual(3);
167
+ });
168
+ await userEvent.keyboard("{ArrowLeft/}");
169
+ await waitFor(() => {
170
+ expect(elInput.selectionStart).toEqual(1);
171
+ expect(elInput.selectionEnd).toEqual(2);
172
+ });
173
+ await userEvent.keyboard("{ArrowLeft}");
174
+ await waitFor(() => {
175
+ expect(elInput.selectionStart).toEqual(0);
176
+ expect(elInput.selectionEnd).toEqual(1);
177
+ });
178
+ // Move to the third character
179
+ await userEvent.keyboard("{ArrowRight}");
180
+ await waitFor(() => {
181
+ expect(elInput.selectionStart).toEqual(1);
182
+ expect(elInput.selectionEnd).toEqual(2);
183
+ });
184
+ await userEvent.keyboard("{ArrowRight}");
185
+ await waitFor(() => {
186
+ expect(elInput.selectionStart).toEqual(2);
187
+ expect(elInput.selectionEnd).toEqual(3);
188
+ });
189
+ expect(elInput).toHaveValue("1234");
190
+ await userEvent.keyboard("{backspace}");
191
+ expect(elInput).toHaveValue("124");
192
+ await waitFor(() => {
193
+ expect(elInput.selectionStart).toEqual(2);
194
+ expect(elInput.selectionEnd).toEqual(3);
195
+ });
196
+ // Switched to type mode
197
+ await userEvent.keyboard("{ArrowRight}");
198
+ await waitFor(() => {
199
+ expect(elInput.selectionStart).toEqual(3);
200
+ expect(elInput.selectionStart).toEqual(3);
201
+ });
202
+ // Can't move further
203
+ await userEvent.keyboard("{ArrowRight}");
204
+ await waitFor(() => {
205
+ expect(elInput.selectionStart).toEqual(3);
206
+ expect(elInput.selectionStart).toEqual(3);
207
+ });
208
+ },
209
+ };
@@ -1,7 +1,7 @@
1
1
  import type React from "react";
2
2
  import type { FlyoutProps, FlyoutInstance } from "../Flyout";
3
3
  export type Instance = FlyoutInstance;
4
- export type Props = Pick<FlyoutProps, "id" | "position" | "forcePosition" | "fallbackPositions" | "onOpen" | "onClose" | "width" | "trapFocusMode" | "active" | "defaultActive" | "contentGap" | "contentShift" | "instanceRef" | "triggerType" | "disableHideAnimation" | "disableContentHover" | "disableCloseOnOutsideClick" | "autoFocus" | "containerRef" | "initialFocusRef" | "originCoordinates"> & {
4
+ export type Props = Pick<FlyoutProps, "id" | "position" | "forcePosition" | "fallbackPositions" | "fallbackAdjustLayout" | "onOpen" | "onClose" | "width" | "trapFocusMode" | "active" | "defaultActive" | "contentGap" | "contentShift" | "instanceRef" | "triggerType" | "disableHideAnimation" | "disableContentHover" | "disableCloseOnOutsideClick" | "autoFocus" | "containerRef" | "initialFocusRef" | "originCoordinates"> & {
5
5
  /** Node for inserting children */
6
6
  children?: React.ReactNode;
7
7
  /** Content element padding, unit token multiplier */
@@ -1 +1 @@
1
- @layer rs.reset{[data-rs-theme]{--rs-radius-circular:9999px;--rs-shadow-border-faded:0px 0px 0px 1px var(--rs-color-border-neutral-faded)}[data-rs-theme] blockquote,[data-rs-theme] body,[data-rs-theme] dd,[data-rs-theme] dl,[data-rs-theme] figcaption,[data-rs-theme] figure,[data-rs-theme] h1,[data-rs-theme] h2,[data-rs-theme] h3,[data-rs-theme] h4,[data-rs-theme] h5,[data-rs-theme] h6,[data-rs-theme] li,[data-rs-theme] ol,[data-rs-theme] p,[data-rs-theme] ul{margin:0;padding:0}[data-rs-theme] ol[class],[data-rs-theme] ul[class]{list-style:none}[data-rs-theme] textarea{resize:vertical}[data-rs-theme] table{border-collapse:collapse;border-spacing:0}[data-rs-theme] fieldset{border:0;margin:0;padding:0}[data-rs-theme] img{display:block;max-width:100%}[data-rs-theme] button,[data-rs-theme] input,[data-rs-theme] select,[data-rs-theme] textarea{font:inherit}[data-rs-theme] option{background:var(--rs-color-background-elevation-base)}[data-rs-theme] label{cursor:pointer}[data-rs-theme] input::placeholder,[data-rs-theme] textarea::placeholder{color:var(--rs-color-foreground-disabled)}html[data-rs-theme]{-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;font-size:100%;text-rendering:optimizelegibility;touch-action:manipulation}[data-rs-theme] *{box-sizing:border-box}[data-rs-theme] body,html[data-rs-theme]{background:var(--rs-color-background-page);color:var(--rs-color-foreground-neutral);height:100%;scroll-behavior:smooth}}[data-rs-theme] body,[data-rs-theme]:not(html){font-family:var(--rs-font-family-body);font-size:var(--rs-font-size-body-3);font-weight:var(--rs-font-weight-regular);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3)}[data-rs-color-mode=light]{color-scheme:light}[data-rs-color-mode=dark]{color-scheme:dark}@media (prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}[data-rs-no-transition] *,[data-rs-no-transition] :after,[data-rs-no-transition] :before{transition:none!important}
1
+ @layer rs.reset{[data-rs-theme]{--rs-radius-circular:9999px;--rs-shadow-border-faded:0px 0px 0px 1px var(--rs-color-border-neutral-faded)}[data-rs-theme] blockquote,[data-rs-theme] body,[data-rs-theme] dd,[data-rs-theme] dl,[data-rs-theme] figcaption,[data-rs-theme] figure,[data-rs-theme] h1,[data-rs-theme] h2,[data-rs-theme] h3,[data-rs-theme] h4,[data-rs-theme] h5,[data-rs-theme] h6,[data-rs-theme] li,[data-rs-theme] ol,[data-rs-theme] p,[data-rs-theme] ul{margin:0;padding:0}[data-rs-theme] ol[class],[data-rs-theme] ul[class]{list-style:none}[data-rs-theme] textarea{resize:vertical}[data-rs-theme] table{border-collapse:collapse;border-spacing:0}[data-rs-theme] fieldset{border:0;margin:0;padding:0}[data-rs-theme] img{display:block;max-width:100%}[data-rs-theme] button,[data-rs-theme] input,[data-rs-theme] select,[data-rs-theme] textarea{font:inherit}[data-rs-theme] option{background:var(--rs-color-background-elevation-base)}[data-rs-theme] label{cursor:pointer}[data-rs-theme] input::placeholder,[data-rs-theme] textarea::placeholder{color:var(--rs-color-foreground-disabled)}html[data-rs-theme]{-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;font-size:100%;text-rendering:optimizelegibility;touch-action:manipulation}[data-rs-theme] *{box-sizing:border-box}[data-rs-theme] body,html[data-rs-theme]{background:var(--rs-color-background-page);color:var(--rs-color-foreground-neutral);min-height:100vh;scroll-behavior:smooth}}[data-rs-theme] body,[data-rs-theme]:not(html){font-family:var(--rs-font-family-body);font-size:var(--rs-font-size-body-3);font-weight:var(--rs-font-weight-regular);letter-spacing:var(--rs-letter-spacing-body-3);line-height:var(--rs-line-height-body-3)}[data-rs-color-mode=light]{color-scheme:light}[data-rs-color-mode=dark]{color-scheme:dark}@media (prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}[data-rs-no-transition] *,[data-rs-no-transition] :after,[data-rs-no-transition] :before{transition:none!important}
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.8.0-canary.0",
4
+ "version": "3.8.0-canary.1",
5
5
  "license": "MIT",
6
6
  "email": "hello@reshaped.so",
7
7
  "homepage": "https://reshaped.so",
@@ -18,8 +18,17 @@
18
18
  "accessibility",
19
19
  "accessible"
20
20
  ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/reshaped-ui/reshaped.git"
24
+ },
21
25
  "bugs": {
22
- "url": "https://github.com/formaat-design/reshaped/issues"
26
+ "url": "https://github.com/reshaped-ui/reshaped/issues"
27
+ },
28
+ "author": {
29
+ "name": "Dmitry Belyaev",
30
+ "email": "blv.dmitry@gmail.com",
31
+ "url": "https://reshaped.so"
23
32
  },
24
33
  "sideEffects": [
25
34
  "*.css"
@@ -63,33 +72,33 @@
63
72
  "scripts": {
64
73
  "dev": "storybook dev -p 3001 --disable-telemetry",
65
74
  "clean": "sh ./bin/clean.sh",
66
- "build": "yarn clean && yarn build:stories && yarn build:esm && yarn build:css && yarn build:cjs && yarn build:bundle",
75
+ "build": "pnpm clean && pnpm build:stories && pnpm build:esm && pnpm build:css && pnpm build:cjs && pnpm build:bundle",
67
76
  "build:themes": "node bin/cli.js theming --config dist/cjs/cli/theming/reshaped.config.js --output src/themes",
68
77
  "build:esm": "tsc -p tsconfig.esm.json && resolve-tspaths -p tsconfig.esm.json",
69
78
  "build:cjs": "tsc -p tsconfig.cjs.json && resolve-tspaths -p tsconfig.cjs.json && node ./bin/format-cjs-build.js",
70
79
  "build:css": "postcss \"src/**/*.css\" --dir dist --base src --config tools/build",
71
80
  "build:stories": "tsc -p tsconfig.stories.json && resolve-tspaths -p tsconfig.stories.json",
72
81
  "build:bundle": "vite build && cp dist/index.d.ts dist/bundle.d.ts",
73
- "build:size": "yarn clean && yarn build:esm && yarn build:bundle",
82
+ "build:size": "pnpm clean && pnpm build:esm && pnpm build:bundle",
74
83
  "build:storybook": "storybook build -o dist/app --disable-telemetry",
75
84
  "build:chromatic": "STORYBOOK_ENV=chromatic storybook build",
76
85
  "changelog": "node bin/generate-changelog.js",
77
- "version": "yarn changelog && git add CHANGELOG.md",
86
+ "version": "pnpm changelog && git add CHANGELOG.md",
78
87
  "release": "node bin/release.js",
79
88
  "release:patch": "node bin/release.js patch",
80
89
  "release:minor": "node bin/release.js minor",
81
90
  "release:major": "node bin/release.js major",
82
- "release:lib": "yarn build && yarn publish",
83
- "release:canary": "yarn build && yarn publish --tag canary",
84
- "release:test": "yarn build && yarn pack --filename reshaped-test.tgz",
91
+ "release:lib": "pnpm build && pnpm publish",
92
+ "release:canary": "pnpm build && yarn publish --tag canary",
93
+ "release:test": "pnpm build && pnpm pack --filename reshaped-test.tgz",
85
94
  "release:copy": "sh ./bin/release-copy.sh",
86
95
  "chromatic": "chromatic -b build:chromatic --project-token=$(cat .chromatic)",
87
- "test:vrt": "yarn chromatic",
88
- "test:vrt:turbo": "yarn chromatic --only-changed",
96
+ "test:vrt": "pnpm chromatic",
97
+ "test:vrt:turbo": "pnpm chromatic --only-changed",
89
98
  "test:browser": "vitest run --project=storybook",
90
99
  "test:unit": "vitest run --project=unit",
91
100
  "test:size": "size-limit",
92
- "lint": "yarn lint:js && yarn lint:css",
101
+ "lint": "pnpm lint:js && pnpm lint:css",
93
102
  "lint:js": "eslint './src/**/*.{ts,tsx}' --fix",
94
103
  "lint:css": "stylelint 'src/**/*.css'",
95
104
  "commit": "git-cz"
@@ -101,31 +110,33 @@
101
110
  "@commitlint/cli": "19.8.1",
102
111
  "@commitlint/config-conventional": "19.8.1",
103
112
  "@commitlint/types": "19.8.1",
104
- "@eslint/js": "9.34.0",
113
+ "@eslint/js": "9.35.0",
105
114
  "@size-limit/preset-big-lib": "11.2.0",
106
- "@storybook/addon-a11y": "9.1.3",
107
- "@storybook/addon-docs": "9.1.3",
108
- "@storybook/addon-vitest": "9.1.3",
109
- "@storybook/react-vite": "9.1.3",
110
- "@types/culori": "4.0.0",
115
+ "@storybook/addon-a11y": "9.1.5",
116
+ "@storybook/addon-docs": "9.1.5",
117
+ "@storybook/addon-vitest": "9.1.5",
118
+ "@storybook/react": "9.1.5",
119
+ "@storybook/react-vite": "9.1.5",
120
+ "@testing-library/user-event": "14.6.1",
121
+ "@types/culori": "4.0.1",
111
122
  "@types/events": "3.0.3",
112
- "@types/node": "24.3.0",
113
- "@types/react": "19.1.12",
123
+ "@types/node": "24.3.3",
124
+ "@types/react": "19.1.13",
114
125
  "@types/react-dom": "19.1.9",
115
126
  "@vitejs/plugin-react": "4.6.0",
116
127
  "@vitest/browser": "3.2.4",
117
128
  "@vitest/coverage-istanbul": "3.2.4",
118
129
  "@vitest/coverage-v8": "3.2.4",
119
- "chromatic": "13.1.3",
130
+ "chromatic": "13.1.4",
120
131
  "conventional-changelog-cli": "5.0.0",
121
132
  "cz-conventional-changelog": "3.3.0",
122
- "eslint": "9.34.0",
133
+ "eslint": "9.35.0",
123
134
  "eslint-config-prettier": "10.1.8",
124
135
  "eslint-plugin-jsx-a11y": "6.10.2",
125
136
  "eslint-plugin-prettier": "5.5.4",
126
137
  "eslint-plugin-react": "7.37.5",
127
138
  "eslint-plugin-react-hooks": "5.2.0",
128
- "lefthook": "1.12.3",
139
+ "lefthook": "1.13.0",
129
140
  "playwright": "1.55.0",
130
141
  "postcss": "8.5.6",
131
142
  "postcss-cli": "11.0.1",
@@ -137,14 +148,14 @@
137
148
  "react-shadow": "20.6.0",
138
149
  "resolve-tspaths": "0.8.23",
139
150
  "size-limit": "11.2.0",
140
- "storybook": "9.1.3",
141
- "stylelint": "16.23.1",
151
+ "storybook": "9.1.5",
152
+ "stylelint": "16.24.0",
142
153
  "stylelint-config-prettier": "9.0.5",
143
154
  "stylelint-config-standard": "39.0.0",
144
155
  "ts-node": "10.9.2",
145
156
  "typescript": "5.9.2",
146
- "typescript-eslint": "8.41.0",
147
- "vite": "7.1.3",
157
+ "typescript-eslint": "8.43.0",
158
+ "vite": "7.1.5",
148
159
  "vite-tsconfig-paths": "5.1.4",
149
160
  "vitest": "3.2.4",
150
161
  "vitest-browser-react": "1.0.1"
@@ -155,9 +166,9 @@
155
166
  "react-dom": "^18 || ^19"
156
167
  },
157
168
  "dependencies": {
158
- "@csstools/postcss-global-data": "3.0.0",
169
+ "@csstools/postcss-global-data": "3.1.0",
159
170
  "chalk": "4.1.2",
160
- "commander": "14.0.0",
171
+ "commander": "14.0.1",
161
172
  "cssnano": "7.1.1",
162
173
  "csstype": "3.1.3",
163
174
  "culori": "4.0.2",
@@ -1,29 +0,0 @@
1
- import { StoryObj } from "@storybook/react-vite";
2
- import { fn } from "storybook/test";
3
- declare const _default: {
4
- title: string;
5
- component: import("react").FC<import("./..").PinFieldProps>;
6
- parameters: {
7
- iframe: {
8
- url: string;
9
- };
10
- chromatic: {
11
- disableSnapshot: boolean;
12
- };
13
- };
14
- };
15
- export default _default;
16
- export declare const render: StoryObj;
17
- export declare const valueLength: StoryObj;
18
- export declare const defaultValue: StoryObj<{
19
- handleChange?: ReturnType<typeof fn>;
20
- }>;
21
- export declare const value: StoryObj<{
22
- handleChange?: ReturnType<typeof fn>;
23
- }>;
24
- export declare const pattern: StoryObj<{
25
- handleChange?: ReturnType<typeof fn>;
26
- }>;
27
- export declare const formControl: StoryObj;
28
- export declare const keyboard: StoryObj;
29
- export declare const className: StoryObj;
@@ -1,177 +0,0 @@
1
- import { expect, fn, userEvent, waitFor } from "storybook/test";
2
- import FormControl from "../../FormControl/index.js";
3
- import PinField from "../index.js";
4
- export default {
5
- title: "Components/PinField/tests",
6
- component: PinField,
7
- parameters: {
8
- iframe: {
9
- url: "https://reshaped.so/docs/components/pin-field",
10
- },
11
- chromatic: { disableSnapshot: true },
12
- },
13
- };
14
- export const render = {
15
- name: "rendering",
16
- render: () => <PinField name="test-name" inputAttributes={{ "aria-label": "Label" }}/>,
17
- play: async ({ canvas }) => {
18
- const elInput = canvas.getByRole("textbox");
19
- expect(elInput).toBeInTheDocument();
20
- expect(elInput).toHaveAttribute("name", "test-name");
21
- expect(elInput).toHaveAttribute("autocomplete", "one-time-code");
22
- expect(elInput).toHaveAttribute("inputmode", "numeric");
23
- },
24
- };
25
- export const valueLength = {
26
- name: "valueLength",
27
- render: () => (<PinField name="test-name" valueLength={6} inputAttributes={{ "aria-label": "Label" }}/>),
28
- play: async ({ canvas }) => {
29
- const elInput = canvas.getByRole("textbox");
30
- expect(elInput).toHaveAttribute("maxlength", "6");
31
- },
32
- };
33
- export const defaultValue = {
34
- name: "defaultValue, uncontrolled",
35
- args: {
36
- handleChange: fn(),
37
- },
38
- render: (args) => (<PinField name="test-name" onChange={args.handleChange} defaultValue="12" inputAttributes={{ "aria-label": "Label" }}/>),
39
- play: async ({ canvas, args }) => {
40
- const elInput = canvas.getByRole("textbox");
41
- elInput.focus();
42
- await userEvent.keyboard("3");
43
- expect(args.handleChange).toHaveBeenCalledTimes(1);
44
- expect(args.handleChange).toHaveBeenCalledWith({
45
- value: "123",
46
- name: "test-name",
47
- event: expect.objectContaining({ target: elInput }),
48
- });
49
- expect(elInput).toHaveValue("123");
50
- },
51
- };
52
- export const value = {
53
- name: "value, controlled",
54
- args: {
55
- handleChange: fn(),
56
- },
57
- render: (args) => (<PinField name="test-name" onChange={args.handleChange} value="12" inputAttributes={{ "aria-label": "Label" }}/>),
58
- play: async ({ canvas, args }) => {
59
- const elInput = canvas.getByRole("textbox");
60
- elInput.focus();
61
- await userEvent.keyboard("3");
62
- expect(args.handleChange).toHaveBeenCalledTimes(1);
63
- expect(args.handleChange).toHaveBeenCalledWith({
64
- value: "123",
65
- name: "test-name",
66
- event: expect.objectContaining({ target: elInput }),
67
- });
68
- expect(elInput).toHaveValue("12");
69
- },
70
- };
71
- export const pattern = {
72
- name: "pattern",
73
- args: {
74
- handleChange: fn(),
75
- },
76
- render: (args) => (<PinField name="test-name" pattern="alphabetic" defaultValue="ab" onChange={args.handleChange} inputAttributes={{ "aria-label": "Label" }}/>),
77
- play: async ({ canvas, args }) => {
78
- const elInput = canvas.getByRole("textbox");
79
- elInput.focus();
80
- await userEvent.keyboard("3");
81
- expect(elInput).toHaveValue("ab");
82
- expect(args.handleChange).toHaveBeenCalledTimes(0);
83
- await userEvent.keyboard("c");
84
- expect(elInput).toHaveValue("abc");
85
- expect(args.handleChange).toHaveBeenCalledTimes(1);
86
- },
87
- };
88
- export const formControl = {
89
- name: "with FormControl",
90
- render: () => (<FormControl>
91
- <FormControl.Label>Label</FormControl.Label>
92
- <PinField name="test-name"/>
93
- </FormControl>),
94
- play: async ({ canvas }) => {
95
- const elInput = canvas.getByRole("textbox");
96
- expect(elInput).toHaveAccessibleName("Label");
97
- },
98
- };
99
- export const keyboard = {
100
- name: "keyboard navigation",
101
- render: () => <PinField name="test-name" inputAttributes={{ "aria-label": "Label" }}/>,
102
- play: async ({ canvas }) => {
103
- const elInput = canvas.getByRole("textbox");
104
- elInput.focus();
105
- expect(elInput.selectionStart).toEqual(0);
106
- expect(elInput.selectionEnd).toEqual(0);
107
- await userEvent.keyboard("1");
108
- await waitFor(() => {
109
- expect(elInput.selectionStart).toEqual(1);
110
- expect(elInput.selectionEnd).toEqual(1);
111
- });
112
- await userEvent.keyboard("234");
113
- await waitFor(() => {
114
- expect(elInput.selectionStart).toEqual(3);
115
- expect(elInput.selectionEnd).toEqual(4);
116
- });
117
- // Move back to the first character
118
- await userEvent.keyboard("{ArrowLeft/}");
119
- await waitFor(() => {
120
- expect(elInput.selectionStart).toEqual(2);
121
- expect(elInput.selectionEnd).toEqual(3);
122
- });
123
- await userEvent.keyboard("{ArrowLeft/}");
124
- await waitFor(() => {
125
- expect(elInput.selectionStart).toEqual(1);
126
- expect(elInput.selectionEnd).toEqual(2);
127
- });
128
- await userEvent.keyboard("{ArrowLeft}");
129
- await waitFor(() => {
130
- expect(elInput.selectionStart).toEqual(0);
131
- expect(elInput.selectionEnd).toEqual(1);
132
- });
133
- // Move to the third character
134
- await userEvent.keyboard("{ArrowRight}");
135
- await waitFor(() => {
136
- expect(elInput.selectionStart).toEqual(1);
137
- expect(elInput.selectionEnd).toEqual(2);
138
- });
139
- await userEvent.keyboard("{ArrowRight}");
140
- await waitFor(() => {
141
- expect(elInput.selectionStart).toEqual(2);
142
- expect(elInput.selectionEnd).toEqual(3);
143
- });
144
- expect(elInput).toHaveValue("1234");
145
- await userEvent.keyboard("{backspace}");
146
- expect(elInput).toHaveValue("124");
147
- await waitFor(() => {
148
- expect(elInput.selectionStart).toEqual(2);
149
- expect(elInput.selectionEnd).toEqual(3);
150
- });
151
- // Switched to type mode
152
- await userEvent.keyboard("{ArrowRight}");
153
- await waitFor(() => {
154
- expect(elInput.selectionStart).toEqual(3);
155
- expect(elInput.selectionStart).toEqual(3);
156
- });
157
- // Can't move further
158
- await userEvent.keyboard("{ArrowRight}");
159
- await waitFor(() => {
160
- expect(elInput.selectionStart).toEqual(3);
161
- expect(elInput.selectionStart).toEqual(3);
162
- });
163
- },
164
- };
165
- export const className = {
166
- name: "className, attributes",
167
- render: () => (<div data-testid="root">
168
- <PinField name="test-name" className="test-classname" attributes={{ id: "test-id" }} inputAttributes={{ "aria-label": "Label", id: "test-input-id" }}/>
169
- </div>),
170
- play: async ({ canvas }) => {
171
- const root = canvas.getByTestId("root").firstChild;
172
- const input = canvas.queryByLabelText("Label");
173
- expect(root).toHaveClass("test-classname");
174
- expect(root).toHaveAttribute("id", "test-id");
175
- expect(input).toHaveAttribute("id", "test-input-id");
176
- },
177
- };