@transferwise/components 46.11.0 → 46.12.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.
Files changed (28) hide show
  1. package/build/i18n/zh-HK.json +2 -2
  2. package/build/index.esm.js +49 -147
  3. package/build/index.esm.js.map +1 -1
  4. package/build/index.js +49 -147
  5. package/build/index.js.map +1 -1
  6. package/build/types/index.d.ts +1 -0
  7. package/build/types/index.d.ts.map +1 -1
  8. package/build/types/typeahead/Typeahead.d.ts +95 -57
  9. package/build/types/typeahead/Typeahead.d.ts.map +1 -1
  10. package/build/types/typeahead/index.d.ts +2 -2
  11. package/build/types/typeahead/index.d.ts.map +1 -1
  12. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts +23 -41
  13. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts.map +1 -1
  14. package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts +9 -17
  15. package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts.map +1 -1
  16. package/build/types/typeahead/util/highlight.d.ts +2 -1
  17. package/build/types/typeahead/util/highlight.d.ts.map +1 -1
  18. package/package.json +5 -2
  19. package/src/i18n/zh-HK.json +2 -2
  20. package/src/index.ts +1 -0
  21. package/src/typeahead/{Typeahead.story.js → Typeahead.story.tsx} +8 -7
  22. package/src/typeahead/{Typeahead.js → Typeahead.tsx} +110 -111
  23. package/src/typeahead/index.ts +2 -0
  24. package/src/typeahead/typeaheadInput/TypeaheadInput.spec.js +1 -0
  25. package/src/typeahead/typeaheadInput/{TypeaheadInput.js → TypeaheadInput.tsx} +35 -46
  26. package/src/typeahead/typeaheadOption/{TypeaheadOption.js → TypeaheadOption.tsx} +10 -20
  27. package/src/typeahead/util/{highlight.js → highlight.tsx} +1 -1
  28. package/src/typeahead/index.js +0 -3
@@ -4,18 +4,20 @@
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 PropTypes from 'prop-types';
10
- import { Component } from 'react';
10
+ import { Component, ReactNode } from 'react';
11
11
 
12
12
  import Chip from '../chips/Chip';
13
- import { Size, Sentiment } from '../common';
13
+ import { Size, Sentiment, SizeMedium, SizeLarge } from '../common';
14
14
  import {
15
15
  addClickClassToDocumentOnIos,
16
16
  removeClickClassFromDocumentOnIos,
17
+ stopPropagation,
17
18
  } from '../common/domHelpers';
18
19
  import InlineAlert from '../inlineAlert';
20
+ import { InlineAlertProps } from '../inlineAlert/InlineAlert';
19
21
 
20
22
  import TypeaheadInput from './typeaheadInput/TypeaheadInput';
21
23
  import TypeaheadOption from './typeaheadOption/TypeaheadOption';
@@ -23,10 +25,80 @@ import TypeaheadOption from './typeaheadOption/TypeaheadOption';
23
25
  const DEFAULT_MIN_QUERY_LENGTH = 3;
24
26
  const SEARCH_DELAY = 200;
25
27
 
26
- export default class Typeahead extends Component {
27
- constructor(props) {
28
+ export type TypeaheadOption<T = string> = {
29
+ label: string;
30
+ note?: string;
31
+ secondary?: string;
32
+ value?: T;
33
+ };
34
+
35
+ export interface TypeaheadProps<T> {
36
+ id: string;
37
+ name: string;
38
+ addon?: ReactNode;
39
+ alert?: {
40
+ message: InlineAlertProps['children'];
41
+ type?: InlineAlertProps['type'];
42
+ };
43
+ allowNew?: boolean;
44
+ autoFillOnBlur?: boolean;
45
+ autoFocus?: boolean;
46
+ chipSeparators?: string[];
47
+ clearable?: boolean;
48
+ footer?: ReactNode;
49
+ initialValue?: TypeaheadOption<T>[];
50
+ inputAutoComplete?: string;
51
+ maxHeight?: number;
52
+ minQueryLength?: number;
53
+ placeholder?: string;
54
+ multiple?: boolean;
55
+ options: TypeaheadOption<T>[];
56
+ searchDelay?: number;
57
+ showSuggestions?: boolean;
58
+ showNewEntry?: boolean;
59
+ size?: SizeMedium | SizeLarge;
60
+
61
+ onBlur?: () => void;
62
+ onChange: (options: TypeaheadOption<T>[]) => void;
63
+ onFocus?: () => void;
64
+ onInputChange?: (query: string) => void;
65
+ onSearch?: (query: string) => void;
66
+ validateChip?: (chip: TypeaheadOption<T>) => boolean;
67
+ }
68
+
69
+ type TypeaheadState<T> = {
70
+ selected: TypeaheadOption<T>[];
71
+ keyboardFocusedOptionIndex: number | null;
72
+ errorState: boolean;
73
+ query: string;
74
+ optionsShown: boolean;
75
+ isFocused: boolean;
76
+ };
77
+
78
+ export default class Typeahead<T> extends Component<TypeaheadProps<T>, TypeaheadState<T>> {
79
+ declare props: TypeaheadProps<T> &
80
+ Required<Pick<TypeaheadProps<T>, keyof typeof Typeahead.defaultProps>>;
81
+
82
+ static defaultProps = {
83
+ allowNew: false,
84
+ autoFillOnBlur: true,
85
+ autoFocus: false,
86
+ chipSeparators: [],
87
+ clearable: true,
88
+ initialValue: [],
89
+ inputAutoComplete: 'new-password',
90
+ minQueryLength: DEFAULT_MIN_QUERY_LENGTH,
91
+ multiple: false,
92
+ searchDelay: SEARCH_DELAY,
93
+ showSuggestions: true,
94
+ showNewEntry: true,
95
+ size: Size.MEDIUM,
96
+ validateChip: () => true,
97
+ } satisfies Partial<TypeaheadProps<unknown>>;
98
+
99
+ constructor(props: TypeaheadProps<T>) {
28
100
  super(props);
29
- const { searchDelay, initialValue, multiple } = props;
101
+ const { searchDelay, initialValue, multiple } = this.props;
30
102
  this.handleSearchDebounced = debounce(this.handleSearch, searchDelay);
31
103
  const initialQuery = !multiple && initialValue.length > 0 ? initialValue[0].label : '';
32
104
  this.state = {
@@ -34,10 +106,14 @@ export default class Typeahead extends Component {
34
106
  errorState: false,
35
107
  query: initialQuery,
36
108
  keyboardFocusedOptionIndex: null,
109
+ optionsShown: false,
110
+ isFocused: false,
37
111
  };
38
112
  }
39
113
 
40
- UNSAFE_componentWillReceiveProps(nextProps) {
114
+ handleSearchDebounced: DebouncedFunc<Typeahead<T>['handleSearch']>;
115
+
116
+ UNSAFE_componentWillReceiveProps(nextProps: TypeaheadProps<T>) {
41
117
  if (nextProps.multiple !== this.props.multiple) {
42
118
  this.setState((previousState) => {
43
119
  const { selected } = previousState;
@@ -48,6 +124,7 @@ export default class Typeahead extends Component {
48
124
  };
49
125
  }
50
126
  return {
127
+ selected: previousState.selected,
51
128
  query: '',
52
129
  };
53
130
  });
@@ -59,20 +136,16 @@ export default class Typeahead extends Component {
59
136
  }
60
137
 
61
138
  handleOnFocus = () => {
62
- const { onFocus } = this.props;
63
139
  this.showMenu();
64
-
65
- if (onFocus) {
66
- this.props.onFocus();
67
- }
140
+ this.props.onFocus?.();
68
141
  };
69
142
 
70
- onOptionSelected = (event, item) => {
143
+ onOptionSelected = (event: React.MouseEvent, item: TypeaheadOption<T>) => {
71
144
  event.preventDefault();
72
145
  this.selectItem(item);
73
146
  };
74
147
 
75
- handleOnChange = (event) => {
148
+ handleOnChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
76
149
  const { optionsShown, selected } = this.state;
77
150
  const { multiple, onInputChange } = this.props;
78
151
 
@@ -94,7 +167,7 @@ export default class Typeahead extends Component {
94
167
  });
95
168
  };
96
169
 
97
- handleOnPaste = (event) => {
170
+ handleOnPaste: React.ClipboardEventHandler<HTMLInputElement> = (event) => {
98
171
  const { allowNew, multiple, chipSeparators } = this.props;
99
172
  const { selected } = this.state;
100
173
 
@@ -113,7 +186,7 @@ export default class Typeahead extends Component {
113
186
  }
114
187
  };
115
188
 
116
- handleOnKeyDown = (event) => {
189
+ handleOnKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
117
190
  const { showSuggestions, allowNew, multiple, chipSeparators, options } = this.props;
118
191
  const { keyboardFocusedOptionIndex, query, selected } = this.state;
119
192
  const chipsMode = !showSuggestions && allowNew && multiple;
@@ -133,7 +206,7 @@ export default class Typeahead extends Component {
133
206
  break;
134
207
  case 'Enter':
135
208
  event.preventDefault();
136
- if (options[keyboardFocusedOptionIndex]) {
209
+ if (keyboardFocusedOptionIndex != null && options[keyboardFocusedOptionIndex]) {
137
210
  this.selectItem(options[keyboardFocusedOptionIndex]);
138
211
  } else if (allowNew && query.trim()) {
139
212
  this.selectItem({ label: query });
@@ -150,7 +223,7 @@ export default class Typeahead extends Component {
150
223
  }
151
224
  };
152
225
 
153
- moveFocusedOption(offset) {
226
+ moveFocusedOption(offset: number) {
154
227
  this.setState((previousState) => {
155
228
  const { keyboardFocusedOptionIndex } = previousState;
156
229
  const { options } = this.props;
@@ -164,7 +237,7 @@ export default class Typeahead extends Component {
164
237
  });
165
238
  }
166
239
 
167
- selectItem = (item) => {
240
+ selectItem = (item: TypeaheadOption<T>) => {
168
241
  const { multiple } = this.props;
169
242
  let selected = [...this.state.selected];
170
243
  let query;
@@ -183,15 +256,7 @@ export default class Typeahead extends Component {
183
256
  });
184
257
  };
185
258
 
186
- stopPropagation = (event) => {
187
- event.stopPropagation();
188
- event.preventDefault();
189
- if (event.nativeEvent && event.nativeEvent.stopImmediatePropagation) {
190
- event.nativeEvent.stopImmediatePropagation();
191
- }
192
- };
193
-
194
- handleSearch = (query) => {
259
+ handleSearch = (query: string) => {
195
260
  const { onSearch } = this.props;
196
261
  if (onSearch) {
197
262
  onSearch(query);
@@ -246,7 +311,7 @@ export default class Typeahead extends Component {
246
311
  );
247
312
  };
248
313
 
249
- updateSelectedValue = (selected) => {
314
+ updateSelectedValue = (selected: TypeaheadOption<T>[]) => {
250
315
  const { onChange, validateChip } = this.props;
251
316
 
252
317
  const errorState = selected.some((chip) => !validateChip(chip));
@@ -255,7 +320,7 @@ export default class Typeahead extends Component {
255
320
  });
256
321
  };
257
322
 
258
- clear = (event) => {
323
+ clear = (event: React.MouseEvent<HTMLButtonElement>) => {
259
324
  event.preventDefault();
260
325
  if (this.state.selected.length > 0) {
261
326
  this.updateSelectedValue([]);
@@ -266,7 +331,7 @@ export default class Typeahead extends Component {
266
331
  });
267
332
  };
268
333
 
269
- removeChip = (option) => {
334
+ removeChip = (option: TypeaheadOption<T>) => {
270
335
  const { selected } = this.state;
271
336
 
272
337
  if (selected.length > 0) {
@@ -274,8 +339,8 @@ export default class Typeahead extends Component {
274
339
  }
275
340
  };
276
341
 
277
- renderChip = (option, idx) => {
278
- const valid = this.props.validateChip(option);
342
+ renderChip = (option: TypeaheadOption<T>, idx: number): ReactNode => {
343
+ const valid = this.props.validateChip?.(option);
279
344
 
280
345
  return (
281
346
  <Chip
@@ -299,7 +364,10 @@ export default class Typeahead extends Component {
299
364
  allowNew,
300
365
  showNewEntry,
301
366
  dropdownOpen,
302
- }) => {
367
+ }: Pick<TypeaheadProps<T>, 'footer' | 'options' | 'id' | 'allowNew' | 'showNewEntry'> &
368
+ Pick<TypeaheadState<T>, 'keyboardFocusedOptionIndex' | 'query'> & {
369
+ dropdownOpen: boolean;
370
+ }) => {
303
371
  const optionsToRender = [...options];
304
372
  if (
305
373
  allowNew &&
@@ -364,6 +432,7 @@ export default class Typeahead extends Component {
364
432
  const menu = this.renderMenu({
365
433
  footer,
366
434
  options,
435
+ id,
367
436
  keyboardFocusedOptionIndex,
368
437
  query,
369
438
  allowNew,
@@ -371,10 +440,11 @@ export default class Typeahead extends Component {
371
440
  dropdownOpen,
372
441
  });
373
442
 
374
- const hasError = errorState || (alert && alert.type === Sentiment.ERROR);
375
- const displayAlert = (!errorState && alert) || (alert && alert.type === Sentiment.ERROR);
376
- const hasWarning = displayAlert && alert.type === Sentiment.WARNING;
377
- const hasInfo = displayAlert && alert.type === Sentiment.NEUTRAL;
443
+ const alertType = alert?.type ?? Sentiment.NEUTRAL;
444
+ const hasError = errorState || (alert && alertType === Sentiment.ERROR);
445
+ const displayAlert = (!errorState && alert) || (alert && alertType === Sentiment.ERROR);
446
+ const hasWarning = displayAlert && alertType === Sentiment.WARNING;
447
+ const hasInfo = displayAlert && alertType === Sentiment.NEUTRAL;
378
448
  return (
379
449
  <div
380
450
  id={id}
@@ -384,7 +454,7 @@ export default class Typeahead extends Component {
384
454
  'typeahead--multiple': multiple,
385
455
  open: dropdownOpen,
386
456
  })}
387
- onClick={this.stopPropagation}
457
+ onClick={stopPropagation}
388
458
  >
389
459
  <div
390
460
  className={classNames('form-group', {
@@ -409,6 +479,7 @@ export default class Typeahead extends Component {
409
479
  selected,
410
480
  maxHeight,
411
481
  }}
482
+ id={id}
412
483
  name={name}
413
484
  value={query}
414
485
  typeaheadId={id}
@@ -435,75 +506,3 @@ export default class Typeahead extends Component {
435
506
  );
436
507
  }
437
508
  }
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
- };
@@ -0,0 +1,2 @@
1
+ export { default } from './Typeahead';
2
+ export type { TypeaheadOption, TypeaheadProps } from './Typeahead';
@@ -23,6 +23,7 @@ describe('Typeahead input', () => {
23
23
  onPaste: jest.fn(),
24
24
  onChange: jest.fn(),
25
25
  autoComplete: 'off',
26
+ selected: [],
26
27
  };
27
28
 
28
29
  component = shallow(<TypeaheadInput {...props} />);
@@ -2,16 +2,39 @@
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 PropTypes from 'prop-types';
6
- import { Component } from 'react';
5
+ import { Component, createRef, ReactNode } from 'react';
7
6
 
8
7
  import { Input } from '../../inputs/Input';
8
+ import { TypeaheadOption, TypeaheadProps } from '../Typeahead';
9
9
 
10
10
  const DEFAULT_INPUT_MIN_WIDTH = 10;
11
11
 
12
- export default class TypeaheadInput extends Component {
13
- constructor() {
14
- super();
12
+ export type TypeaheadInputProps<T> = {
13
+ typeaheadId: string;
14
+ value: string;
15
+ selected: TypeaheadOption<T>[];
16
+ optionsShown?: boolean;
17
+ autoComplete: string;
18
+ onChange: React.ChangeEventHandler<HTMLInputElement>;
19
+ onKeyDown: React.KeyboardEventHandler<HTMLInputElement>;
20
+ onFocus: () => void;
21
+ onPaste: React.ClipboardEventHandler<HTMLInputElement>;
22
+ renderChip: (chip: TypeaheadOption<T>, index: number) => ReactNode;
23
+ } & Pick<TypeaheadProps<T>, 'id' | 'name' | 'autoFocus' | 'multiple' | 'placeholder' | 'maxHeight'>;
24
+
25
+ type TypeaheadInputState = {
26
+ inputWidth: number;
27
+ };
28
+
29
+ export default class TypeaheadInput<T> extends Component<
30
+ TypeaheadInputProps<T>,
31
+ TypeaheadInputState
32
+ > {
33
+ inputRef = createRef<HTMLInputElement>();
34
+ sizerRef = createRef<HTMLDivElement>();
35
+
36
+ constructor(props: TypeaheadInputProps<T>) {
37
+ super(props);
15
38
  this.state = {
16
39
  inputWidth: DEFAULT_INPUT_MIN_WIDTH,
17
40
  };
@@ -20,11 +43,11 @@ export default class TypeaheadInput extends Component {
20
43
  componentDidMount() {
21
44
  const { autoFocus } = this.props;
22
45
  if (autoFocus) {
23
- this.inputRef.focus();
46
+ this.inputRef.current?.focus();
24
47
  }
25
48
  }
26
49
 
27
- componentDidUpdate(previousProps) {
50
+ componentDidUpdate(previousProps: TypeaheadInputProps<T>) {
28
51
  if (previousProps.value !== this.props.value && this.props.multiple) {
29
52
  this.recalculateWidth();
30
53
  }
@@ -33,7 +56,7 @@ export default class TypeaheadInput extends Component {
33
56
  recalculateWidth = () => {
34
57
  requestAnimationFrame(() => {
35
58
  this.setState({
36
- inputWidth: Math.max(DEFAULT_INPUT_MIN_WIDTH, this.sizerRef.scrollWidth + 10),
59
+ inputWidth: Math.max(DEFAULT_INPUT_MIN_WIDTH, this.sizerRef.current?.scrollWidth ?? 0 + 10),
37
60
  });
38
61
  });
39
62
  };
@@ -59,9 +82,7 @@ export default class TypeaheadInput extends Component {
59
82
  const hasPlaceholder = !multiple || selected.length === 0;
60
83
  return (
61
84
  <Input
62
- ref={(reference) => {
63
- this.inputRef = reference;
64
- }}
85
+ ref={this.inputRef}
65
86
  className={classnames(multiple && 'typeahead__input')}
66
87
  name={name}
67
88
  id={`input-${typeaheadId}`}
@@ -90,9 +111,9 @@ export default class TypeaheadInput extends Component {
90
111
  return multiple ? (
91
112
  <div
92
113
  className="form-control typeahead__input-container"
93
- style={maxHeight && { maxHeight }}
114
+ style={{ maxHeight }}
94
115
  onClick={() => {
95
- this.inputRef.focus();
116
+ this.inputRef.current?.focus();
96
117
  }}
97
118
  >
98
119
  <div className="typeahead__input-wrapper">
@@ -101,12 +122,7 @@ export default class TypeaheadInput extends Component {
101
122
  {this.renderInput()}
102
123
  <div className="typeahead__input-aligner" />
103
124
  </div>
104
- <div
105
- ref={(reference) => {
106
- this.sizerRef = reference;
107
- }}
108
- className="sizer form-control typeahead__input"
109
- >
125
+ <div ref={this.sizerRef} className="sizer form-control typeahead__input">
110
126
  {value}
111
127
  </div>
112
128
  </div>
@@ -115,30 +131,3 @@ export default class TypeaheadInput extends Component {
115
131
  );
116
132
  }
117
133
  }
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
- const Option = (props) => {
8
- const { option, selected, onClick, query } = props;
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;
@@ -1,4 +1,4 @@
1
- export default function highlight(value, query) {
1
+ export default function highlight(value: string, query: string) {
2
2
  if (value && query) {
3
3
  const highlightStart = value.toUpperCase().indexOf(query.trim().toUpperCase());
4
4
  const highlightEnd = highlightStart + query.trim().length;
@@ -1,3 +0,0 @@
1
- import Typeahead from './Typeahead';
2
-
3
- export default Typeahead;