@transferwise/components 46.97.5 → 46.98.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/alert/Alert.js +8 -0
- package/build/alert/Alert.js.map +1 -1
- package/build/alert/Alert.mjs +8 -0
- package/build/alert/Alert.mjs.map +1 -1
- package/build/common/closeButton/CloseButton.js +3 -1
- package/build/common/closeButton/CloseButton.js.map +1 -1
- package/build/common/closeButton/CloseButton.mjs +3 -1
- package/build/common/closeButton/CloseButton.mjs.map +1 -1
- package/build/i18n/cs.json +3 -0
- package/build/i18n/cs.json.js +3 -0
- package/build/i18n/cs.json.js.map +1 -1
- package/build/i18n/cs.json.mjs +3 -0
- package/build/i18n/cs.json.mjs.map +1 -1
- package/build/i18n/de.json +3 -0
- package/build/i18n/de.json.js +3 -0
- package/build/i18n/de.json.js.map +1 -1
- package/build/i18n/de.json.mjs +3 -0
- package/build/i18n/de.json.mjs.map +1 -1
- package/build/i18n/en.json +3 -0
- package/build/i18n/en.json.js +3 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +3 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/i18n/es.json +3 -0
- package/build/i18n/es.json.js +3 -0
- package/build/i18n/es.json.js.map +1 -1
- package/build/i18n/es.json.mjs +3 -0
- package/build/i18n/es.json.mjs.map +1 -1
- package/build/i18n/fr.json +3 -0
- package/build/i18n/fr.json.js +3 -0
- package/build/i18n/fr.json.js.map +1 -1
- package/build/i18n/fr.json.mjs +3 -0
- package/build/i18n/fr.json.mjs.map +1 -1
- package/build/i18n/hu.json +3 -0
- package/build/i18n/hu.json.js +3 -0
- package/build/i18n/hu.json.js.map +1 -1
- package/build/i18n/hu.json.mjs +3 -0
- package/build/i18n/hu.json.mjs.map +1 -1
- package/build/i18n/id.json +3 -0
- package/build/i18n/id.json.js +3 -0
- package/build/i18n/id.json.js.map +1 -1
- package/build/i18n/id.json.mjs +3 -0
- package/build/i18n/id.json.mjs.map +1 -1
- package/build/i18n/it.json +3 -0
- package/build/i18n/it.json.js +3 -0
- package/build/i18n/it.json.js.map +1 -1
- package/build/i18n/it.json.mjs +3 -0
- package/build/i18n/it.json.mjs.map +1 -1
- package/build/i18n/ja.json +3 -0
- package/build/i18n/ja.json.js +3 -0
- package/build/i18n/ja.json.js.map +1 -1
- package/build/i18n/ja.json.mjs +3 -0
- package/build/i18n/ja.json.mjs.map +1 -1
- package/build/i18n/nl.json +6 -3
- package/build/i18n/pl.json +3 -0
- package/build/i18n/pl.json.js +3 -0
- package/build/i18n/pl.json.js.map +1 -1
- package/build/i18n/pl.json.mjs +3 -0
- package/build/i18n/pl.json.mjs.map +1 -1
- package/build/i18n/pt.json +3 -0
- package/build/i18n/pt.json.js +3 -0
- package/build/i18n/pt.json.js.map +1 -1
- package/build/i18n/pt.json.mjs +3 -0
- package/build/i18n/pt.json.mjs.map +1 -1
- package/build/i18n/ro.json +3 -0
- package/build/i18n/ro.json.js +3 -0
- package/build/i18n/ro.json.js.map +1 -1
- package/build/i18n/ro.json.mjs +3 -0
- package/build/i18n/ro.json.mjs.map +1 -1
- package/build/i18n/ru.json +3 -0
- package/build/i18n/ru.json.js +3 -0
- package/build/i18n/ru.json.js.map +1 -1
- package/build/i18n/ru.json.mjs +3 -0
- package/build/i18n/ru.json.mjs.map +1 -1
- package/build/i18n/th.json +3 -0
- package/build/i18n/th.json.js +3 -0
- package/build/i18n/th.json.js.map +1 -1
- package/build/i18n/th.json.mjs +3 -0
- package/build/i18n/th.json.mjs.map +1 -1
- package/build/i18n/tr.json +3 -0
- package/build/i18n/tr.json.js +3 -0
- package/build/i18n/tr.json.js.map +1 -1
- package/build/i18n/tr.json.mjs +3 -0
- package/build/i18n/tr.json.mjs.map +1 -1
- package/build/i18n/zh-CN.json +3 -0
- package/build/i18n/zh-CN.json.js +3 -0
- package/build/i18n/zh-CN.json.js.map +1 -1
- package/build/i18n/zh-CN.json.mjs +3 -0
- package/build/i18n/zh-CN.json.mjs.map +1 -1
- package/build/i18n/zh-HK.json +3 -0
- package/build/i18n/zh-HK.json.js +3 -0
- package/build/i18n/zh-HK.json.js.map +1 -1
- package/build/i18n/zh-HK.json.mjs +3 -0
- package/build/i18n/zh-HK.json.mjs.map +1 -1
- package/build/image/Image.js +9 -10
- package/build/image/Image.js.map +1 -1
- package/build/image/Image.mjs +11 -11
- package/build/image/Image.mjs.map +1 -1
- package/build/main.css +5 -2
- package/build/moneyInput/MoneyInput.js +2 -6
- package/build/moneyInput/MoneyInput.js.map +1 -1
- package/build/moneyInput/MoneyInput.messages.js +3 -0
- package/build/moneyInput/MoneyInput.messages.js.map +1 -1
- package/build/moneyInput/MoneyInput.messages.mjs +3 -0
- package/build/moneyInput/MoneyInput.messages.mjs.map +1 -1
- package/build/moneyInput/MoneyInput.mjs +2 -6
- package/build/moneyInput/MoneyInput.mjs.map +1 -1
- package/build/phoneNumberInput/PhoneNumberInput.js +36 -2
- package/build/phoneNumberInput/PhoneNumberInput.js.map +1 -1
- package/build/phoneNumberInput/PhoneNumberInput.messages.js +6 -0
- package/build/phoneNumberInput/PhoneNumberInput.messages.js.map +1 -1
- package/build/phoneNumberInput/PhoneNumberInput.messages.mjs +6 -0
- package/build/phoneNumberInput/PhoneNumberInput.messages.mjs.map +1 -1
- package/build/phoneNumberInput/PhoneNumberInput.mjs +36 -2
- package/build/phoneNumberInput/PhoneNumberInput.mjs.map +1 -1
- package/build/styles/circularButton/CircularButton.css +1 -0
- package/build/styles/dateInput/DateInput.css +2 -1
- package/build/styles/main.css +5 -2
- package/build/styles/uploadInput/uploadItem/UploadItem.css +2 -1
- package/build/types/alert/Alert.d.ts.map +1 -1
- package/build/types/common/closeButton/CloseButton.d.ts +2 -0
- package/build/types/common/closeButton/CloseButton.d.ts.map +1 -1
- package/build/types/image/Image.d.ts +0 -1
- package/build/types/image/Image.d.ts.map +1 -1
- package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
- package/build/types/moneyInput/MoneyInput.messages.d.ts +5 -0
- package/build/types/moneyInput/MoneyInput.messages.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.messages.d.ts +8 -0
- package/build/types/phoneNumberInput/PhoneNumberInput.messages.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +6 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/build/types/upload/Upload.d.ts +1 -2
- package/build/types/upload/Upload.d.ts.map +1 -1
- package/build/types/upload/steps/processingStep/processingStep.d.ts +1 -3
- package/build/types/upload/steps/processingStep/processingStep.d.ts.map +1 -1
- package/build/types/uploadInput/UploadInput.d.ts.map +1 -1
- package/build/types/uploadInput/uploadItem/UploadItem.d.ts +1 -1
- package/build/types/uploadInput/uploadItem/UploadItem.d.ts.map +1 -1
- package/build/types/withDisplayFormat/WithDisplayFormat.d.ts.map +1 -1
- package/build/upload/Upload.js +27 -43
- package/build/upload/Upload.js.map +1 -1
- package/build/upload/Upload.mjs +27 -43
- package/build/upload/Upload.mjs.map +1 -1
- package/build/upload/steps/processingStep/processingStep.js +1 -3
- package/build/upload/steps/processingStep/processingStep.js.map +1 -1
- package/build/upload/steps/processingStep/processingStep.mjs +1 -3
- package/build/upload/steps/processingStep/processingStep.mjs.map +1 -1
- package/build/uploadInput/UploadInput.js +55 -6
- package/build/uploadInput/UploadInput.js.map +1 -1
- package/build/uploadInput/UploadInput.mjs +55 -6
- package/build/uploadInput/UploadInput.mjs.map +1 -1
- package/build/uploadInput/uploadItem/UploadItem.js +12 -6
- package/build/uploadInput/uploadItem/UploadItem.js.map +1 -1
- package/build/uploadInput/uploadItem/UploadItem.mjs +12 -6
- package/build/uploadInput/uploadItem/UploadItem.mjs.map +1 -1
- package/build/withDisplayFormat/WithDisplayFormat.js +3 -2
- package/build/withDisplayFormat/WithDisplayFormat.js.map +1 -1
- package/build/withDisplayFormat/WithDisplayFormat.mjs +3 -2
- package/build/withDisplayFormat/WithDisplayFormat.mjs.map +1 -1
- package/package.json +6 -9
- package/src/alert/Alert.spec.tsx +11 -0
- package/src/alert/Alert.story.tsx +23 -9
- package/src/alert/Alert.tsx +14 -1
- package/src/circularButton/CircularButton.css +1 -0
- package/src/circularButton/CircularButton.less +1 -0
- package/src/circularButton/CircularButton.tests.story.tsx +23 -0
- package/src/common/closeButton/CloseButton.spec.tsx +13 -1
- package/src/common/closeButton/CloseButton.tsx +3 -0
- package/src/dateInput/DateInput.css +2 -1
- package/src/dateInput/DateInput.less +7 -4
- package/src/i18n/cs.json +3 -0
- package/src/i18n/de.json +3 -0
- package/src/i18n/en.json +3 -0
- package/src/i18n/es.json +3 -0
- package/src/i18n/fr.json +3 -0
- package/src/i18n/hu.json +3 -0
- package/src/i18n/id.json +3 -0
- package/src/i18n/it.json +3 -0
- package/src/i18n/ja.json +3 -0
- package/src/i18n/nl.json +6 -3
- package/src/i18n/pl.json +3 -0
- package/src/i18n/pt.json +3 -0
- package/src/i18n/ro.json +3 -0
- package/src/i18n/ru.json +3 -0
- package/src/i18n/th.json +3 -0
- package/src/i18n/tr.json +3 -0
- package/src/i18n/zh-CN.json +3 -0
- package/src/i18n/zh-HK.json +3 -0
- package/src/image/Image.spec.tsx +3 -3
- package/src/image/Image.tsx +10 -12
- package/src/main.css +5 -2
- package/src/moneyInput/MoneyInput.messages.ts +5 -0
- package/src/moneyInput/MoneyInput.spec.tsx +42 -5
- package/src/moneyInput/MoneyInput.story.tsx +11 -2
- package/src/moneyInput/MoneyInput.tsx +5 -7
- package/src/phoneNumberInput/PhoneNumberInput.messages.ts +8 -0
- package/src/phoneNumberInput/PhoneNumberInput.spec.tsx +77 -43
- package/src/phoneNumberInput/PhoneNumberInput.tsx +34 -2
- package/src/promoCard/__snapshots__/PromoCard.spec.tsx.snap +1 -0
- package/src/promoCard/__snapshots__/PromoCardGroup.spec.tsx.snap +2 -0
- package/src/test-utils/jest.setup.ts +0 -4
- package/src/typeahead/Typeahead.spec.tsx +182 -0
- package/src/typeahead/typeaheadInput/TypeaheadInput.spec.tsx +103 -0
- package/src/typeahead/util/highlight.spec.tsx +43 -0
- package/src/upload/Upload.spec.tsx +63 -0
- package/src/upload/Upload.story.tsx +0 -51
- package/src/upload/Upload.tests.story.tsx +93 -0
- package/src/upload/Upload.tsx +28 -49
- package/src/upload/steps/processingStep/processingStep.tsx +2 -7
- package/src/uploadInput/UploadInput.tsx +81 -8
- package/src/uploadInput/uploadItem/UploadItem.css +2 -1
- package/src/uploadInput/uploadItem/UploadItem.less +1 -1
- package/src/uploadInput/uploadItem/UploadItem.tsx +11 -6
- package/src/withDisplayFormat/WithDisplayFormat.spec.js +11 -15
- package/src/withDisplayFormat/WithDisplayFormat.tsx +3 -2
- package/src/typeahead/Typeahead.rtl.spec.tsx +0 -54
- package/src/typeahead/Typeahead.spec.js +0 -404
- package/src/typeahead/typeaheadInput/TypeaheadInput.spec.js +0 -74
- package/src/typeahead/typeaheadOption/TypeaheadOption.spec.js +0 -75
- package/src/typeahead/util/highlight.spec.js +0 -34
|
@@ -19,7 +19,7 @@ export type UploadItemProps = React.JSX.IntrinsicAttributes & {
|
|
|
19
19
|
singleFileUpload: boolean;
|
|
20
20
|
canDelete: boolean;
|
|
21
21
|
onDelete: () => void;
|
|
22
|
-
onFocus: () => void;
|
|
22
|
+
onFocus: (target?: 'button' | 'link') => void;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Callback to be called when the file link is clicked.
|
|
@@ -63,11 +63,15 @@ const UploadItem = forwardRef<UploadItemRef, UploadItemProps>(
|
|
|
63
63
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
64
64
|
|
|
65
65
|
useImperativeHandle<UploadItemRef, UploadItemRef>(ref, () => ({
|
|
66
|
-
focus: (): void => {
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
} else {
|
|
70
|
-
|
|
66
|
+
focus: (target?: 'button' | 'link'): void => {
|
|
67
|
+
if (target === 'button' && buttonRef.current) {
|
|
68
|
+
buttonRef.current.focus();
|
|
69
|
+
} else if (target === 'link' && linkRef.current) {
|
|
70
|
+
linkRef.current.focus();
|
|
71
|
+
} else if (buttonRef.current) {
|
|
72
|
+
buttonRef.current.focus();
|
|
73
|
+
} else if (linkRef.current) {
|
|
74
|
+
linkRef.current.focus();
|
|
71
75
|
}
|
|
72
76
|
},
|
|
73
77
|
id: file.id,
|
|
@@ -199,6 +203,7 @@ const UploadItem = forwardRef<UploadItemRef, UploadItemProps>(
|
|
|
199
203
|
tabIndex={0}
|
|
200
204
|
data-testid={`${file.id}-${TEST_IDS.action}`}
|
|
201
205
|
onClick={() => onDelete()}
|
|
206
|
+
// @ts-expect-error: handleFocus is not a standard FocusEventHandler, but required for parent logic
|
|
202
207
|
onFocus={handleFocus}
|
|
203
208
|
>
|
|
204
209
|
<Bin size={16} />
|
|
@@ -3,7 +3,7 @@ import { userEvent, render, screen, waitFor } from '../test-utils';
|
|
|
3
3
|
import { Input } from '../inputs/Input';
|
|
4
4
|
import WithDisplayFormat from '.';
|
|
5
5
|
|
|
6
|
-
const user = userEvent
|
|
6
|
+
const user = userEvent;
|
|
7
7
|
|
|
8
8
|
const setup = (props) =>
|
|
9
9
|
render(
|
|
@@ -22,15 +22,6 @@ describe('InputWithTextFormat (React Testing Library)', () => {
|
|
|
22
22
|
onBlur: jest.fn(),
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
jest.useFakeTimers();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
afterEach(async () => {
|
|
30
|
-
await jest.runOnlyPendingTimersAsync();
|
|
31
|
-
jest.useRealTimers();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
25
|
[
|
|
35
26
|
{ value: '123', expectedValue: '12-3', displayPattern: '**-**-**' },
|
|
36
27
|
{ value: '1234', expectedValue: '12-34', displayPattern: '**-**-**' },
|
|
@@ -132,18 +123,23 @@ describe('InputWithTextFormat (React Testing Library)', () => {
|
|
|
132
123
|
expect(input).toHaveValue('12');
|
|
133
124
|
});
|
|
134
125
|
|
|
135
|
-
|
|
136
|
-
it(`moves to the end, when entered a char in the middle`, async () => {
|
|
126
|
+
it(`cusor preserve position when entered a char in the middle`, async () => {
|
|
137
127
|
setup(initialProps);
|
|
138
128
|
const input = screen.getByRole('textbox');
|
|
139
129
|
await user.type(input, '123');
|
|
140
130
|
expect(input).toHaveValue('12-3');
|
|
141
131
|
|
|
142
|
-
await user.
|
|
132
|
+
await user.keyboard('{arrowleft}{arrowleft}{arrowleft}');
|
|
133
|
+
await user.keyboard('@');
|
|
134
|
+
|
|
143
135
|
expect(input).toHaveValue('1@-23');
|
|
144
136
|
|
|
145
|
-
await user.
|
|
146
|
-
expect(input).toHaveValue('1@-
|
|
137
|
+
await user.keyboard('x');
|
|
138
|
+
expect(input).toHaveValue('1@-x23');
|
|
139
|
+
|
|
140
|
+
await user.keyboard('{arrowright}{backspace}');
|
|
141
|
+
|
|
142
|
+
expect(input).toHaveValue('1@-x3');
|
|
147
143
|
});
|
|
148
144
|
|
|
149
145
|
it(`supports pasting`, async () => {
|
|
@@ -270,7 +270,6 @@ class WithDisplayFormat<T extends TextElementProps> extends React.Component<
|
|
|
270
270
|
handleCursorPositioning = (action: string) => {
|
|
271
271
|
const { displayPattern } = this.props;
|
|
272
272
|
const { triggerEvent, selectionStart, selectionEnd, pastedLength } = this.state;
|
|
273
|
-
const target = triggerEvent?.currentTarget;
|
|
274
273
|
|
|
275
274
|
const cursorPosition = getCursorPositionAfterKeystroke(
|
|
276
275
|
action,
|
|
@@ -281,7 +280,9 @@ class WithDisplayFormat<T extends TextElementProps> extends React.Component<
|
|
|
281
280
|
);
|
|
282
281
|
|
|
283
282
|
setTimeout(() => {
|
|
284
|
-
|
|
283
|
+
if (triggerEvent) {
|
|
284
|
+
(triggerEvent.target as HTMLTextElement).setSelectionRange(cursorPosition, cursorPosition);
|
|
285
|
+
}
|
|
285
286
|
this.setState({ selectionStart: cursorPosition, selectionEnd: cursorPosition });
|
|
286
287
|
}, 0);
|
|
287
288
|
};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { Field } from '../field/Field';
|
|
2
|
-
import { mockMatchMedia, render, screen } from '../test-utils';
|
|
3
|
-
import Typeahead from './Typeahead';
|
|
4
|
-
import { createIntl, createIntlCache } from 'react-intl';
|
|
5
|
-
import messages from '../i18n';
|
|
6
|
-
import { DEFAULT_LANG, DEFAULT_LOCALE } from '../common';
|
|
7
|
-
|
|
8
|
-
mockMatchMedia();
|
|
9
|
-
|
|
10
|
-
const cache = createIntlCache();
|
|
11
|
-
const intl = createIntl({ locale: DEFAULT_LOCALE, messages: messages[DEFAULT_LANG] }, cache);
|
|
12
|
-
|
|
13
|
-
describe('Typeahead', () => {
|
|
14
|
-
it('supports `Field` for labeling', () => {
|
|
15
|
-
render(
|
|
16
|
-
<Field id="test" label="Tags">
|
|
17
|
-
<Typeahead
|
|
18
|
-
id="test"
|
|
19
|
-
name="test"
|
|
20
|
-
options={[{ label: 'Test' }]}
|
|
21
|
-
intl={intl}
|
|
22
|
-
onChange={() => {}}
|
|
23
|
-
/>
|
|
24
|
-
</Field>,
|
|
25
|
-
);
|
|
26
|
-
expect(screen.getAllByRole('group')[0]).toHaveAccessibleName(/^Tags/);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
describe('when no options are provided', () => {
|
|
30
|
-
it('does not render a dropdown when no options and no footer are provided', () => {
|
|
31
|
-
render(
|
|
32
|
-
<Field id="test" label="Tags">
|
|
33
|
-
<Typeahead id="test" name="test" options={[]} intl={intl} onChange={() => {}} />
|
|
34
|
-
</Field>,
|
|
35
|
-
);
|
|
36
|
-
expect(screen.queryByRole('menu')).not.toBeInTheDocument();
|
|
37
|
-
});
|
|
38
|
-
it('does render a dropdown when only a footer is provided', () => {
|
|
39
|
-
render(
|
|
40
|
-
<Field id="test" label="Tags">
|
|
41
|
-
<Typeahead
|
|
42
|
-
id="test"
|
|
43
|
-
name="test"
|
|
44
|
-
options={[]}
|
|
45
|
-
intl={intl}
|
|
46
|
-
footer={<p>hello</p>}
|
|
47
|
-
onChange={() => {}}
|
|
48
|
-
/>
|
|
49
|
-
</Field>,
|
|
50
|
-
);
|
|
51
|
-
expect(screen.getByRole('menu')).toBeInTheDocument();
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
});
|
|
@@ -1,404 +0,0 @@
|
|
|
1
|
-
import { mount } from 'enzyme';
|
|
2
|
-
import doTimes from 'lodash.times';
|
|
3
|
-
import { mockMatchMedia } from '../test-utils';
|
|
4
|
-
|
|
5
|
-
import { InlineAlert } from '..';
|
|
6
|
-
import { Sentiment } from '../common';
|
|
7
|
-
import { fakeEvent, fakeKeyDownEventForKey } from '../common/fakeEvents';
|
|
8
|
-
|
|
9
|
-
import Typeahead from './Typeahead';
|
|
10
|
-
|
|
11
|
-
mockMatchMedia();
|
|
12
|
-
|
|
13
|
-
const defaultLocale = 'en-GB';
|
|
14
|
-
jest.mock('react-intl', () => {
|
|
15
|
-
const mockedIntl = {
|
|
16
|
-
locale: defaultLocale,
|
|
17
|
-
formatMessage: (id) => String(id),
|
|
18
|
-
};
|
|
19
|
-
return {
|
|
20
|
-
injectIntl: (Component) => (props) => <Component {...props} intl={mockedIntl} />,
|
|
21
|
-
defineMessages: (translations) => translations,
|
|
22
|
-
useIntl: () => mockedIntl,
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('Typeahead', () => {
|
|
27
|
-
let component;
|
|
28
|
-
let props;
|
|
29
|
-
const closeButton = () => component.find('.input-group-addon button');
|
|
30
|
-
const formGroup = () => component.find('.form-group');
|
|
31
|
-
const input = () => component.find('input');
|
|
32
|
-
const chip = () => component.find('.np-chip');
|
|
33
|
-
const option = () => component.find('.dropdown-item');
|
|
34
|
-
const menu = () => component.find('.dropdown');
|
|
35
|
-
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
props = {
|
|
38
|
-
id: 'test',
|
|
39
|
-
onChange: () => {},
|
|
40
|
-
name: 'test',
|
|
41
|
-
options: [
|
|
42
|
-
{
|
|
43
|
-
label: 'Test',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
label: 'Test1',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
label: 'Test2',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
label: 'Test3',
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
component = mount(<Typeahead {...props} />);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('renders addon', () => {
|
|
61
|
-
expect(component.find('#test-addon')).toHaveLength(0);
|
|
62
|
-
component.setProps({ addon: <div id="test-addon" /> });
|
|
63
|
-
expect(component.find('#test-addon')).toHaveLength(1);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('renders clear button', () => {
|
|
67
|
-
component.setProps({
|
|
68
|
-
clearable: true,
|
|
69
|
-
});
|
|
70
|
-
input().simulate('change', { target: { value: 'test' } });
|
|
71
|
-
|
|
72
|
-
expect(closeButton()).toHaveLength(1);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('splits pasted value correctly', () => {
|
|
76
|
-
const chips = ['test', 'test2', 'test3'];
|
|
77
|
-
const event = {
|
|
78
|
-
preventDefault: jest.fn(),
|
|
79
|
-
clipboardData: {
|
|
80
|
-
getData: () => chips.join(', '),
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
component.setProps({
|
|
85
|
-
allowNew: true,
|
|
86
|
-
multiple: true,
|
|
87
|
-
chipSeparators: [',', ' '],
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
input().simulate('paste', event);
|
|
91
|
-
const renderedChips = chip().map((chipNode) => chipNode.text());
|
|
92
|
-
|
|
93
|
-
expect(renderedChips.every((label, idx) => chips[idx] === label)).toBe(true);
|
|
94
|
-
expect(renderedChips).toHaveLength(chips.length);
|
|
95
|
-
expect(event.preventDefault).toHaveBeenCalledTimes(1);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('removes last selected value when backspace clicked on empty input', () => {
|
|
99
|
-
const event = {
|
|
100
|
-
key: 'Backspace',
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
component.setProps({
|
|
104
|
-
multiple: true,
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
expect(chip()).toHaveLength(0);
|
|
108
|
-
|
|
109
|
-
input().simulate('change', { target: { value: 'test' } });
|
|
110
|
-
option().at(0).simulate('click', fakeEvent());
|
|
111
|
-
|
|
112
|
-
expect(chip()).toHaveLength(1);
|
|
113
|
-
|
|
114
|
-
input().simulate('keyDown', event);
|
|
115
|
-
|
|
116
|
-
expect(chip()).toHaveLength(0);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('does not remove last selected value when backspace clicked on non-empty input', () => {
|
|
120
|
-
const event = {
|
|
121
|
-
key: 'Backspace',
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
component.setProps({
|
|
125
|
-
multiple: true,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
expect(chip()).toHaveLength(0);
|
|
129
|
-
|
|
130
|
-
input().simulate('change', { target: { value: 'test' } });
|
|
131
|
-
option().at(0).simulate('click', fakeEvent());
|
|
132
|
-
|
|
133
|
-
expect(chip()).toHaveLength(1);
|
|
134
|
-
|
|
135
|
-
input().simulate('change', { target: { value: 'test' } });
|
|
136
|
-
input().simulate('keyDown', event);
|
|
137
|
-
|
|
138
|
-
expect(chip()).toHaveLength(1);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('adds new value as selected and clears the input when separator is entered', () => {
|
|
142
|
-
const event = {
|
|
143
|
-
key: ',',
|
|
144
|
-
preventDefault: jest.fn(),
|
|
145
|
-
};
|
|
146
|
-
const text = 'test';
|
|
147
|
-
|
|
148
|
-
component.setProps({
|
|
149
|
-
multiple: true,
|
|
150
|
-
allowNew: true,
|
|
151
|
-
showSuggestions: false,
|
|
152
|
-
chipSeparators: [','],
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
input().simulate('change', { target: { value: text } });
|
|
156
|
-
input().simulate('keyDown', event);
|
|
157
|
-
|
|
158
|
-
expect(chip().text()).toStrictEqual(text);
|
|
159
|
-
expect(event.preventDefault).toHaveBeenCalledTimes(1);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('adds new value as selected and clears the input when Enter is pressed', () => {
|
|
163
|
-
const event = {
|
|
164
|
-
key: 'Enter',
|
|
165
|
-
preventDefault: jest.fn(),
|
|
166
|
-
};
|
|
167
|
-
const text = 'test';
|
|
168
|
-
|
|
169
|
-
component.setProps({
|
|
170
|
-
multiple: true,
|
|
171
|
-
allowNew: true,
|
|
172
|
-
showSuggestions: false,
|
|
173
|
-
chipSeparators: [','],
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
input().simulate('change', { target: { value: text } });
|
|
177
|
-
input().simulate('keyDown', event);
|
|
178
|
-
|
|
179
|
-
expect(chip().text()).toStrictEqual(text);
|
|
180
|
-
expect(event.preventDefault).toHaveBeenCalledTimes(1);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('adds new value as selected and clears the input when Tab is pressed', () => {
|
|
184
|
-
const event = {
|
|
185
|
-
key: 'Tab',
|
|
186
|
-
preventDefault: jest.fn(),
|
|
187
|
-
};
|
|
188
|
-
const text = 'test';
|
|
189
|
-
|
|
190
|
-
component.setProps({
|
|
191
|
-
multiple: true,
|
|
192
|
-
allowNew: true,
|
|
193
|
-
showSuggestions: false,
|
|
194
|
-
chipSeparators: [','],
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
input().simulate('change', { target: { value: text } });
|
|
198
|
-
input().simulate('keyDown', event);
|
|
199
|
-
|
|
200
|
-
expect(chip().text()).toStrictEqual(text);
|
|
201
|
-
expect(event.preventDefault).toHaveBeenCalledTimes(1);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('clears typeahead when clear button is clicked', () => {
|
|
205
|
-
const event = {
|
|
206
|
-
key: ',',
|
|
207
|
-
preventDefault: jest.fn(),
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const query = 'test';
|
|
211
|
-
|
|
212
|
-
component.setProps({
|
|
213
|
-
multiple: true,
|
|
214
|
-
allowNew: true,
|
|
215
|
-
showSuggestions: false,
|
|
216
|
-
chipSeparators: [','],
|
|
217
|
-
clearable: true,
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
input().simulate('change', { target: { value: query } });
|
|
221
|
-
input().simulate('keyDown', event);
|
|
222
|
-
input().simulate('change', { target: { value: query } });
|
|
223
|
-
|
|
224
|
-
expect(chip()).toHaveLength(1);
|
|
225
|
-
expect(input().getDOMNode().value).toBe(query);
|
|
226
|
-
|
|
227
|
-
closeButton().simulate('click', fakeEvent());
|
|
228
|
-
expect(chip()).toHaveLength(0);
|
|
229
|
-
expect(input().getDOMNode().value).toBe('');
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
it('moves selected items when down and up keys pressed', () => {
|
|
233
|
-
doTimes(3, () => input().simulate('keyDown', fakeKeyDownEventForKey('ArrowDown')));
|
|
234
|
-
|
|
235
|
-
expect(option().at(2).parent().is('.tw-dropdown-item--focused')).toBe(true);
|
|
236
|
-
|
|
237
|
-
input().simulate('keyDown', fakeKeyDownEventForKey('ArrowUp'));
|
|
238
|
-
expect(option().at(1).parent().is('.tw-dropdown-item--focused')).toBe(true);
|
|
239
|
-
|
|
240
|
-
doTimes(5, () => input().simulate('keyDown', fakeKeyDownEventForKey('ArrowDown')));
|
|
241
|
-
expect(option().last().parent().is('.tw-dropdown-item--focused')).toBe(true);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('adds new value as selected and clears the input when no option is highlighted and enter is pressed', () => {
|
|
245
|
-
const event = {
|
|
246
|
-
key: 'Enter',
|
|
247
|
-
preventDefault: jest.fn(),
|
|
248
|
-
};
|
|
249
|
-
const text = 'test';
|
|
250
|
-
|
|
251
|
-
component.setProps({
|
|
252
|
-
multiple: true,
|
|
253
|
-
allowNew: true,
|
|
254
|
-
chipSeparators: [','],
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
input().simulate('change', { target: { value: text } });
|
|
258
|
-
input().simulate('keyDown', event);
|
|
259
|
-
|
|
260
|
-
expect(chip().text()).toStrictEqual(text);
|
|
261
|
-
expect(event.preventDefault).toHaveBeenCalledTimes(1);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
it('sets aria-activedescendant correctly based on focus', () => {
|
|
265
|
-
const inputElement = input().getDOMNode();
|
|
266
|
-
|
|
267
|
-
doTimes(3, () => input().simulate('keyDown', fakeKeyDownEventForKey('ArrowDown')));
|
|
268
|
-
|
|
269
|
-
expect(inputElement).toHaveAttribute('aria-activedescendant', 'menu-test option-Test2');
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it('displays alert when alert is provided and chips are valid or alert type is error', () => {
|
|
273
|
-
const event = {
|
|
274
|
-
key: 'Enter',
|
|
275
|
-
preventDefault: jest.fn(),
|
|
276
|
-
};
|
|
277
|
-
const text = 'test';
|
|
278
|
-
|
|
279
|
-
expect(component.find(InlineAlert)).toHaveLength(0);
|
|
280
|
-
expect(formGroup().hasClass('has-error')).toBe(false);
|
|
281
|
-
component.setProps({
|
|
282
|
-
validateChip: () => {
|
|
283
|
-
return false;
|
|
284
|
-
},
|
|
285
|
-
allowNew: true,
|
|
286
|
-
alert: {
|
|
287
|
-
message: 'test',
|
|
288
|
-
type: Sentiment.ERROR,
|
|
289
|
-
},
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
expect(formGroup().hasClass('has-error')).toBe(true);
|
|
293
|
-
expect(component.find(InlineAlert)).toHaveLength(1);
|
|
294
|
-
|
|
295
|
-
input().simulate('change', { target: { value: text } });
|
|
296
|
-
input().simulate('keyDown', event);
|
|
297
|
-
|
|
298
|
-
expect(formGroup().hasClass('has-error')).toBe(true);
|
|
299
|
-
expect(component.find(InlineAlert)).toHaveLength(1);
|
|
300
|
-
component.setProps({
|
|
301
|
-
alert: {
|
|
302
|
-
message: 'test',
|
|
303
|
-
type: Sentiment.WARNING,
|
|
304
|
-
},
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
expect(formGroup().hasClass('has-error')).toBe(true);
|
|
308
|
-
expect(component.find(InlineAlert)).toHaveLength(0);
|
|
309
|
-
|
|
310
|
-
closeButton().simulate('click', fakeEvent());
|
|
311
|
-
|
|
312
|
-
expect(formGroup().hasClass('has-error')).toBe(false);
|
|
313
|
-
expect(formGroup().hasClass('has-warning')).toBe(true);
|
|
314
|
-
expect(component.find(InlineAlert)).toHaveLength(1);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it('allows supplying options with arbitrary data that will be included with the onChange callback', () => {
|
|
318
|
-
const text = 'test';
|
|
319
|
-
const options = [
|
|
320
|
-
{
|
|
321
|
-
label: 'label 1',
|
|
322
|
-
note: 'note-1',
|
|
323
|
-
secondary: 'secondary-1',
|
|
324
|
-
value: { id: 123, name: 'Neptune' },
|
|
325
|
-
},
|
|
326
|
-
];
|
|
327
|
-
let selectedOption;
|
|
328
|
-
|
|
329
|
-
component.setProps({
|
|
330
|
-
onChange: (selections) => {
|
|
331
|
-
selectedOption = selections[0];
|
|
332
|
-
},
|
|
333
|
-
options,
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
input().simulate('change', { target: { value: text } });
|
|
337
|
-
option().at(0).simulate('click', fakeEvent());
|
|
338
|
-
expect(selectedOption).toStrictEqual({
|
|
339
|
-
label: 'label 1',
|
|
340
|
-
note: 'note-1',
|
|
341
|
-
secondary: 'secondary-1',
|
|
342
|
-
value: { id: 123, name: 'Neptune' },
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
describe('menu', () => {
|
|
347
|
-
it('should render footer', () => {
|
|
348
|
-
component.setProps({
|
|
349
|
-
footer: <div id="footer">footer</div>,
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
expect(component.find('#footer')).toHaveLength(1);
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
it('renders all options', () => {
|
|
356
|
-
const options = option().map((optNode) => optNode.text());
|
|
357
|
-
expect(options).toHaveLength(props.options.length);
|
|
358
|
-
expect(options.every((label, i) => label === props.options[i].label)).toBe(true);
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
it('renders dropdown-menu if has options', () => {
|
|
362
|
-
expect(component.find('.dropdown-menu')).toHaveLength(1);
|
|
363
|
-
component.setProps({ options: [] });
|
|
364
|
-
expect(component.find('.dropdown-menu')).toHaveLength(0);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
it('does not render new option if showNewEntry is false', () => {
|
|
368
|
-
component.setProps({
|
|
369
|
-
allowNew: true,
|
|
370
|
-
showNewEntry: false,
|
|
371
|
-
});
|
|
372
|
-
input().simulate('change', { target: { value: 'check' } });
|
|
373
|
-
|
|
374
|
-
const options = option().map((optNode) => optNode.text());
|
|
375
|
-
expect(options).toHaveLength(props.options.length);
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
it('renders new option if showNewEntry is true', () => {
|
|
379
|
-
component.setProps({
|
|
380
|
-
allowNew: true,
|
|
381
|
-
showNewEntry: true,
|
|
382
|
-
});
|
|
383
|
-
input().simulate('change', { target: { value: 'check' } });
|
|
384
|
-
|
|
385
|
-
const options = option().map((optNode) => optNode.text());
|
|
386
|
-
expect(options).toHaveLength(props.options.length + 1);
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
it('does not show options if query is too short', () => {
|
|
390
|
-
expect(menu().is('.open')).toBe(false);
|
|
391
|
-
input().simulate('change', { target: { value: 'test' } });
|
|
392
|
-
expect(menu().is('.open')).toBe(true);
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it('sets aria-expanded to true when options are shown', () => {
|
|
396
|
-
expect(input().prop('aria-expanded')).toBe(false);
|
|
397
|
-
// we don't want aria-expanded to be true on focus or before 3 characters are entered (that's when the menu is shown)
|
|
398
|
-
input().simulate('change', { target: { value: 'aa' } });
|
|
399
|
-
expect(input().prop('aria-expanded')).toBe(false);
|
|
400
|
-
input().simulate('change', { target: { value: 'aaa' } });
|
|
401
|
-
expect(input().prop('aria-expanded')).toBe(true);
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
});
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { shallow } from 'enzyme';
|
|
2
|
-
|
|
3
|
-
import { Input } from '../../inputs/Input';
|
|
4
|
-
|
|
5
|
-
import TypeaheadInput from './TypeaheadInput';
|
|
6
|
-
|
|
7
|
-
describe('Typeahead input', () => {
|
|
8
|
-
let component;
|
|
9
|
-
let props;
|
|
10
|
-
|
|
11
|
-
const inputWrapper = () => component.find('.typeahead__input-wrapper');
|
|
12
|
-
const input = () => component.find(Input);
|
|
13
|
-
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
props = {
|
|
16
|
-
name: 'test',
|
|
17
|
-
typeaheadId: 'test',
|
|
18
|
-
multiple: false,
|
|
19
|
-
value: '',
|
|
20
|
-
renderChip: jest.fn(),
|
|
21
|
-
onKeyDown: jest.fn(),
|
|
22
|
-
onFocus: jest.fn(),
|
|
23
|
-
onPaste: jest.fn(),
|
|
24
|
-
onChange: jest.fn(),
|
|
25
|
-
autoComplete: 'off',
|
|
26
|
-
selected: [],
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
component = shallow(<TypeaheadInput {...props} />);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('renders single input when multiple is false', () => {
|
|
33
|
-
expect(component.children()).toHaveLength(0);
|
|
34
|
-
expect(component.is(Input)).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('renders chips when provided', () => {
|
|
38
|
-
const selected = [{ label: 'test1' }, { label: 'test2' }];
|
|
39
|
-
component.setProps({
|
|
40
|
-
multiple: true,
|
|
41
|
-
selected,
|
|
42
|
-
renderChip: jest.fn(({ label }, idx) => <span key={idx}>{label}</span>),
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
expect(inputWrapper().find('span')).toHaveLength(selected.length);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('adds aria-controls prop if dropdown is open', () => {
|
|
49
|
-
expect(component.prop('aria-controls')).toBeUndefined();
|
|
50
|
-
component.setProps({ dropdownOpen: true });
|
|
51
|
-
expect(component.prop('aria-controls')).toBe('menu-test');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('passes all callbacks to input', () => {
|
|
55
|
-
const event = {};
|
|
56
|
-
const extraProps = {
|
|
57
|
-
multiple: true,
|
|
58
|
-
value: '',
|
|
59
|
-
onKeyDown: jest.fn(),
|
|
60
|
-
onBlur: jest.fn(),
|
|
61
|
-
onFocus: jest.fn(),
|
|
62
|
-
onPaste: jest.fn(),
|
|
63
|
-
};
|
|
64
|
-
component.setProps(extraProps);
|
|
65
|
-
|
|
66
|
-
input().simulate('focus', event);
|
|
67
|
-
input().simulate('paste', event);
|
|
68
|
-
input().simulate('keyDown', event);
|
|
69
|
-
const inputProps = input().props();
|
|
70
|
-
expect(inputProps.onKeyDown).toHaveBeenCalledWith(event);
|
|
71
|
-
expect(inputProps.onFocus).toHaveBeenCalledWith(event);
|
|
72
|
-
expect(inputProps.onPaste).toHaveBeenCalledWith(event);
|
|
73
|
-
});
|
|
74
|
-
});
|