@transferwise/components 46.40.0 → 46.41.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.
- 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 +4 -2
- package/build/styles/main.css +4 -2
- package/build/styles/statusIcon/StatusIcon.css +4 -2
- 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 +2 -2
- 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 +14 -7
- package/src/main.css +4 -2
- package/src/statusIcon/StatusIcon.css +4 -2
- package/src/statusIcon/StatusIcon.less +4 -2
- package/src/statusIcon/StatusIcon.tsx +1 -1
- package/src/dateInput/DateInput.rtl.spec.tsx +0 -17
- package/src/dateInput/DateInput.spec.js +0 -477
package/build/main.css
CHANGED
|
@@ -4208,14 +4208,16 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
4208
4208
|
height: var(--size-32);
|
|
4209
4209
|
}
|
|
4210
4210
|
}
|
|
4211
|
-
.status-circle.negative
|
|
4211
|
+
.status-circle.negative,
|
|
4212
|
+
.status-circle.error {
|
|
4212
4213
|
background-color: var(--color-sentiment-negative);
|
|
4213
4214
|
}
|
|
4214
4215
|
.status-circle.neutral {
|
|
4215
4216
|
background-color: #5d7079;
|
|
4216
4217
|
background-color: var(--color-content-secondary);
|
|
4217
4218
|
}
|
|
4218
|
-
.status-circle.positive
|
|
4219
|
+
.status-circle.positive,
|
|
4220
|
+
.status-circle.success {
|
|
4219
4221
|
background-color: var(--color-sentiment-positive);
|
|
4220
4222
|
}
|
|
4221
4223
|
.tw-stepper {
|
package/build/styles/main.css
CHANGED
|
@@ -4208,14 +4208,16 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
4208
4208
|
height: var(--size-32);
|
|
4209
4209
|
}
|
|
4210
4210
|
}
|
|
4211
|
-
.status-circle.negative
|
|
4211
|
+
.status-circle.negative,
|
|
4212
|
+
.status-circle.error {
|
|
4212
4213
|
background-color: var(--color-sentiment-negative);
|
|
4213
4214
|
}
|
|
4214
4215
|
.status-circle.neutral {
|
|
4215
4216
|
background-color: #5d7079;
|
|
4216
4217
|
background-color: var(--color-content-secondary);
|
|
4217
4218
|
}
|
|
4218
|
-
.status-circle.positive
|
|
4219
|
+
.status-circle.positive,
|
|
4220
|
+
.status-circle.success {
|
|
4219
4221
|
background-color: var(--color-sentiment-positive);
|
|
4220
4222
|
}
|
|
4221
4223
|
.tw-stepper {
|
|
@@ -66,13 +66,15 @@
|
|
|
66
66
|
height: var(--size-32);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
.status-circle.negative
|
|
69
|
+
.status-circle.negative,
|
|
70
|
+
.status-circle.error {
|
|
70
71
|
background-color: var(--color-sentiment-negative);
|
|
71
72
|
}
|
|
72
73
|
.status-circle.neutral {
|
|
73
74
|
background-color: #5d7079;
|
|
74
75
|
background-color: var(--color-content-secondary);
|
|
75
76
|
}
|
|
76
|
-
.status-circle.positive
|
|
77
|
+
.status-circle.positive,
|
|
78
|
+
.status-circle.success {
|
|
77
79
|
background-color: var(--color-sentiment-positive);
|
|
78
80
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../src/dateInput/DateInput.tsx"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"DateInput.d.ts","sourceRoot":"","sources":["../../../src/dateInput/DateInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAsD,gBAAgB,EAAE,MAAM,IAAI,CAAC;AAC1F,OAAO,EACL,QAAQ,EACR,WAAW,EAEX,SAAS,EACT,UAAU,EACV,SAAS,EAEV,MAAM,WAAW,CAAC;AAMnB,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,GAAG,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;CACxD;AAED,QAAA,MAAM,SAAS,wPAmBZ,cAAc,gCAkShB,CAAC;AA8BF,eAAe,SAAS,CAAC"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { Sentiment } from '../common';
|
|
1
2
|
export type FieldProps = {
|
|
2
3
|
/** `null` disables auto-generating the `id` attribute, falling back to nesting-based label association over setting `htmlFor` explicitly. */
|
|
3
4
|
id?: string | null;
|
|
4
5
|
label: React.ReactNode;
|
|
6
|
+
/** @deprecated use `message` and `type={Sentiment.NEUTRAL}` prop instead */
|
|
5
7
|
hint?: React.ReactNode;
|
|
8
|
+
message?: React.ReactNode;
|
|
9
|
+
/** @deprecated use `message` and `type={Sentiment.NEGATIVE}` prop instead */
|
|
6
10
|
error?: React.ReactNode;
|
|
11
|
+
sentiment?: `${Sentiment.NEGATIVE | Sentiment.NEUTRAL | Sentiment.POSITIVE | Sentiment.WARNING}`;
|
|
7
12
|
className?: string;
|
|
8
13
|
children?: React.ReactNode;
|
|
9
14
|
};
|
|
10
|
-
export declare const Field: ({ id, label,
|
|
15
|
+
export declare const Field: ({ id, label, message: propMessage, sentiment: propType, className, children, ...props }: FieldProps) => import("react").JSX.Element;
|
|
11
16
|
//# sourceMappingURL=Field.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../../src/field/Field.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../../src/field/Field.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAUtC,MAAM,MAAM,UAAU,GAAG;IACvB,6IAA6I;IAC7I,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,6EAA6E;IAC7E,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;IACjG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,KAAK,4FAQf,UAAU,gCA4CZ,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
2
|
import { Sentiment } from '../common';
|
|
3
3
|
export interface InlineAlertProps {
|
|
4
4
|
id?: string;
|
|
@@ -6,5 +6,5 @@ export interface InlineAlertProps {
|
|
|
6
6
|
className?: string;
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
}
|
|
9
|
-
export default function InlineAlert({ id, type, className, children, }: InlineAlertProps):
|
|
9
|
+
export default function InlineAlert({ id, type, className, children, }: InlineAlertProps): React.JSX.Element;
|
|
10
10
|
//# sourceMappingURL=InlineAlert.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InlineAlert.d.ts","sourceRoot":"","sources":["../../../src/inlineAlert/InlineAlert.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"InlineAlert.d.ts","sourceRoot":"","sources":["../../../src/inlineAlert/InlineAlert.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzC,OAAO,EAAE,SAAS,EAAQ,MAAM,WAAW,CAAC;AAG5C,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,EAAE,EACF,IAAgB,EAChB,SAAS,EACT,QAAQ,GACT,EAAE,gBAAgB,qBAmBlB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@transferwise/components",
|
|
3
|
-
"version": "46.
|
|
3
|
+
"version": "46.41.0",
|
|
4
4
|
"description": "Neptune React components",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -92,9 +92,9 @@
|
|
|
92
92
|
"react-intl": "^6.6.6",
|
|
93
93
|
"rollup": "^4.17.2",
|
|
94
94
|
"storybook": "^8.1.10",
|
|
95
|
-
"@transferwise/
|
|
96
|
-
"@
|
|
97
|
-
"@
|
|
95
|
+
"@transferwise/less-config": "3.1.0",
|
|
96
|
+
"@transferwise/neptune-css": "14.11.0",
|
|
97
|
+
"@wise/components-theming": "1.3.0"
|
|
98
98
|
},
|
|
99
99
|
"peerDependencies": {
|
|
100
100
|
"@transferwise/icons": "^3.7.0",
|
|
@@ -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'] });
|