@transferwise/components 0.0.0-experimental-77b2752 → 0.0.0-experimental-c9a3ec4
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.esm.js +51 -154
- package/build/index.esm.js.map +1 -1
- package/build/index.js +51 -154
- package/build/index.js.map +1 -1
- package/build/types/inputs/SelectInput.d.ts +2 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/typeahead/Typeahead.d.ts +77 -57
- package/build/types/typeahead/Typeahead.d.ts.map +1 -1
- package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts +28 -41
- package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts.map +1 -1
- package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts +9 -17
- package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts.map +1 -1
- package/package.json +4 -1
- package/src/inputs/SelectInput.spec.tsx +7 -0
- package/src/inputs/SelectInput.story.tsx +221 -315
- package/src/inputs/SelectInput.tsx +4 -0
- package/src/typeahead/{Typeahead.story.js → Typeahead.story.tsx} +8 -7
- package/src/typeahead/{Typeahead.js → Typeahead.tsx} +101 -114
- package/src/typeahead/typeaheadInput/{TypeaheadInput.js → TypeaheadInput.tsx} +45 -42
- package/src/typeahead/typeaheadOption/{TypeaheadOption.js → TypeaheadOption.tsx} +10 -20
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { Cross as CrossIcon } from '@transferwise/icons';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
|
+
import { DebouncedFunc } from 'lodash';
|
|
7
8
|
import clamp from 'lodash.clamp';
|
|
8
9
|
import debounce from 'lodash.debounce';
|
|
9
|
-
import
|
|
10
|
-
import { Component } from 'react';
|
|
10
|
+
import { Component, ReactNode } from 'react';
|
|
11
11
|
|
|
12
12
|
import Chip from '../chips/Chip';
|
|
13
13
|
import { Size, Sentiment } from '../common';
|
|
@@ -23,10 +23,59 @@ import TypeaheadOption from './typeaheadOption/TypeaheadOption';
|
|
|
23
23
|
const DEFAULT_MIN_QUERY_LENGTH = 3;
|
|
24
24
|
const SEARCH_DELAY = 200;
|
|
25
25
|
|
|
26
|
-
export
|
|
27
|
-
|
|
26
|
+
export type TypeaheadOption<T = string> = {
|
|
27
|
+
label: string;
|
|
28
|
+
note?: string;
|
|
29
|
+
secondary?: string;
|
|
30
|
+
value?: T;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type TypeaheadProps<T> = {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
options: TypeaheadOption<T>[];
|
|
37
|
+
initialValue?: TypeaheadOption<T>[];
|
|
38
|
+
onChange: (options: TypeaheadOption<T>[]) => void;
|
|
39
|
+
allowNew?: boolean;
|
|
40
|
+
autoFocus?: boolean;
|
|
41
|
+
clearable?: boolean;
|
|
42
|
+
multiple?: boolean;
|
|
43
|
+
showSuggestions?: boolean;
|
|
44
|
+
showNewEntry?: boolean;
|
|
45
|
+
searchDelay?: number;
|
|
46
|
+
maxHeight?: number;
|
|
47
|
+
minQueryLength?: number;
|
|
48
|
+
addon?: ReactNode;
|
|
49
|
+
placeholder?: string;
|
|
50
|
+
alert?: {
|
|
51
|
+
message: string;
|
|
52
|
+
type: 'error' | 'warning' | 'neutral';
|
|
53
|
+
};
|
|
54
|
+
footer: ReactNode;
|
|
55
|
+
validateChip?: (chip: TypeaheadOption<T>) => boolean;
|
|
56
|
+
onSearch?: (query: string) => void;
|
|
57
|
+
onBlur?: () => void;
|
|
58
|
+
onInputChange?: (query: string) => void;
|
|
59
|
+
onFocus?: () => void;
|
|
60
|
+
chipSeparators?: string[];
|
|
61
|
+
size?: 'md' | 'lg';
|
|
62
|
+
inputAutoComplete?: string;
|
|
63
|
+
autoFillOnBlur?: boolean;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type TypeaheadState<T> = {
|
|
67
|
+
selected: TypeaheadOption<T>[];
|
|
68
|
+
keyboardFocusedOptionIndex: number | null;
|
|
69
|
+
errorState: boolean;
|
|
70
|
+
query: string;
|
|
71
|
+
optionsShown?: boolean;
|
|
72
|
+
isFocused?: boolean;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default class Typeahead<T> extends Component<TypeaheadProps<T>, TypeaheadState<T>> {
|
|
76
|
+
constructor(props: TypeaheadProps<T>) {
|
|
28
77
|
super(props);
|
|
29
|
-
const { searchDelay, initialValue, multiple } = props;
|
|
78
|
+
const { searchDelay = SEARCH_DELAY, initialValue = [], multiple } = props;
|
|
30
79
|
this.handleSearchDebounced = debounce(this.handleSearch, searchDelay);
|
|
31
80
|
const initialQuery = !multiple && initialValue.length > 0 ? initialValue[0].label : '';
|
|
32
81
|
this.state = {
|
|
@@ -37,7 +86,9 @@ export default class Typeahead extends Component {
|
|
|
37
86
|
};
|
|
38
87
|
}
|
|
39
88
|
|
|
40
|
-
|
|
89
|
+
handleSearchDebounced: DebouncedFunc<(query: string) => void>;
|
|
90
|
+
|
|
91
|
+
UNSAFE_componentWillReceiveProps(nextProps: TypeaheadProps<T>) {
|
|
41
92
|
if (nextProps.multiple !== this.props.multiple) {
|
|
42
93
|
this.setState((previousState) => {
|
|
43
94
|
const { selected } = previousState;
|
|
@@ -49,6 +100,7 @@ export default class Typeahead extends Component {
|
|
|
49
100
|
}
|
|
50
101
|
return {
|
|
51
102
|
query: '',
|
|
103
|
+
selected: previousState.selected,
|
|
52
104
|
};
|
|
53
105
|
});
|
|
54
106
|
}
|
|
@@ -59,20 +111,17 @@ export default class Typeahead extends Component {
|
|
|
59
111
|
}
|
|
60
112
|
|
|
61
113
|
handleOnFocus = () => {
|
|
62
|
-
const { onFocus } = this.props;
|
|
63
114
|
this.showMenu();
|
|
64
|
-
|
|
65
|
-
if (onFocus) {
|
|
66
|
-
this.props.onFocus();
|
|
67
|
-
}
|
|
115
|
+
this.props.onFocus?.();
|
|
68
116
|
};
|
|
69
117
|
|
|
70
|
-
onOptionSelected = (event, item) => {
|
|
118
|
+
onOptionSelected = (event: React.MouseEvent, item: TypeaheadOption<T>) => {
|
|
71
119
|
event.preventDefault();
|
|
72
120
|
this.selectItem(item);
|
|
73
121
|
};
|
|
74
122
|
|
|
75
|
-
|
|
123
|
+
// START HERE
|
|
124
|
+
handleOnChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
|
|
76
125
|
const { optionsShown, selected } = this.state;
|
|
77
126
|
const { multiple, onInputChange } = this.props;
|
|
78
127
|
|
|
@@ -94,8 +143,8 @@ export default class Typeahead extends Component {
|
|
|
94
143
|
});
|
|
95
144
|
};
|
|
96
145
|
|
|
97
|
-
handleOnPaste = (event) => {
|
|
98
|
-
const { allowNew, multiple, chipSeparators } = this.props;
|
|
146
|
+
handleOnPaste: React.ClipboardEventHandler<HTMLInputElement> = (event) => {
|
|
147
|
+
const { allowNew = false, multiple = false, chipSeparators = [] } = this.props;
|
|
99
148
|
const { selected } = this.state;
|
|
100
149
|
|
|
101
150
|
if (allowNew && multiple && chipSeparators.length > 0) {
|
|
@@ -113,8 +162,14 @@ export default class Typeahead extends Component {
|
|
|
113
162
|
}
|
|
114
163
|
};
|
|
115
164
|
|
|
116
|
-
handleOnKeyDown = (event) => {
|
|
117
|
-
const {
|
|
165
|
+
handleOnKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
|
|
166
|
+
const {
|
|
167
|
+
showSuggestions = true,
|
|
168
|
+
allowNew = false,
|
|
169
|
+
multiple = false,
|
|
170
|
+
chipSeparators = [],
|
|
171
|
+
options,
|
|
172
|
+
} = this.props;
|
|
118
173
|
const { keyboardFocusedOptionIndex, query, selected } = this.state;
|
|
119
174
|
const chipsMode = !showSuggestions && allowNew && multiple;
|
|
120
175
|
|
|
@@ -133,7 +188,7 @@ export default class Typeahead extends Component {
|
|
|
133
188
|
break;
|
|
134
189
|
case 'Enter':
|
|
135
190
|
event.preventDefault();
|
|
136
|
-
if (options[keyboardFocusedOptionIndex]) {
|
|
191
|
+
if (keyboardFocusedOptionIndex && options[keyboardFocusedOptionIndex]) {
|
|
137
192
|
this.selectItem(options[keyboardFocusedOptionIndex]);
|
|
138
193
|
} else if (allowNew && query.trim()) {
|
|
139
194
|
this.selectItem({ label: query });
|
|
@@ -150,7 +205,7 @@ export default class Typeahead extends Component {
|
|
|
150
205
|
}
|
|
151
206
|
};
|
|
152
207
|
|
|
153
|
-
moveFocusedOption(offset) {
|
|
208
|
+
moveFocusedOption(offset: number) {
|
|
154
209
|
this.setState((previousState) => {
|
|
155
210
|
const { keyboardFocusedOptionIndex } = previousState;
|
|
156
211
|
const { options } = this.props;
|
|
@@ -164,7 +219,7 @@ export default class Typeahead extends Component {
|
|
|
164
219
|
});
|
|
165
220
|
}
|
|
166
221
|
|
|
167
|
-
selectItem = (item) => {
|
|
222
|
+
selectItem = (item: TypeaheadOption<T>) => {
|
|
168
223
|
const { multiple } = this.props;
|
|
169
224
|
let selected = [...this.state.selected];
|
|
170
225
|
let query;
|
|
@@ -183,7 +238,7 @@ export default class Typeahead extends Component {
|
|
|
183
238
|
});
|
|
184
239
|
};
|
|
185
240
|
|
|
186
|
-
stopPropagation = (event) => {
|
|
241
|
+
stopPropagation = (event: React.MouseEvent) => {
|
|
187
242
|
event.stopPropagation();
|
|
188
243
|
event.preventDefault();
|
|
189
244
|
if (event.nativeEvent && event.nativeEvent.stopImmediatePropagation) {
|
|
@@ -191,7 +246,7 @@ export default class Typeahead extends Component {
|
|
|
191
246
|
}
|
|
192
247
|
};
|
|
193
248
|
|
|
194
|
-
handleSearch = (query) => {
|
|
249
|
+
handleSearch = (query: string) => {
|
|
195
250
|
const { onSearch } = this.props;
|
|
196
251
|
if (onSearch) {
|
|
197
252
|
onSearch(query);
|
|
@@ -246,8 +301,8 @@ export default class Typeahead extends Component {
|
|
|
246
301
|
);
|
|
247
302
|
};
|
|
248
303
|
|
|
249
|
-
updateSelectedValue = (selected) => {
|
|
250
|
-
const { onChange, validateChip } = this.props;
|
|
304
|
+
updateSelectedValue = (selected: TypeaheadOption<T>[]) => {
|
|
305
|
+
const { onChange, validateChip = () => true } = this.props;
|
|
251
306
|
|
|
252
307
|
const errorState = selected.some((chip) => !validateChip(chip));
|
|
253
308
|
this.setState({ selected, errorState }, () => {
|
|
@@ -255,7 +310,7 @@ export default class Typeahead extends Component {
|
|
|
255
310
|
});
|
|
256
311
|
};
|
|
257
312
|
|
|
258
|
-
clear = (event) => {
|
|
313
|
+
clear = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
259
314
|
event.preventDefault();
|
|
260
315
|
if (this.state.selected.length > 0) {
|
|
261
316
|
this.updateSelectedValue([]);
|
|
@@ -266,7 +321,7 @@ export default class Typeahead extends Component {
|
|
|
266
321
|
});
|
|
267
322
|
};
|
|
268
323
|
|
|
269
|
-
removeChip = (option) => {
|
|
324
|
+
removeChip = (option: TypeaheadOption<T>) => {
|
|
270
325
|
const { selected } = this.state;
|
|
271
326
|
|
|
272
327
|
if (selected.length > 0) {
|
|
@@ -274,8 +329,8 @@ export default class Typeahead extends Component {
|
|
|
274
329
|
}
|
|
275
330
|
};
|
|
276
331
|
|
|
277
|
-
renderChip = (option
|
|
278
|
-
const valid = this.props.validateChip(option);
|
|
332
|
+
renderChip = (option: TypeaheadOption<T>, idx: number): ReactNode => {
|
|
333
|
+
const valid = this.props.validateChip?.(option);
|
|
279
334
|
|
|
280
335
|
return (
|
|
281
336
|
<Chip
|
|
@@ -299,7 +354,10 @@ export default class Typeahead extends Component {
|
|
|
299
354
|
allowNew,
|
|
300
355
|
showNewEntry,
|
|
301
356
|
dropdownOpen,
|
|
302
|
-
}
|
|
357
|
+
}: Pick<TypeaheadProps<T>, 'footer' | 'options' | 'id' | 'allowNew' | 'showNewEntry'> &
|
|
358
|
+
Pick<TypeaheadState<T>, 'keyboardFocusedOptionIndex' | 'query'> & {
|
|
359
|
+
dropdownOpen?: boolean;
|
|
360
|
+
}) => {
|
|
303
361
|
const optionsToRender = [...options];
|
|
304
362
|
if (
|
|
305
363
|
allowNew &&
|
|
@@ -338,21 +396,21 @@ export default class Typeahead extends Component {
|
|
|
338
396
|
const {
|
|
339
397
|
id,
|
|
340
398
|
placeholder,
|
|
341
|
-
multiple,
|
|
342
|
-
size,
|
|
343
|
-
addon,
|
|
399
|
+
multiple = false,
|
|
400
|
+
size = Size.MEDIUM,
|
|
401
|
+
addon = null,
|
|
344
402
|
name,
|
|
345
|
-
clearable,
|
|
346
|
-
allowNew,
|
|
347
|
-
footer,
|
|
348
|
-
showSuggestions,
|
|
349
|
-
showNewEntry,
|
|
403
|
+
clearable = true,
|
|
404
|
+
allowNew = false,
|
|
405
|
+
footer = null,
|
|
406
|
+
showSuggestions = true,
|
|
407
|
+
showNewEntry = true,
|
|
350
408
|
options,
|
|
351
|
-
minQueryLength,
|
|
352
|
-
autoFocus,
|
|
353
|
-
maxHeight,
|
|
354
|
-
alert,
|
|
355
|
-
inputAutoComplete,
|
|
409
|
+
minQueryLength = DEFAULT_MIN_QUERY_LENGTH,
|
|
410
|
+
autoFocus = false,
|
|
411
|
+
maxHeight = null,
|
|
412
|
+
alert = null,
|
|
413
|
+
inputAutoComplete = 'new-password',
|
|
356
414
|
} = this.props;
|
|
357
415
|
|
|
358
416
|
const { errorState, query, selected, optionsShown, keyboardFocusedOptionIndex } = this.state;
|
|
@@ -364,6 +422,7 @@ export default class Typeahead extends Component {
|
|
|
364
422
|
const menu = this.renderMenu({
|
|
365
423
|
footer,
|
|
366
424
|
options,
|
|
425
|
+
id,
|
|
367
426
|
keyboardFocusedOptionIndex,
|
|
368
427
|
query,
|
|
369
428
|
allowNew,
|
|
@@ -435,75 +494,3 @@ export default class Typeahead extends Component {
|
|
|
435
494
|
);
|
|
436
495
|
}
|
|
437
496
|
}
|
|
438
|
-
|
|
439
|
-
Typeahead.propTypes = {
|
|
440
|
-
id: PropTypes.string.isRequired,
|
|
441
|
-
name: PropTypes.string.isRequired,
|
|
442
|
-
options: PropTypes.arrayOf(
|
|
443
|
-
PropTypes.shape({
|
|
444
|
-
label: PropTypes.string.isRequired,
|
|
445
|
-
note: PropTypes.string,
|
|
446
|
-
secondary: PropTypes.string,
|
|
447
|
-
value: PropTypes.object,
|
|
448
|
-
}),
|
|
449
|
-
).isRequired,
|
|
450
|
-
initialValue: PropTypes.arrayOf(
|
|
451
|
-
PropTypes.shape({
|
|
452
|
-
label: PropTypes.string.isRequired,
|
|
453
|
-
note: PropTypes.string,
|
|
454
|
-
secondary: PropTypes.string,
|
|
455
|
-
}),
|
|
456
|
-
),
|
|
457
|
-
onChange: PropTypes.func.isRequired,
|
|
458
|
-
allowNew: PropTypes.bool,
|
|
459
|
-
autoFocus: PropTypes.bool,
|
|
460
|
-
clearable: PropTypes.bool,
|
|
461
|
-
multiple: PropTypes.bool,
|
|
462
|
-
showSuggestions: PropTypes.bool,
|
|
463
|
-
showNewEntry: PropTypes.bool,
|
|
464
|
-
searchDelay: PropTypes.number,
|
|
465
|
-
maxHeight: PropTypes.number,
|
|
466
|
-
minQueryLength: PropTypes.number,
|
|
467
|
-
addon: PropTypes.node,
|
|
468
|
-
placeholder: PropTypes.string,
|
|
469
|
-
alert: PropTypes.shape({
|
|
470
|
-
message: PropTypes.string.isRequired,
|
|
471
|
-
type: PropTypes.oneOf(['error', 'warning', 'neutral']).isRequired,
|
|
472
|
-
}),
|
|
473
|
-
footer: PropTypes.node,
|
|
474
|
-
validateChip: PropTypes.func,
|
|
475
|
-
onSearch: PropTypes.func,
|
|
476
|
-
onBlur: PropTypes.func,
|
|
477
|
-
onInputChange: PropTypes.func,
|
|
478
|
-
onFocus: PropTypes.func,
|
|
479
|
-
chipSeparators: PropTypes.arrayOf(PropTypes.string),
|
|
480
|
-
size: PropTypes.oneOf(['md', 'lg']),
|
|
481
|
-
inputAutoComplete: PropTypes.string,
|
|
482
|
-
autoFillOnBlur: PropTypes.bool,
|
|
483
|
-
};
|
|
484
|
-
|
|
485
|
-
Typeahead.defaultProps = {
|
|
486
|
-
allowNew: false,
|
|
487
|
-
autoFocus: false,
|
|
488
|
-
clearable: true,
|
|
489
|
-
multiple: false,
|
|
490
|
-
maxHeight: null,
|
|
491
|
-
showSuggestions: true,
|
|
492
|
-
showNewEntry: true,
|
|
493
|
-
searchDelay: SEARCH_DELAY,
|
|
494
|
-
minQueryLength: DEFAULT_MIN_QUERY_LENGTH,
|
|
495
|
-
addon: null,
|
|
496
|
-
placeholder: null,
|
|
497
|
-
alert: null,
|
|
498
|
-
footer: null,
|
|
499
|
-
size: Size.MEDIUM,
|
|
500
|
-
chipSeparators: [],
|
|
501
|
-
initialValue: [],
|
|
502
|
-
onSearch: null,
|
|
503
|
-
onBlur: null,
|
|
504
|
-
onInputChange: null,
|
|
505
|
-
onFocus: null,
|
|
506
|
-
validateChip: () => true,
|
|
507
|
-
inputAutoComplete: 'new-password',
|
|
508
|
-
autoFillOnBlur: true,
|
|
509
|
-
};
|
|
@@ -2,16 +2,44 @@
|
|
|
2
2
|
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
|
3
3
|
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
|
4
4
|
import classnames from 'classnames';
|
|
5
|
-
import
|
|
6
|
-
import { Component } from 'react';
|
|
5
|
+
import { Component, ReactNode } from 'react';
|
|
7
6
|
|
|
8
7
|
import { Input } from '../../inputs/Input';
|
|
8
|
+
import { TypeaheadOption } from '../Typeahead';
|
|
9
9
|
|
|
10
10
|
const DEFAULT_INPUT_MIN_WIDTH = 10;
|
|
11
11
|
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
export type TypeaheadInputProps<T> = {
|
|
13
|
+
typeaheadId: string;
|
|
14
|
+
name: string;
|
|
15
|
+
autoFocus?: boolean;
|
|
16
|
+
multiple: boolean;
|
|
17
|
+
value: string;
|
|
18
|
+
selected: TypeaheadOption<T>[];
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
optionsShown?: boolean;
|
|
21
|
+
maxHeight: number | null;
|
|
22
|
+
autoComplete: string;
|
|
23
|
+
onChange: React.ChangeEventHandler<HTMLInputElement>;
|
|
24
|
+
onKeyDown: React.KeyboardEventHandler<HTMLInputElement>;
|
|
25
|
+
onFocus: () => void;
|
|
26
|
+
onPaste: React.ClipboardEventHandler<HTMLInputElement>;
|
|
27
|
+
renderChip: (chip: TypeaheadOption<T>, index: number) => ReactNode;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type TypeaheadInputState = {
|
|
31
|
+
inputWidth: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default class TypeaheadInput<T> extends Component<
|
|
35
|
+
TypeaheadInputProps<T>,
|
|
36
|
+
TypeaheadInputState
|
|
37
|
+
> {
|
|
38
|
+
inputRef: HTMLInputElement | null = null;
|
|
39
|
+
sizerRef: HTMLDivElement | null = null;
|
|
40
|
+
|
|
41
|
+
constructor(props: TypeaheadInputProps<T>) {
|
|
42
|
+
super(props);
|
|
15
43
|
this.state = {
|
|
16
44
|
inputWidth: DEFAULT_INPUT_MIN_WIDTH,
|
|
17
45
|
};
|
|
@@ -20,11 +48,11 @@ export default class TypeaheadInput extends Component {
|
|
|
20
48
|
componentDidMount() {
|
|
21
49
|
const { autoFocus } = this.props;
|
|
22
50
|
if (autoFocus) {
|
|
23
|
-
this.inputRef
|
|
51
|
+
this.inputRef?.focus();
|
|
24
52
|
}
|
|
25
53
|
}
|
|
26
54
|
|
|
27
|
-
componentDidUpdate(previousProps) {
|
|
55
|
+
componentDidUpdate(previousProps: TypeaheadInputProps<T>) {
|
|
28
56
|
if (previousProps.value !== this.props.value && this.props.multiple) {
|
|
29
57
|
this.recalculateWidth();
|
|
30
58
|
}
|
|
@@ -33,7 +61,9 @@ export default class TypeaheadInput extends Component {
|
|
|
33
61
|
recalculateWidth = () => {
|
|
34
62
|
requestAnimationFrame(() => {
|
|
35
63
|
this.setState({
|
|
36
|
-
inputWidth:
|
|
64
|
+
inputWidth: this.sizerRef
|
|
65
|
+
? Math.max(DEFAULT_INPUT_MIN_WIDTH, this.sizerRef.scrollWidth + 10)
|
|
66
|
+
: DEFAULT_INPUT_MIN_WIDTH,
|
|
37
67
|
});
|
|
38
68
|
});
|
|
39
69
|
};
|
|
@@ -41,12 +71,12 @@ export default class TypeaheadInput extends Component {
|
|
|
41
71
|
renderInput = () => {
|
|
42
72
|
const {
|
|
43
73
|
typeaheadId,
|
|
44
|
-
autoFocus,
|
|
74
|
+
autoFocus = false,
|
|
45
75
|
multiple,
|
|
46
76
|
name,
|
|
47
|
-
optionsShown,
|
|
48
|
-
placeholder,
|
|
49
|
-
selected,
|
|
77
|
+
optionsShown = false,
|
|
78
|
+
placeholder = '',
|
|
79
|
+
selected = [],
|
|
50
80
|
value,
|
|
51
81
|
onChange,
|
|
52
82
|
onKeyDown,
|
|
@@ -85,14 +115,14 @@ export default class TypeaheadInput extends Component {
|
|
|
85
115
|
};
|
|
86
116
|
|
|
87
117
|
render() {
|
|
88
|
-
const { multiple, selected, value, maxHeight, renderChip } = this.props;
|
|
118
|
+
const { multiple, selected = [], value, maxHeight = null, renderChip } = this.props;
|
|
89
119
|
|
|
90
120
|
return multiple ? (
|
|
91
121
|
<div
|
|
92
122
|
className="form-control typeahead__input-container"
|
|
93
|
-
style={maxHeight
|
|
123
|
+
style={{ maxHeight: maxHeight ?? undefined }}
|
|
94
124
|
onClick={() => {
|
|
95
|
-
this.inputRef
|
|
125
|
+
this.inputRef?.focus();
|
|
96
126
|
}}
|
|
97
127
|
>
|
|
98
128
|
<div className="typeahead__input-wrapper">
|
|
@@ -115,30 +145,3 @@ export default class TypeaheadInput extends Component {
|
|
|
115
145
|
);
|
|
116
146
|
}
|
|
117
147
|
}
|
|
118
|
-
|
|
119
|
-
TypeaheadInput.propTypes = {
|
|
120
|
-
typeaheadId: PropTypes.string.isRequired,
|
|
121
|
-
name: PropTypes.string.isRequired,
|
|
122
|
-
autoFocus: PropTypes.bool,
|
|
123
|
-
multiple: PropTypes.bool.isRequired,
|
|
124
|
-
value: PropTypes.string.isRequired,
|
|
125
|
-
selected: PropTypes.arrayOf(PropTypes.object),
|
|
126
|
-
placeholder: PropTypes.string,
|
|
127
|
-
optionsShown: PropTypes.bool,
|
|
128
|
-
maxHeight: PropTypes.number,
|
|
129
|
-
autoComplete: PropTypes.string.isRequired,
|
|
130
|
-
|
|
131
|
-
onChange: PropTypes.func.isRequired,
|
|
132
|
-
renderChip: PropTypes.func.isRequired,
|
|
133
|
-
onKeyDown: PropTypes.func.isRequired,
|
|
134
|
-
onFocus: PropTypes.func.isRequired,
|
|
135
|
-
onPaste: PropTypes.func.isRequired,
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
TypeaheadInput.defaultProps = {
|
|
139
|
-
autoFocus: false,
|
|
140
|
-
maxHeight: null,
|
|
141
|
-
placeholder: '',
|
|
142
|
-
optionsShown: false,
|
|
143
|
-
selected: [],
|
|
144
|
-
};
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
/* eslint-disable jsx-a11y/anchor-is-valid */
|
|
2
2
|
import classNames from 'classnames';
|
|
3
|
-
import PropTypes from 'prop-types';
|
|
4
3
|
|
|
4
|
+
import { TypeaheadOption } from '../Typeahead';
|
|
5
5
|
import highlight from '../util/highlight';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
export type TypeaheadOptionProps<T> = {
|
|
8
|
+
option: TypeaheadOption<T>;
|
|
9
|
+
selected?: boolean;
|
|
10
|
+
onClick?: React.MouseEventHandler;
|
|
11
|
+
query?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const Option = <T,>(props: TypeaheadOptionProps<T>) => {
|
|
15
|
+
const { option, selected = false, onClick = () => {}, query = '' } = props;
|
|
9
16
|
const { label, note, secondary } = option;
|
|
10
17
|
|
|
11
18
|
return (
|
|
@@ -27,21 +34,4 @@ const Option = (props) => {
|
|
|
27
34
|
);
|
|
28
35
|
};
|
|
29
36
|
|
|
30
|
-
Option.propTypes = {
|
|
31
|
-
option: PropTypes.shape({
|
|
32
|
-
label: PropTypes.string.isRequired,
|
|
33
|
-
note: PropTypes.string,
|
|
34
|
-
secondary: PropTypes.string,
|
|
35
|
-
}).isRequired,
|
|
36
|
-
query: PropTypes.string,
|
|
37
|
-
selected: PropTypes.bool,
|
|
38
|
-
onClick: PropTypes.func,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
Option.defaultProps = {
|
|
42
|
-
selected: false,
|
|
43
|
-
query: '',
|
|
44
|
-
onClick: () => {},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
37
|
export default Option;
|