@transferwise/components 46.28.0 → 46.29.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 +130 -630
- package/build/index.js.map +1 -1
- package/build/index.mjs +132 -630
- package/build/index.mjs.map +1 -1
- package/build/main.css +16 -5
- package/build/styles/logo/Logo.css +16 -0
- package/build/styles/main.css +16 -5
- package/build/types/alert/Alert.d.ts.map +1 -1
- package/build/types/dimmer/Dimmer.d.ts.map +1 -1
- package/build/types/index.d.ts +0 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/inputs/_BottomSheet.d.ts.map +1 -1
- package/build/types/inputs/_Popover.d.ts.map +1 -1
- package/build/types/loader/Loader.d.ts.map +1 -1
- package/build/types/logo/Logo.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
- package/build/types/popover/Popover.d.ts.map +1 -1
- package/build/types/segmentedControl/SegmentedControl.d.ts +2 -2
- package/build/types/segmentedControl/SegmentedControl.d.ts.map +1 -1
- package/build/types/select/Select.d.ts.map +1 -1
- package/build/types/stepper/deviceDetection.d.ts.map +1 -1
- package/build/types/uploadInput/uploadButton/UploadButton.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/accordion/Accordion.story.tsx +1 -1
- package/src/alert/Alert.tsx +2 -1
- package/src/avatar/colors/colors.ts +1 -1
- package/src/body/Body.spec.tsx +1 -1
- package/src/body/Body.story.tsx +8 -8
- package/src/checkbox/Checkbox.js +1 -1
- package/src/checkboxButton/CheckboxButton.spec.tsx +0 -1
- package/src/common/Option/Option.tsx +1 -1
- package/src/common/deviceDetection/deviceDetection.js +1 -1
- package/src/common/deviceDetection/deviceDetection.spec.js +4 -2
- package/src/common/responsivePanel/ResponsivePanel.spec.js +11 -15
- package/src/decision/Decision.spec.js +0 -1
- package/src/dimmer/Dimmer.tsx +6 -2
- package/src/index.ts +0 -1
- package/src/inlineAlert/InlineAlert.story.tsx +8 -7
- package/src/inputs/SelectInput.tsx +1 -0
- package/src/inputs/_BottomSheet.tsx +33 -28
- package/src/inputs/_Popover.tsx +23 -20
- package/src/link/Link.story.tsx +16 -16
- package/src/loader/Loader.tsx +0 -1
- package/src/logo/Logo.css +16 -0
- package/src/logo/Logo.js +4 -9
- package/src/logo/Logo.less +16 -0
- package/src/logo/__snapshots__/Logo.spec.js.snap +104 -8
- package/src/main.css +16 -5
- package/src/main.less +0 -1
- package/src/moneyInput/MoneyInput.story.tsx +3 -3
- package/src/nudge/Nudge.spec.tsx +5 -5
- package/src/phoneNumberInput/PhoneNumberInput.tsx +2 -1
- package/src/phoneNumberInput/utils/cleanNumber/cleanNumber.ts +1 -1
- package/src/popover/Popover.tsx +2 -1
- package/src/promoCard/PromoCard.tsx +1 -1
- package/src/radioGroup/RadioGroup.spec.js +1 -1
- package/src/section/Section.story.tsx +2 -1
- package/src/segmentedControl/SegmentedControl.spec.tsx +88 -2
- package/src/segmentedControl/SegmentedControl.story.tsx +54 -16
- package/src/segmentedControl/SegmentedControl.tsx +21 -33
- package/src/select/Select.js +2 -3
- package/src/stepper/deviceDetection.js +1 -2
- package/src/stepper/deviceDetection.spec.js +8 -3
- package/src/test-utils/index.js +1 -1
- package/src/test-utils/story-config.ts +1 -1
- package/src/title/Title.spec.tsx +1 -1
- package/src/typeahead/Typeahead.spec.js +4 -2
- package/src/upload/Upload.spec.js +8 -4
- package/src/uploadInput/uploadButton/UploadButton.tsx +1 -0
- package/build/styles/dynamicFieldDefinitionList/FormattedValue/FormattedValue.css +0 -5
- package/build/types/common/requirements.d.ts +0 -3
- package/build/types/common/requirements.d.ts.map +0 -1
- package/build/types/dynamicFieldDefinitionList/DynamicFieldDefinitionList.d.ts +0 -21
- package/build/types/dynamicFieldDefinitionList/DynamicFieldDefinitionList.d.ts.map +0 -1
- package/build/types/dynamicFieldDefinitionList/FormattedValue/FormattedValue.d.ts +0 -12
- package/build/types/dynamicFieldDefinitionList/FormattedValue/FormattedValue.d.ts.map +0 -1
- package/build/types/dynamicFieldDefinitionList/FormattedValue/index.d.ts +0 -2
- package/build/types/dynamicFieldDefinitionList/FormattedValue/index.d.ts.map +0 -1
- package/build/types/dynamicFieldDefinitionList/index.d.ts +0 -2
- package/build/types/dynamicFieldDefinitionList/index.d.ts.map +0 -1
- package/build/types/dynamicFieldDefinitionList/utils/createDefinitions.d.ts +0 -2
- package/build/types/dynamicFieldDefinitionList/utils/createDefinitions.d.ts.map +0 -1
- package/build/types/dynamicFieldDefinitionList/utils/text-format.d.ts +0 -2
- package/build/types/dynamicFieldDefinitionList/utils/text-format.d.ts.map +0 -1
- package/src/dynamicFieldDefinitionList/DynamicFieldDefinitionList.js +0 -41
- package/src/dynamicFieldDefinitionList/DynamicFieldDefinitionList.spec.js +0 -21
- package/src/dynamicFieldDefinitionList/DynamicFieldDefinitionList.story.js +0 -134
- package/src/dynamicFieldDefinitionList/FormattedValue/FormattedValue.css +0 -5
- package/src/dynamicFieldDefinitionList/FormattedValue/FormattedValue.js +0 -73
- package/src/dynamicFieldDefinitionList/FormattedValue/FormattedValue.less +0 -4
- package/src/dynamicFieldDefinitionList/FormattedValue/FormattedValue.spec.js +0 -200
- package/src/dynamicFieldDefinitionList/FormattedValue/index.js +0 -1
- package/src/dynamicFieldDefinitionList/index.js +0 -1
- package/src/dynamicFieldDefinitionList/utils/createDefinitions.js +0 -33
- package/src/dynamicFieldDefinitionList/utils/createDefinitions.spec.js +0 -83
- package/src/dynamicFieldDefinitionList/utils/text-format.js +0 -46
- package/src/dynamicFieldDefinitionList/utils/text-format.spec.js +0 -43
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import '@testing-library/jest-dom';
|
|
2
|
-
import
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { render, screen, userEvent, waitFor } from '../test-utils';
|
|
3
5
|
|
|
4
6
|
import SegmentedControl, { SegmentedControlProps } from './SegmentedControl';
|
|
5
7
|
|
|
@@ -30,7 +32,7 @@ const onChange = jest.fn();
|
|
|
30
32
|
|
|
31
33
|
const defaultProps: SegmentedControlProps = {
|
|
32
34
|
name: 'segmentedControl',
|
|
33
|
-
|
|
35
|
+
value: defaultSegments[0].value,
|
|
34
36
|
mode: 'input',
|
|
35
37
|
segments: defaultSegments,
|
|
36
38
|
onChange,
|
|
@@ -86,6 +88,90 @@ describe('SegmentedControl', () => {
|
|
|
86
88
|
expect(onChange).toHaveBeenCalledWith('reporting');
|
|
87
89
|
});
|
|
88
90
|
|
|
91
|
+
it('does not call onChange on mount', async () => {
|
|
92
|
+
let onChangeCallCount = 0;
|
|
93
|
+
|
|
94
|
+
const ParentComponent = () => {
|
|
95
|
+
const [_, simulateRerender] = React.useState({});
|
|
96
|
+
|
|
97
|
+
// new function is created on every render
|
|
98
|
+
const onChange = () => {
|
|
99
|
+
onChangeCallCount += 1;
|
|
100
|
+
simulateRerender({});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return <SegmentedControl {...defaultProps} onChange={onChange} />;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
render(<ParentComponent />);
|
|
107
|
+
|
|
108
|
+
await waitFor(() => {
|
|
109
|
+
expect(onChangeCallCount).toBe(0);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('does not repeatedly call the onChange when the provided onChange prop changes on every render of its parent component', async () => {
|
|
114
|
+
let onChangeCallCount = 0;
|
|
115
|
+
|
|
116
|
+
const ParentComponent = () => {
|
|
117
|
+
const [_, simulateRerender] = React.useState({});
|
|
118
|
+
|
|
119
|
+
// a new onChange function is created on every render
|
|
120
|
+
const onChange = () => {
|
|
121
|
+
onChangeCallCount += 1;
|
|
122
|
+
simulateRerender({});
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return <SegmentedControl {...defaultProps} onChange={onChange} />;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const { rerender } = render(<ParentComponent />);
|
|
129
|
+
|
|
130
|
+
rerender(<ParentComponent />);
|
|
131
|
+
|
|
132
|
+
await waitFor(() => {
|
|
133
|
+
expect(onChangeCallCount).toBe(0);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('updates the selected segment when the selectedValue prop changes', () => {
|
|
138
|
+
const { rerender } = render(<SegmentedControl {...defaultProps} />);
|
|
139
|
+
|
|
140
|
+
const payroll = screen.getByRole('radio', { name: 'Payroll' });
|
|
141
|
+
userEvent.click(payroll);
|
|
142
|
+
|
|
143
|
+
expect(onChange).toHaveBeenCalledWith('payroll');
|
|
144
|
+
|
|
145
|
+
rerender(<SegmentedControl {...defaultProps} value="reporting" />);
|
|
146
|
+
|
|
147
|
+
const reporting = screen.getByRole('radio', { name: 'Reporting' });
|
|
148
|
+
expect(reporting).toBeChecked();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('updates the options when the segments prop changes', () => {
|
|
152
|
+
const { rerender } = render(<SegmentedControl {...defaultProps} />);
|
|
153
|
+
|
|
154
|
+
const newSegments = [
|
|
155
|
+
{
|
|
156
|
+
id: '1',
|
|
157
|
+
value: 'payroll',
|
|
158
|
+
label: 'Payroll',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: '3',
|
|
162
|
+
value: 'anotherOne',
|
|
163
|
+
label: 'Another One',
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
|
|
167
|
+
rerender(<SegmentedControl {...defaultProps} segments={newSegments} />);
|
|
168
|
+
|
|
169
|
+
const anotherOne = screen.getByRole('radio', { name: 'Another One' });
|
|
170
|
+
userEvent.click(anotherOne);
|
|
171
|
+
|
|
172
|
+
expect(onChange).toHaveBeenCalledWith('anotherOne');
|
|
173
|
+
});
|
|
174
|
+
|
|
89
175
|
it('throws error if user tries to add too many segments', () => {
|
|
90
176
|
expect(() => {
|
|
91
177
|
renderSegmentedControl({
|
|
@@ -1,40 +1,78 @@
|
|
|
1
1
|
import { StoryFn } from '@storybook/react';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import Button from '../button';
|
|
5
|
+
|
|
6
|
+
import SegmentedControl from './SegmentedControl';
|
|
5
7
|
|
|
6
8
|
export default {
|
|
7
9
|
component: SegmentedControl,
|
|
8
10
|
title: 'Forms/SegmentedControl',
|
|
9
11
|
};
|
|
10
12
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
const Template: StoryFn = (args) => {
|
|
14
|
+
const [segments, setSegments] = React.useState([
|
|
15
|
+
{ id: 'CUPCAKE', label: 'Cupcakes', value: 'cupcakes' },
|
|
16
|
+
{ id: 'SPONGECAKE', label: 'Sponge cake', value: 'spongecake' },
|
|
17
|
+
{ id: 'CARROT_CAKE', label: 'Carrot cake', value: 'carrotcake' },
|
|
18
|
+
]);
|
|
16
19
|
|
|
17
|
-
const segmentsWithControls
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
];
|
|
20
|
+
const [segmentsWithControls, setSegmentsWithControls] = React.useState([
|
|
21
|
+
{ id: 'CUPCAKE', label: 'Cupcakes', value: 'cupcakes', controls: 'aControlId' },
|
|
22
|
+
{ id: 'SPONGECAKE', label: 'Sponge cake', value: 'spongecake', controls: 'aControlId' },
|
|
23
|
+
{ id: 'CARROT_CAKE', label: 'Carrot cake', value: 'carrotcake', controls: 'aControlId' },
|
|
24
|
+
]);
|
|
22
25
|
|
|
23
|
-
const
|
|
24
|
-
const [selectedValue, setSelectedValue] = React.useState(segments[0].value);
|
|
26
|
+
const [value, setValue] = React.useState(segments[0].value);
|
|
25
27
|
|
|
28
|
+
console.log('render: segments.length', segments.length);
|
|
26
29
|
return (
|
|
27
30
|
<div className="p-a-2">
|
|
28
31
|
<SegmentedControl
|
|
29
32
|
name="aSegmentedControl"
|
|
30
|
-
|
|
31
|
-
onChange={
|
|
33
|
+
value={value}
|
|
34
|
+
onChange={setValue}
|
|
32
35
|
{...(args.mode === 'view'
|
|
33
36
|
? { segments: segmentsWithControls, mode: 'view', controls: 'aControlId' }
|
|
34
37
|
: { segments, mode: 'input' })}
|
|
35
38
|
/>
|
|
36
39
|
<div className="m-a-2" id="aControlId">
|
|
37
|
-
<p>Selected value: {
|
|
40
|
+
<p>Selected value: {value}</p>
|
|
41
|
+
</div>
|
|
42
|
+
<div className="m-a-2">
|
|
43
|
+
<p>
|
|
44
|
+
Force the <b>selectedValue</b> to be one of the following:
|
|
45
|
+
<ul>
|
|
46
|
+
{segments.map((segment) => (
|
|
47
|
+
<li key={segment.id}>
|
|
48
|
+
<a
|
|
49
|
+
href="/"
|
|
50
|
+
onClick={(e) => {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
setValue(segment.value);
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{segment.label}
|
|
56
|
+
</a>
|
|
57
|
+
</li>
|
|
58
|
+
))}
|
|
59
|
+
</ul>
|
|
60
|
+
</p>
|
|
61
|
+
</div>
|
|
62
|
+
<div className="m-a-2">
|
|
63
|
+
<Button
|
|
64
|
+
priority="secondary"
|
|
65
|
+
type="danger"
|
|
66
|
+
size="sm"
|
|
67
|
+
disabled={segments.length < 2}
|
|
68
|
+
onClick={() => {
|
|
69
|
+
const index = segments.findIndex((s) => s.value !== value);
|
|
70
|
+
setSegments((prev) => prev.filter((_, i) => i !== index));
|
|
71
|
+
setSegmentsWithControls((prev) => prev.filter((_, i) => i !== index));
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
Remove one segment
|
|
75
|
+
</Button>
|
|
38
76
|
</div>
|
|
39
77
|
</div>
|
|
40
78
|
);
|
|
@@ -13,7 +13,7 @@ export type Segments = readonly Segment[] | readonly SegmentWithControls[];
|
|
|
13
13
|
|
|
14
14
|
type SegmentedControlPropsBase = {
|
|
15
15
|
name: string;
|
|
16
|
-
|
|
16
|
+
value: string;
|
|
17
17
|
mode: 'input' | 'view';
|
|
18
18
|
onChange: (value: string) => void;
|
|
19
19
|
};
|
|
@@ -33,12 +33,11 @@ export type SegmentedControlProps = SegmentedControlPropsBase &
|
|
|
33
33
|
|
|
34
34
|
const SegmentedControl = ({
|
|
35
35
|
name,
|
|
36
|
-
|
|
36
|
+
value,
|
|
37
37
|
mode = 'input',
|
|
38
38
|
segments,
|
|
39
39
|
onChange,
|
|
40
40
|
}: SegmentedControlProps) => {
|
|
41
|
-
const [selectedValue, setSelectedValue] = useState(defaultValue || segments[0].value);
|
|
42
41
|
const [animate, setAnimate] = useState(false);
|
|
43
42
|
|
|
44
43
|
const segmentsRef = useRef<HTMLDivElement>(null);
|
|
@@ -55,9 +54,7 @@ const SegmentedControl = ({
|
|
|
55
54
|
}));
|
|
56
55
|
|
|
57
56
|
const updateSegmentPosition = () => {
|
|
58
|
-
const selectedSegmentRef = segmentsWithRefs.find(
|
|
59
|
-
(segment) => segment.value === selectedValue,
|
|
60
|
-
)?.ref;
|
|
57
|
+
const selectedSegmentRef = segmentsWithRefs.find((segment) => segment.value === value)?.ref;
|
|
61
58
|
|
|
62
59
|
// We grab the active segments style object from the ref
|
|
63
60
|
// and set the css variables to the selected segments width and x position.
|
|
@@ -70,6 +67,7 @@ const SegmentedControl = ({
|
|
|
70
67
|
};
|
|
71
68
|
|
|
72
69
|
useEffect(() => {
|
|
70
|
+
setAnimate(true);
|
|
73
71
|
updateSegmentPosition();
|
|
74
72
|
|
|
75
73
|
const handleWindowSizeChange = () => {
|
|
@@ -83,11 +81,7 @@ const SegmentedControl = ({
|
|
|
83
81
|
};
|
|
84
82
|
|
|
85
83
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
86
|
-
}, [segmentsWithRefs,
|
|
87
|
-
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
onChange(selectedValue);
|
|
90
|
-
}, [onChange, selectedValue]);
|
|
84
|
+
}, [segmentsWithRefs, value]);
|
|
91
85
|
|
|
92
86
|
return (
|
|
93
87
|
<div
|
|
@@ -103,14 +97,18 @@ const SegmentedControl = ({
|
|
|
103
97
|
})}
|
|
104
98
|
role={mode !== 'input' ? 'tablist' : undefined}
|
|
105
99
|
>
|
|
106
|
-
{segmentsWithRefs.map((segment) =>
|
|
107
|
-
|
|
100
|
+
{segmentsWithRefs.map((segment) => {
|
|
101
|
+
const onSelect = () => {
|
|
102
|
+
setAnimate(true);
|
|
103
|
+
onChange(segment.value);
|
|
104
|
+
};
|
|
105
|
+
return mode === 'input' ? (
|
|
108
106
|
<label
|
|
109
107
|
ref={segment.ref as React.RefObject<HTMLLabelElement>}
|
|
110
108
|
key={segment.id}
|
|
111
109
|
htmlFor={segment.id}
|
|
112
110
|
className={classNames('segmented-control__segment', {
|
|
113
|
-
'segmented-control__selected-segment':
|
|
111
|
+
'segmented-control__selected-segment': value === segment.value,
|
|
114
112
|
})}
|
|
115
113
|
>
|
|
116
114
|
<input
|
|
@@ -119,19 +117,14 @@ const SegmentedControl = ({
|
|
|
119
117
|
id={segment.id}
|
|
120
118
|
name={name}
|
|
121
119
|
value={segment.value}
|
|
122
|
-
checked={
|
|
123
|
-
onChange={
|
|
124
|
-
setAnimate(true);
|
|
125
|
-
setSelectedValue(segment.value);
|
|
126
|
-
}}
|
|
120
|
+
checked={value === segment.value}
|
|
121
|
+
onChange={onSelect}
|
|
127
122
|
/>
|
|
128
123
|
<Body
|
|
129
124
|
className="segmented-control__text"
|
|
130
125
|
as="span"
|
|
131
126
|
type={
|
|
132
|
-
|
|
133
|
-
? Typography.BODY_DEFAULT_BOLD
|
|
134
|
-
: Typography.BODY_DEFAULT
|
|
127
|
+
value === segment.value ? Typography.BODY_DEFAULT_BOLD : Typography.BODY_DEFAULT
|
|
135
128
|
}
|
|
136
129
|
>
|
|
137
130
|
{segment.label}
|
|
@@ -145,29 +138,24 @@ const SegmentedControl = ({
|
|
|
145
138
|
role="tab"
|
|
146
139
|
id={segment.id}
|
|
147
140
|
aria-controls={segment.controls}
|
|
148
|
-
aria-selected={
|
|
141
|
+
aria-selected={value === segment.value}
|
|
149
142
|
className={classNames('segmented-control__segment', 'segmented-control__button', {
|
|
150
|
-
'segmented-control__selected-segment':
|
|
143
|
+
'segmented-control__selected-segment': value === segment.value,
|
|
151
144
|
})}
|
|
152
|
-
onClick={
|
|
153
|
-
setAnimate(true);
|
|
154
|
-
setSelectedValue(segment.value);
|
|
155
|
-
}}
|
|
145
|
+
onClick={onSelect}
|
|
156
146
|
>
|
|
157
147
|
<Body
|
|
158
148
|
as="span"
|
|
159
149
|
className="segmented-control__text"
|
|
160
150
|
type={
|
|
161
|
-
|
|
162
|
-
? Typography.BODY_DEFAULT_BOLD
|
|
163
|
-
: Typography.BODY_DEFAULT
|
|
151
|
+
value === segment.value ? Typography.BODY_DEFAULT_BOLD : Typography.BODY_DEFAULT
|
|
164
152
|
}
|
|
165
153
|
>
|
|
166
154
|
{segment.label}
|
|
167
155
|
</Body>
|
|
168
156
|
</button>
|
|
169
|
-
)
|
|
170
|
-
)}
|
|
157
|
+
);
|
|
158
|
+
})}
|
|
171
159
|
</div>
|
|
172
160
|
</div>
|
|
173
161
|
);
|
package/src/select/Select.js
CHANGED
|
@@ -246,8 +246,8 @@ export default function Select({
|
|
|
246
246
|
};
|
|
247
247
|
|
|
248
248
|
function selectKeyboardFocusedOption() {
|
|
249
|
-
if (keyboardFocusedOptionIndex != null) {
|
|
250
|
-
|
|
249
|
+
if (keyboardFocusedOptionIndex != null && selectableOptions.length > 0) {
|
|
250
|
+
selectOption(selectableOptions[keyboardFocusedOptionIndex]);
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
@@ -511,7 +511,6 @@ export default function Select({
|
|
|
511
511
|
disabled={disabled}
|
|
512
512
|
aria-controls={listboxId}
|
|
513
513
|
aria-expanded={open}
|
|
514
|
-
aria-autocomplete="none"
|
|
515
514
|
onClick={handleOnClick}
|
|
516
515
|
{...buttonProps}
|
|
517
516
|
>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
function supportsTouchEvents() {
|
|
2
2
|
const onTouchStartIsDefined = typeof window !== 'undefined' && window.ontouchstart !== undefined;
|
|
3
|
-
// eslint-disable-next-line compat/compat
|
|
4
3
|
const maxTouchPointsIsDefined = typeof navigator !== 'undefined' && navigator.maxTouchPoints;
|
|
5
4
|
const documentTouchIsDefined =
|
|
6
5
|
typeof window !== 'undefined' &&
|
|
@@ -21,7 +20,7 @@ function userAgentSuggestsTouchDevice() {
|
|
|
21
20
|
'bada',
|
|
22
21
|
];
|
|
23
22
|
const matchString = sampleTouchDevices.map((device) => `(${device})`).join('|');
|
|
24
|
-
const regex = new RegExp(matchString, '
|
|
23
|
+
const regex = new RegExp(matchString, 'gi');
|
|
25
24
|
return typeof navigator !== 'undefined' && !!navigator.userAgent.match(regex);
|
|
26
25
|
}
|
|
27
26
|
// Important: this is not fool-proof! It gives false positives and negatives, and will be outdated.
|
|
@@ -2,12 +2,17 @@ import { isTouchDevice } from './deviceDetection';
|
|
|
2
2
|
|
|
3
3
|
describe('Device detection', () => {
|
|
4
4
|
function fakeUserAgent(userAgent) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Object.defineProperty(navigator, 'userAgent', {
|
|
6
|
+
value: userAgent,
|
|
7
|
+
configurable: true,
|
|
8
|
+
});
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
function fakeMaxTouchPoints(maxTouchPoints) {
|
|
10
|
-
|
|
12
|
+
Object.defineProperty(navigator, 'maxTouchPoints', {
|
|
13
|
+
value: maxTouchPoints,
|
|
14
|
+
configurable: true,
|
|
15
|
+
});
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
// We don't test DocumentTouch api as it's basically impossible to test :(
|
package/src/test-utils/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import en from '../i18n/en.json';
|
|
|
13
13
|
*/
|
|
14
14
|
function customRender(ui, { locale = DEFAULT_LOCALE, messages = en, ...renderOptions } = {}) {
|
|
15
15
|
// eslint-disable-next-line react/prop-types
|
|
16
|
-
|
|
16
|
+
const Wrapper = ({ children }) => {
|
|
17
17
|
return <Provider i18n={{ locale, messages }}>{children}</Provider>;
|
|
18
18
|
};
|
|
19
19
|
return render(ui, { wrapper: Wrapper, ...renderOptions });
|
|
@@ -30,7 +30,7 @@ interface StoryConfig {
|
|
|
30
30
|
|
|
31
31
|
const getViewportWidth = (viewport: (typeof viewports)[keyof typeof viewports]) => {
|
|
32
32
|
if (viewport.styles && typeof viewport.styles === 'object') {
|
|
33
|
-
return parseInt(viewport.styles.width);
|
|
33
|
+
return Number.parseInt(viewport.styles.width, 10);
|
|
34
34
|
}
|
|
35
35
|
throw new Error('Unknown viewport styles');
|
|
36
36
|
};
|
package/src/title/Title.spec.tsx
CHANGED
|
@@ -34,7 +34,7 @@ describe('Title', () => {
|
|
|
34
34
|
|
|
35
35
|
it('handles unsupported typography type', () => {
|
|
36
36
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
37
|
-
// @ts-
|
|
37
|
+
// @ts-expect-error
|
|
38
38
|
render(<Title type={Typography.BODY_LARGE_BOLD}>Test</Title>);
|
|
39
39
|
const copy = screen.getByText('Test');
|
|
40
40
|
expect(copy).toBeInTheDocument();
|
|
@@ -314,7 +314,9 @@ describe('Typeahead', () => {
|
|
|
314
314
|
let selectedOption;
|
|
315
315
|
|
|
316
316
|
component.setProps({
|
|
317
|
-
onChange: (selections) =>
|
|
317
|
+
onChange: (selections) => {
|
|
318
|
+
selectedOption = selections[0];
|
|
319
|
+
},
|
|
318
320
|
options: options,
|
|
319
321
|
});
|
|
320
322
|
|
|
@@ -340,7 +342,7 @@ describe('Typeahead', () => {
|
|
|
340
342
|
it('renders all options', () => {
|
|
341
343
|
const options = option().map((optNode) => optNode.text());
|
|
342
344
|
expect(options).toHaveLength(props.options.length);
|
|
343
|
-
expect(options.every((label, i) => label === props.options[i].label));
|
|
345
|
+
expect(options.every((label, i) => label === props.options[i].label)).toBe(true);
|
|
344
346
|
});
|
|
345
347
|
|
|
346
348
|
it('does not render new option if showNewEntry is false', () => {
|
|
@@ -8,7 +8,7 @@ import Upload from '.';
|
|
|
8
8
|
|
|
9
9
|
jest.useFakeTimers();
|
|
10
10
|
jest.mock('./utils/postData', () => ({
|
|
11
|
-
postData:
|
|
11
|
+
postData: async () => 'ServerResponse',
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
14
|
jest.mock('./utils/asyncFileRead');
|
|
@@ -88,7 +88,7 @@ describe('Upload', () => {
|
|
|
88
88
|
let component;
|
|
89
89
|
beforeEach(() => {
|
|
90
90
|
component = shallow(<Upload {...props} />).dive();
|
|
91
|
-
asyncFileRead.mockImplementation(
|
|
91
|
+
asyncFileRead.mockImplementation(async () => 'a value');
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
afterEach(() => {
|
|
@@ -183,7 +183,9 @@ describe('Upload', () => {
|
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
it('step ProcessingStep is called with error props', async () => {
|
|
186
|
-
asyncFileRead.mockImplementation(
|
|
186
|
+
asyncFileRead.mockImplementation(async () => {
|
|
187
|
+
throw 'An error';
|
|
188
|
+
});
|
|
187
189
|
|
|
188
190
|
await component.instance().fileDropped(TEST_FILE);
|
|
189
191
|
jest.advanceTimersByTime(props.animationDelay);
|
|
@@ -230,7 +232,9 @@ describe('Upload', () => {
|
|
|
230
232
|
it('step CompleteStep is called with error props', async () => {
|
|
231
233
|
component = mount(<Upload {...props} />);
|
|
232
234
|
const upload = component.children();
|
|
233
|
-
asyncFileRead.mockImplementation(
|
|
235
|
+
asyncFileRead.mockImplementation(async () => {
|
|
236
|
+
throw 'An error';
|
|
237
|
+
});
|
|
234
238
|
|
|
235
239
|
await upload.instance().fileDropped(TEST_FILE);
|
|
236
240
|
jest.advanceTimersByTime(props.animationDelay + ANIMATION_DELAY);
|
|
@@ -225,6 +225,7 @@ const UploadButton = ({
|
|
|
225
225
|
data-testid={TEST_IDS.uploadInput}
|
|
226
226
|
onChange={filesSelected}
|
|
227
227
|
/>
|
|
228
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
|
228
229
|
<label
|
|
229
230
|
htmlFor={id}
|
|
230
231
|
className={classNames('btn', 'np-upload-accent', 'np-upload-button', {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"requirements.d.ts","sourceRoot":"","sources":["../../../src/common/requirements.js"],"names":[],"mappings":"AAqCA,kFAgBC;AAuTD,gDAsBC"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
export interface DynamicFieldDefinitionListModel {
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export interface DynamicFieldDefinitionListFields {
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type DynamicFieldDefinitionListLayout = "VERTICAL_TWO_COLUMN" | "VERTICAL_ONE_COLUMN" | "HORIZONTAL_JUSTIFIED" | "HORIZONTAL_LEFT_ALIGNED";
|
|
10
|
-
|
|
11
|
-
export interface DynamicFieldDefinitionListProps {
|
|
12
|
-
model: DynamicFieldDefinitionListModel;
|
|
13
|
-
fields: DynamicFieldDefinitionListFields;
|
|
14
|
-
title?: string;
|
|
15
|
-
layout?: DynamicFieldDefinitionListLayout;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
declare const DynamicFieldDefinitionList: React.FC<DynamicFieldDefinitionListProps>;
|
|
19
|
-
|
|
20
|
-
export default DynamicFieldDefinitionList;
|
|
21
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DynamicFieldDefinitionList.d.ts","sourceRoot":"","sources":["../../../src/dynamicFieldDefinitionList/DynamicFieldDefinitionList.js"],"names":[],"mappings":";AASA;;;;;gCAYC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export default FormattedValue;
|
|
2
|
-
declare function FormattedValue({ field, value }: {
|
|
3
|
-
field: any;
|
|
4
|
-
value: any;
|
|
5
|
-
}): import("react").JSX.Element;
|
|
6
|
-
declare namespace FormattedValue {
|
|
7
|
-
namespace propTypes {
|
|
8
|
-
const field: any;
|
|
9
|
-
const value: any;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
//# sourceMappingURL=FormattedValue.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FormattedValue.d.ts","sourceRoot":"","sources":["../../../../src/dynamicFieldDefinitionList/FormattedValue/FormattedValue.js"],"names":[],"mappings":";AAuBA;;;gCA+BC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/dynamicFieldDefinitionList/FormattedValue/index.js"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dynamicFieldDefinitionList/index.js"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createDefinitions.d.ts","sourceRoot":"","sources":["../../../../src/dynamicFieldDefinitionList/utils/createDefinitions.js"],"names":[],"mappings":"AAIA,wEAIC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text-format.d.ts","sourceRoot":"","sources":["../../../../src/dynamicFieldDefinitionList/utils/text-format.js"],"names":[],"mappings":"AAAA,oFAgCC"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import PropTypes from 'prop-types';
|
|
2
|
-
|
|
3
|
-
import { Typography, Layout } from '../common';
|
|
4
|
-
import { prepFields } from '../common/requirements';
|
|
5
|
-
import DefinitionList from '../definitionList';
|
|
6
|
-
import Title from '../title';
|
|
7
|
-
|
|
8
|
-
import createDefinitions from './utils/createDefinitions';
|
|
9
|
-
|
|
10
|
-
const DynamicFieldDefinitionList = ({ model, title, layout, fields }) => (
|
|
11
|
-
<>
|
|
12
|
-
{title && (
|
|
13
|
-
<div className="m-t-1 m-b-3">
|
|
14
|
-
<Title type={Typography.TITLE_BODY} className="p-t-3">
|
|
15
|
-
{title}
|
|
16
|
-
</Title>
|
|
17
|
-
</div>
|
|
18
|
-
)}
|
|
19
|
-
|
|
20
|
-
<DefinitionList layout={layout} definitions={createDefinitions(prepFields(fields), model)} />
|
|
21
|
-
</>
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
DynamicFieldDefinitionList.propTypes = {
|
|
25
|
-
model: PropTypes.shape({}).isRequired,
|
|
26
|
-
fields: PropTypes.shape({}).isRequired,
|
|
27
|
-
title: PropTypes.string,
|
|
28
|
-
layout: PropTypes.oneOf([
|
|
29
|
-
'VERTICAL_TWO_COLUMN',
|
|
30
|
-
'VERTICAL_ONE_COLUMN',
|
|
31
|
-
'HORIZONTAL_JUSTIFIED',
|
|
32
|
-
'HORIZONTAL_LEFT_ALIGNED',
|
|
33
|
-
]),
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
DynamicFieldDefinitionList.defaultProps = {
|
|
37
|
-
title: null,
|
|
38
|
-
layout: Layout.VERTICAL_TWO_COLUMN,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export default DynamicFieldDefinitionList;
|