@transferwise/components 46.40.0 → 46.41.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.
- package/build/index.js +68 -74
- package/build/index.js.map +1 -1
- package/build/index.mjs +69 -75
- package/build/index.mjs.map +1 -1
- package/build/main.css +69 -14
- package/build/styles/main.css +69 -14
- package/build/styles/statusIcon/StatusIcon.css +4 -2
- package/build/styles/uploadInput/UploadInput.css +18 -1
- package/build/styles/uploadInput/uploadButton/UploadButton.css +4 -0
- package/build/styles/uploadInput/uploadItem/UploadItem.css +43 -11
- package/build/types/dateInput/DateInput.d.ts.map +1 -1
- package/build/types/field/Field.d.ts +6 -1
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/dateInput/DateInput.spec.tsx +220 -0
- package/src/dateInput/DateInput.story.tsx +3 -76
- package/src/dateInput/DateInput.tests.story.tsx +238 -0
- package/src/dateInput/DateInput.tsx +50 -53
- package/src/field/Field.story.tsx +17 -36
- package/src/field/Field.tests.story.tsx +33 -0
- package/src/field/Field.tsx +23 -13
- package/src/inlineAlert/InlineAlert.story.tsx +13 -5
- package/src/inlineAlert/InlineAlert.tsx +13 -6
- package/src/main.css +69 -14
- package/src/statusIcon/StatusIcon.css +4 -2
- package/src/statusIcon/StatusIcon.less +4 -2
- package/src/statusIcon/StatusIcon.tsx +1 -1
- package/src/uploadInput/UploadInput.css +18 -1
- package/src/uploadInput/UploadInput.less +17 -1
- package/src/uploadInput/UploadInput.tests.story.tsx +13 -2
- package/src/uploadInput/uploadButton/UploadButton.css +4 -0
- package/src/uploadInput/uploadButton/UploadButton.less +5 -0
- package/src/uploadInput/uploadItem/UploadItem.css +43 -11
- package/src/uploadInput/uploadItem/UploadItem.less +61 -17
- package/src/dateInput/DateInput.rtl.spec.tsx +0 -17
- package/src/dateInput/DateInput.spec.js +0 -477
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { screen, fireEvent } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { DateInput, DateInputProps, Field } from '..';
|
|
4
|
+
import { mockMatchMedia, mockResizeObserver, render } from '../test-utils';
|
|
5
|
+
|
|
6
|
+
describe('Date Input Component', () => {
|
|
7
|
+
const props: DateInputProps = { onChange: jest.fn() };
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
mockMatchMedia();
|
|
11
|
+
mockResizeObserver();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
jest.resetAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('when initialised without a model', () => {
|
|
19
|
+
it('sets day field to empty', () => {
|
|
20
|
+
render(<DateInput {...props} />);
|
|
21
|
+
const dayInput = screen.getByRole('textbox', { name: /day/i });
|
|
22
|
+
expect(dayInput).toHaveValue('');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('sets month field to empty', () => {
|
|
26
|
+
render(<DateInput {...props} />);
|
|
27
|
+
const monthSelect = screen.getByRole('button', { name: /month/i });
|
|
28
|
+
expect(monthSelect).toHaveValue('');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('sets year field to empty', () => {
|
|
32
|
+
render(<DateInput {...props} />);
|
|
33
|
+
const yearInput = screen.getByRole('textbox', { name: /year/i });
|
|
34
|
+
expect(yearInput).toHaveValue('');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('when initialised with a model', () => {
|
|
39
|
+
it('sets values correctly with a valid Date instance', () => {
|
|
40
|
+
render(<DateInput {...props} value="1971-08-01" />);
|
|
41
|
+
expect(screen.getByRole('textbox', { name: /day/i })).toHaveValue('1');
|
|
42
|
+
expect(screen.getByRole('button', { name: /month/i })).toHaveTextContent('August');
|
|
43
|
+
expect(screen.getByRole('textbox', { name: /year/i })).toHaveValue('1971');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('sets values correctly with a valid short ISO8601 string', () => {
|
|
47
|
+
render(<DateInput {...props} value="1990-08-22" />);
|
|
48
|
+
expect(screen.getByRole('textbox', { name: /day/i })).toHaveValue('22');
|
|
49
|
+
expect(screen.getByRole('button', { name: /month/i })).toHaveTextContent('August');
|
|
50
|
+
expect(screen.getByRole('textbox', { name: /year/i })).toHaveValue('1990');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('sets values correctly with a valid short ISO8601 string with year and month only', () => {
|
|
54
|
+
render(<DateInput {...props} value="1990-08" />);
|
|
55
|
+
expect(screen.getByRole('textbox', { name: /day/i })).toHaveValue('');
|
|
56
|
+
expect(screen.getByRole('button', { name: /month/i })).toHaveTextContent('August');
|
|
57
|
+
expect(screen.getByRole('textbox', { name: /year/i })).toHaveValue('1990');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('sets values correctly with a valid long ISO8601 string', () => {
|
|
61
|
+
render(<DateInput {...props} value="1990-02-28T00:00:00.000Z" />);
|
|
62
|
+
expect(screen.getByRole('textbox', { name: /day/i })).toHaveValue('28');
|
|
63
|
+
expect(screen.getByRole('button', { name: /month/i })).toHaveTextContent('February');
|
|
64
|
+
expect(screen.getByRole('textbox', { name: /year/i })).toHaveValue('1990');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('sets values to disabled when disabled is set to true', () => {
|
|
68
|
+
render(<DateInput {...props} disabled />);
|
|
69
|
+
expect(screen.getByRole('textbox', { name: /day/i })).toBeDisabled();
|
|
70
|
+
expect(screen.getByRole('button', { name: /month/i })).toBeDisabled();
|
|
71
|
+
expect(screen.getByRole('textbox', { name: /year/i })).toBeDisabled();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("doesn't set values to disabled when disabled is set to false", () => {
|
|
75
|
+
render(<DateInput {...props} />);
|
|
76
|
+
expect(screen.getByRole('textbox', { name: /day/i })).toBeEnabled();
|
|
77
|
+
expect(screen.getByRole('button', { name: /month/i })).toBeEnabled();
|
|
78
|
+
expect(screen.getByRole('textbox', { name: /year/i })).toBeEnabled();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('when initialised', () => {
|
|
83
|
+
it(`doesn't call the onChange callback without an initial value`, () => {
|
|
84
|
+
render(<DateInput {...props} />);
|
|
85
|
+
expect(props.onChange).not.toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it(`doesn't call the onChange callback with an initial value`, () => {
|
|
89
|
+
render(<DateInput {...props} value="1990-08-08" />);
|
|
90
|
+
expect(props.onChange).not.toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('when interacting', () => {
|
|
95
|
+
it('calls the onChange callback with null when changing the day but nothing else is filled out', async () => {
|
|
96
|
+
render(<DateInput {...props} />);
|
|
97
|
+
await userEvent.type(screen.getByRole('textbox', { name: /day/i }), '4');
|
|
98
|
+
expect(props.onChange).toHaveBeenCalledWith(null);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('calls the onChange callback with null when changing the month but nothing else is filled out', async () => {
|
|
102
|
+
render(<DateInput {...props} />);
|
|
103
|
+
const monthSelect = screen.getByLabelText(/month/i);
|
|
104
|
+
await userEvent.click(monthSelect);
|
|
105
|
+
await userEvent.click(screen.getByRole('option', { name: /January/ }));
|
|
106
|
+
expect(props.onChange).toHaveBeenCalledWith(null);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('calls the onChange callback with null when changing the year but nothing else is filled out', async () => {
|
|
110
|
+
render(<DateInput {...props} />);
|
|
111
|
+
await userEvent.type(screen.getByRole('textbox', { name: /day/i }), '4444');
|
|
112
|
+
expect(props.onChange).toHaveBeenCalledWith(null);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('calls the onChange callback with the correct value when changing the day', async () => {
|
|
116
|
+
render(<DateInput {...props} value="2022-12-1" />);
|
|
117
|
+
const dayInput = screen.getByRole('textbox', { name: /day/i });
|
|
118
|
+
// add 8 to the 1 that's already there
|
|
119
|
+
await userEvent.type(dayInput, '8');
|
|
120
|
+
expect(props.onChange).toHaveBeenCalledWith('2022-12-18');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('calls the onChange callback with the correct value when changing the month', async () => {
|
|
124
|
+
render(<DateInput {...props} value="2022-12-1" />);
|
|
125
|
+
const monthSelect = screen.getByRole('button', { name: /month/i });
|
|
126
|
+
await userEvent.click(monthSelect);
|
|
127
|
+
await userEvent.click(screen.getByRole('option', { name: /January/ }));
|
|
128
|
+
expect(props.onChange).toHaveBeenCalledWith('2022-01-01');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('calls the onChange callback with the correct value when changing the year', async () => {
|
|
132
|
+
render(<DateInput {...props} value="0122-12-1" />);
|
|
133
|
+
const yearInput = screen.getByRole('textbox', { name: /year/i });
|
|
134
|
+
// 0122 ends up being 122 in the box when it's supplied as the starting value
|
|
135
|
+
await userEvent.type(yearInput, '2');
|
|
136
|
+
expect(props.onChange).toHaveBeenCalledWith('1222-12-01');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('calls the onChange callback with the null when not having a 4 digit year', async () => {
|
|
140
|
+
render(<DateInput {...props} />);
|
|
141
|
+
const yearInput = screen.getByRole('textbox', { name: /year/i });
|
|
142
|
+
await userEvent.type(yearInput, '123');
|
|
143
|
+
expect(props.onChange).toHaveBeenCalledWith(null);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('calls the onChange callback with the null when changing the day to a day that is higher than max value', async () => {
|
|
147
|
+
render(<DateInput {...props} value="2022-11-3" />);
|
|
148
|
+
const dayInput = screen.getByRole('textbox', { name: /day/i });
|
|
149
|
+
// add 1 to the 3 that's already there
|
|
150
|
+
await userEvent.type(dayInput, '1');
|
|
151
|
+
expect(props.onChange).toHaveBeenCalledWith(null);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('does not overflow very high day values to the next month', async () => {
|
|
155
|
+
render(<DateInput {...props} value="2022-11-6" />);
|
|
156
|
+
const dayInput = screen.getByRole('textbox', { name: /day/i });
|
|
157
|
+
// add 6 to the 6 that's already there
|
|
158
|
+
await userEvent.type(dayInput, '6');
|
|
159
|
+
expect(props.onChange).toHaveBeenCalledWith(null);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('only pastes in first 6 digits of a long number for year', async () => {
|
|
163
|
+
render(<DateInput {...props} value="2010-01-01" />);
|
|
164
|
+
const yearInput = screen.getByRole('textbox', { name: /year/i });
|
|
165
|
+
yearInput.focus();
|
|
166
|
+
await userEvent.type(yearInput, '{backspace}{backspace}{backspace}{backspace}');
|
|
167
|
+
await userEvent.paste('12345678');
|
|
168
|
+
expect(props.onChange).toHaveBeenCalledWith('123456-01-01');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('only pastes in first 2 digits of a long number for day', async () => {
|
|
172
|
+
render(<DateInput {...props} value="2010-01-01" />);
|
|
173
|
+
const yearInput = screen.getByRole('textbox', { name: /day/i });
|
|
174
|
+
yearInput.focus();
|
|
175
|
+
await userEvent.type(yearInput, '{backspace}{backspace}');
|
|
176
|
+
await userEvent.paste('123456');
|
|
177
|
+
expect(props.onChange).toHaveBeenCalledWith('2010-01-12');
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('when switching from day input to year input', () => {
|
|
182
|
+
it('does not call onBlur nor onFocus', () => {
|
|
183
|
+
const onFocus = jest.fn();
|
|
184
|
+
const onBlur = jest.fn();
|
|
185
|
+
|
|
186
|
+
render(<DateInput {...props} />);
|
|
187
|
+
const dayInput = screen.getByRole('textbox', { name: /day/i });
|
|
188
|
+
const yearInput = screen.getByRole('textbox', { name: /year/i });
|
|
189
|
+
fireEvent.focus(dayInput);
|
|
190
|
+
fireEvent.blur(dayInput);
|
|
191
|
+
fireEvent.focus(yearInput);
|
|
192
|
+
expect(onFocus).not.toHaveBeenCalled();
|
|
193
|
+
expect(onBlur).not.toHaveBeenCalled();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('when selectProps is provided', () => {
|
|
198
|
+
it('renders Select component with expected props', () => {
|
|
199
|
+
render(
|
|
200
|
+
<DateInput
|
|
201
|
+
selectProps={{
|
|
202
|
+
id: 'mock-id',
|
|
203
|
+
}}
|
|
204
|
+
{...props}
|
|
205
|
+
/>,
|
|
206
|
+
);
|
|
207
|
+
const monthSelect = screen.getByRole('button', { name: /month/i });
|
|
208
|
+
expect(monthSelect).toHaveAttribute('id', 'mock-id');
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('supports `Field` for labeling', () => {
|
|
213
|
+
render(
|
|
214
|
+
<Field label="Date of birth">
|
|
215
|
+
<DateInput onChange={() => {}} />
|
|
216
|
+
</Field>,
|
|
217
|
+
);
|
|
218
|
+
expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Date of birth/);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
-
import {
|
|
2
|
+
import { fn } from '@storybook/test';
|
|
3
3
|
|
|
4
|
-
import { DateInput
|
|
5
|
-
import { lorem10, storyConfig } from '../test-utils';
|
|
4
|
+
import { DateInput } from '..';
|
|
6
5
|
|
|
7
6
|
const meta = {
|
|
8
7
|
component: DateInput,
|
|
9
8
|
title: 'Forms/DateInput',
|
|
10
|
-
args: {
|
|
11
|
-
dayLabel: 'Day input',
|
|
12
|
-
dayAutoComplete: 'bday-day',
|
|
13
|
-
monthLabel: 'Month select',
|
|
14
|
-
yearLabel: 'Year input',
|
|
15
|
-
yearAutoComplete: 'bday-year',
|
|
16
|
-
},
|
|
17
9
|
tags: ['autodocs'],
|
|
18
10
|
} satisfies Meta<typeof DateInput>;
|
|
19
11
|
|
|
@@ -23,71 +15,6 @@ type Story = StoryObj<typeof meta>;
|
|
|
23
15
|
|
|
24
16
|
export const Basic = {
|
|
25
17
|
args: {
|
|
26
|
-
onChange: ()
|
|
27
|
-
},
|
|
28
|
-
play: async ({ canvasElement }) => {
|
|
29
|
-
const canvas = within(canvasElement);
|
|
30
|
-
await userEvent.click(canvas.getByRole('button'));
|
|
31
|
-
},
|
|
32
|
-
} satisfies Story;
|
|
33
|
-
|
|
34
|
-
export const WithLabel = {
|
|
35
|
-
args: {
|
|
36
|
-
onChange: () => {},
|
|
37
|
-
},
|
|
38
|
-
render: (args) => {
|
|
39
|
-
const id1 = 'date-input-group-label-1';
|
|
40
|
-
const label = 'Date of Birth';
|
|
41
|
-
return (
|
|
42
|
-
<>
|
|
43
|
-
<Title type={Typography.TITLE_SUBSECTION}>
|
|
44
|
-
label (as <code>div</code> element) is linked with <code>DateInput</code> via{' '}
|
|
45
|
-
<code>aria-labelledby</code> prop
|
|
46
|
-
</Title>
|
|
47
|
-
<div className="control-label" id={id1}>
|
|
48
|
-
Date of Delivery
|
|
49
|
-
</div>
|
|
50
|
-
<DateInput {...args} aria-labelledby={id1} />
|
|
51
|
-
|
|
52
|
-
<br />
|
|
53
|
-
|
|
54
|
-
<Title type={Typography.TITLE_SUBSECTION}>
|
|
55
|
-
label (as <code>div</code> element) is detached but <code>DateInput</code> has same label
|
|
56
|
-
via <code>aria-label</code> attribute
|
|
57
|
-
</Title>
|
|
58
|
-
<div className="control-label">
|
|
59
|
-
{label}{' '}
|
|
60
|
-
<Info aria-label="Fast transfer hint" title="Fast transfer hint" content={lorem10} />
|
|
61
|
-
</div>
|
|
62
|
-
<DateInput {...args} aria-label={label} />
|
|
63
|
-
|
|
64
|
-
<br />
|
|
65
|
-
|
|
66
|
-
<Title type={Typography.TITLE_SUBSECTION}>
|
|
67
|
-
<code>DateInput</code> wrapped in <code>fieldset</code> + using <code>legend</code> as
|
|
68
|
-
label (rare use case)
|
|
69
|
-
</Title>
|
|
70
|
-
<fieldset>
|
|
71
|
-
<legend className="control-label">
|
|
72
|
-
Expiry Date for Credit Card (example of MONTH_YEAR mode)
|
|
73
|
-
</legend>
|
|
74
|
-
<DateInput {...args} mode={DateMode.MONTH_YEAR} />
|
|
75
|
-
</fieldset>
|
|
76
|
-
</>
|
|
77
|
-
);
|
|
18
|
+
onChange: fn(),
|
|
78
19
|
},
|
|
79
20
|
} satisfies Story;
|
|
80
|
-
|
|
81
|
-
export const BasicMobile = storyConfig(Basic, { variants: ['mobile'] });
|
|
82
|
-
|
|
83
|
-
export const InputError = {
|
|
84
|
-
...Basic,
|
|
85
|
-
render: (args) => (
|
|
86
|
-
<div className="form-group has-error">
|
|
87
|
-
<DateInput {...args} />
|
|
88
|
-
<InlineAlert type="error">{lorem10}</InlineAlert>
|
|
89
|
-
</div>
|
|
90
|
-
),
|
|
91
|
-
} satisfies Story;
|
|
92
|
-
|
|
93
|
-
export const InputErrorMobile = storyConfig(InputError, { variants: ['mobile'] });
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { userEvent, within, fn } from '@storybook/test';
|
|
3
|
+
|
|
4
|
+
import { DateInput, DateMode, Info, InlineAlert, Title, Typography } from '..';
|
|
5
|
+
import { lorem10, storyConfig } from '../test-utils';
|
|
6
|
+
|
|
7
|
+
import Provider from '../provider/Provider';
|
|
8
|
+
import translations from '../i18n';
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
component: DateInput,
|
|
12
|
+
title: 'Forms/DateInput/Tests',
|
|
13
|
+
args: {
|
|
14
|
+
dayLabel: 'Day input',
|
|
15
|
+
dayAutoComplete: 'bday-day',
|
|
16
|
+
monthLabel: 'Month select',
|
|
17
|
+
yearLabel: 'Year input',
|
|
18
|
+
yearAutoComplete: 'bday-year',
|
|
19
|
+
},
|
|
20
|
+
} satisfies Meta<typeof DateInput>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
|
|
24
|
+
type Story = StoryObj<typeof meta>;
|
|
25
|
+
|
|
26
|
+
const Basic = {
|
|
27
|
+
args: {
|
|
28
|
+
onChange: fn(),
|
|
29
|
+
},
|
|
30
|
+
play: async ({ canvasElement }) => {
|
|
31
|
+
const canvas = within(canvasElement);
|
|
32
|
+
await userEvent.click(canvas.getByRole('button'));
|
|
33
|
+
},
|
|
34
|
+
} satisfies Story;
|
|
35
|
+
|
|
36
|
+
export const WithLabel = {
|
|
37
|
+
args: {
|
|
38
|
+
onChange: fn(),
|
|
39
|
+
},
|
|
40
|
+
render: (args) => {
|
|
41
|
+
const id1 = 'date-input-group-label-1';
|
|
42
|
+
const label = 'Date of Birth';
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
<Title type={Typography.TITLE_SUBSECTION}>
|
|
47
|
+
label (as <code>div</code> element) is linked with <code>DateInput</code> via{' '}
|
|
48
|
+
<code>aria-labelledby</code> prop
|
|
49
|
+
</Title>
|
|
50
|
+
<div className="control-label" id={id1}>
|
|
51
|
+
Date of Delivery
|
|
52
|
+
</div>
|
|
53
|
+
<DateInput {...args} aria-labelledby={id1} />
|
|
54
|
+
|
|
55
|
+
<br />
|
|
56
|
+
|
|
57
|
+
<Title type={Typography.TITLE_SUBSECTION}>
|
|
58
|
+
label (as <code>div</code> element) is detached but <code>DateInput</code> has same label
|
|
59
|
+
via <code>aria-label</code> attribute
|
|
60
|
+
</Title>
|
|
61
|
+
<div className="control-label">
|
|
62
|
+
{label}{' '}
|
|
63
|
+
<Info aria-label="Fast transfer hint" title="Fast transfer hint" content={lorem10} />
|
|
64
|
+
</div>
|
|
65
|
+
<DateInput {...args} aria-label={label} />
|
|
66
|
+
|
|
67
|
+
<br />
|
|
68
|
+
|
|
69
|
+
<Title type={Typography.TITLE_SUBSECTION}>
|
|
70
|
+
<code>DateInput</code> wrapped in <code>fieldset</code> + using <code>legend</code> as
|
|
71
|
+
label (rare use case)
|
|
72
|
+
</Title>
|
|
73
|
+
<fieldset>
|
|
74
|
+
<legend className="control-label">
|
|
75
|
+
Expiry Date for Credit Card (example of MONTH_YEAR mode)
|
|
76
|
+
</legend>
|
|
77
|
+
<DateInput {...args} mode={DateMode.MONTH_YEAR} />
|
|
78
|
+
</fieldset>
|
|
79
|
+
</>
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
} satisfies Story;
|
|
83
|
+
|
|
84
|
+
export const InputError = {
|
|
85
|
+
...Basic,
|
|
86
|
+
render: (args) => (
|
|
87
|
+
<div className="form-group has-error">
|
|
88
|
+
<DateInput {...args} value={new Date('2010-04-05')} />
|
|
89
|
+
<InlineAlert type="error">{lorem10}</InlineAlert>
|
|
90
|
+
</div>
|
|
91
|
+
),
|
|
92
|
+
} satisfies Story;
|
|
93
|
+
|
|
94
|
+
export const CustomPlaceholders = {
|
|
95
|
+
...Basic,
|
|
96
|
+
args: {
|
|
97
|
+
placeholders: {
|
|
98
|
+
day: 'D',
|
|
99
|
+
month: 'M',
|
|
100
|
+
year: 'YY',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const SingleZero = storyConfig(Basic, {});
|
|
106
|
+
SingleZero.play = async ({ canvasElement, step }) => {
|
|
107
|
+
const canvas = within(canvasElement);
|
|
108
|
+
await step('can enter leading zero on day field', async () => {
|
|
109
|
+
const dayInput = await canvas.findByRole('textbox', { name: /day/i });
|
|
110
|
+
await userEvent.click(dayInput);
|
|
111
|
+
await userEvent.type(dayInput, '0');
|
|
112
|
+
});
|
|
113
|
+
await step('can enter 1 leading zero on year field', async () => {
|
|
114
|
+
const yearInput = await canvas.findByRole('textbox', { name: /year/i });
|
|
115
|
+
await userEvent.click(yearInput);
|
|
116
|
+
await userEvent.type(yearInput, '0');
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const ZeroesAsValue = storyConfig(Basic, {});
|
|
121
|
+
ZeroesAsValue.play = async ({ canvasElement, step }) => {
|
|
122
|
+
const canvas = within(canvasElement);
|
|
123
|
+
await step('can enter 2 leading zeroes on day field', async () => {
|
|
124
|
+
const dayInput = await canvas.findByRole('textbox', { name: /day/i });
|
|
125
|
+
await userEvent.click(dayInput);
|
|
126
|
+
await userEvent.type(dayInput, '00');
|
|
127
|
+
});
|
|
128
|
+
await step('can enter 4 leading zeroes on year field', async () => {
|
|
129
|
+
const yearInput = await canvas.findByRole('textbox', { name: /year/i });
|
|
130
|
+
await userEvent.click(yearInput);
|
|
131
|
+
await userEvent.type(yearInput, '0000');
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const ZeroesBeforeValue = storyConfig(Basic, {});
|
|
136
|
+
ZeroesBeforeValue.play = async ({ canvasElement, step }) => {
|
|
137
|
+
const canvas = within(canvasElement);
|
|
138
|
+
await step('can enter leading zeroes on day followed by day', async () => {
|
|
139
|
+
const dayInput = await canvas.findByRole('textbox', { name: /day/i });
|
|
140
|
+
await userEvent.click(dayInput);
|
|
141
|
+
await userEvent.type(dayInput, '01');
|
|
142
|
+
});
|
|
143
|
+
await step('can enter leading zeroes on year followed by year', async () => {
|
|
144
|
+
const yearInput = await canvas.findByRole('textbox', { name: /year/i });
|
|
145
|
+
await userEvent.click(yearInput);
|
|
146
|
+
await userEvent.type(yearInput, '0001');
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export const MaxLengthRespected = storyConfig(Basic, {});
|
|
151
|
+
MaxLengthRespected.play = async ({ canvasElement, step }) => {
|
|
152
|
+
const canvas = within(canvasElement);
|
|
153
|
+
await step('can only enter 4 digits in the year field', async () => {
|
|
154
|
+
const yearInput = await canvas.findByRole('textbox', { name: /year/i });
|
|
155
|
+
await userEvent.click(yearInput);
|
|
156
|
+
await userEvent.type(yearInput, '11111111');
|
|
157
|
+
});
|
|
158
|
+
await step('can only enter 2 digits in the day field', async () => {
|
|
159
|
+
const dayInput = await canvas.findByRole('textbox', { name: /day/i });
|
|
160
|
+
await userEvent.click(dayInput);
|
|
161
|
+
await userEvent.type(dayInput, '11111111');
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export const LocalizationWorks: Story = {
|
|
166
|
+
...Basic,
|
|
167
|
+
decorators: [
|
|
168
|
+
(Story) => {
|
|
169
|
+
const locale = 'zh-HK';
|
|
170
|
+
return (
|
|
171
|
+
<Provider i18n={{ locale, messages: translations[locale] }}>
|
|
172
|
+
<Story />
|
|
173
|
+
</Provider>
|
|
174
|
+
);
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const YearFirst: Story = {
|
|
180
|
+
...Basic,
|
|
181
|
+
decorators: [
|
|
182
|
+
(Story) => {
|
|
183
|
+
const locale = 'ja';
|
|
184
|
+
return (
|
|
185
|
+
<Provider i18n={{ locale, messages: translations[locale] }}>
|
|
186
|
+
<Story />
|
|
187
|
+
</Provider>
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export const MonthFirst: Story = {
|
|
194
|
+
...Basic,
|
|
195
|
+
decorators: [
|
|
196
|
+
(Story) => {
|
|
197
|
+
const locale = 'en-US';
|
|
198
|
+
return (
|
|
199
|
+
<Provider i18n={{ locale, messages: translations[locale] }}>
|
|
200
|
+
<Story />
|
|
201
|
+
</Provider>
|
|
202
|
+
);
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export const DayFirst: Story = {
|
|
208
|
+
...Basic,
|
|
209
|
+
decorators: [
|
|
210
|
+
(Story) => {
|
|
211
|
+
const locale = 'en';
|
|
212
|
+
return (
|
|
213
|
+
<Provider i18n={{ locale, messages: translations[locale] }}>
|
|
214
|
+
<Story />
|
|
215
|
+
</Provider>
|
|
216
|
+
);
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export const NoNonDigitsAllowed = storyConfig(Basic, {});
|
|
222
|
+
NoNonDigitsAllowed.play = async ({ canvasElement, step }) => {
|
|
223
|
+
const canvas = within(canvasElement);
|
|
224
|
+
await step('can only enter digits in the day field', async () => {
|
|
225
|
+
const dayInput = await canvas.findByRole('textbox', { name: /day/i });
|
|
226
|
+
await userEvent.click(dayInput);
|
|
227
|
+
await userEvent.type(dayInput, 'abcd1');
|
|
228
|
+
});
|
|
229
|
+
await step('can only enter digits in the year field', async () => {
|
|
230
|
+
const yearInput = await canvas.findByRole('textbox', { name: /year/i });
|
|
231
|
+
await userEvent.click(yearInput);
|
|
232
|
+
await userEvent.type(yearInput, 'abcd2');
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
export const BasicMobile = storyConfig(Basic, { variants: ['mobile'] });
|
|
237
|
+
|
|
238
|
+
export const InputErrorMobile = storyConfig(InputError, { variants: ['mobile'] });
|