@transferwise/components 0.0.0-experimental-ddeb968 → 0.0.0-experimental-80dda9c

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 (38) hide show
  1. package/README.md +8 -0
  2. package/build/common/panel/Panel.js +2 -1
  3. package/build/common/panel/Panel.js.map +1 -1
  4. package/build/common/panel/Panel.mjs +2 -1
  5. package/build/common/panel/Panel.mjs.map +1 -1
  6. package/build/select/Select.js +125 -41
  7. package/build/select/Select.js.map +1 -1
  8. package/build/select/Select.mjs +121 -41
  9. package/build/select/Select.mjs.map +1 -1
  10. package/build/select/index.js +8 -0
  11. package/build/select/index.js.map +1 -0
  12. package/build/select/index.mjs +6 -0
  13. package/build/select/index.mjs.map +1 -0
  14. package/build/snackbar/Snackbar.js +2 -1
  15. package/build/snackbar/Snackbar.js.map +1 -1
  16. package/build/snackbar/Snackbar.mjs +2 -1
  17. package/build/snackbar/Snackbar.mjs.map +1 -1
  18. package/build/types/index.d.ts +0 -1
  19. package/build/types/index.d.ts.map +1 -1
  20. package/build/types/select/Select.d.ts +94 -64
  21. package/build/types/select/Select.d.ts.map +1 -1
  22. package/build/types/select/index.d.ts +2 -2
  23. package/build/types/select/index.d.ts.map +1 -1
  24. package/build/types/select/option/index.d.ts +1 -1
  25. package/build/types/select/option/index.d.ts.map +1 -1
  26. package/build/types/select/searchBox/index.d.ts +1 -1
  27. package/build/types/select/searchBox/index.d.ts.map +1 -1
  28. package/build/types/snackbar/Snackbar.d.ts.map +1 -1
  29. package/package.json +5 -3
  30. package/src/index.ts +0 -6
  31. package/src/select/{Select.tsx → Select.js} +155 -137
  32. package/src/select/{Select.spec.tsx → Select.spec.js} +6 -8
  33. package/src/select/index.js +3 -0
  34. package/src/snackbar/Snackbar.tsx +7 -1
  35. package/src/select/index.ts +0 -7
  36. /package/src/select/option/{index.ts → index.js} +0 -0
  37. /package/src/select/searchBox/{SearchBox.spec.tsx → SearchBox.spec.js} +0 -0
  38. /package/src/select/searchBox/{index.ts → index.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "0.0.0-experimental-ddeb968",
3
+ "version": "0.0.0-experimental-80dda9c",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -92,9 +92,9 @@
92
92
  "rollup": "^4.18.1",
93
93
  "rollup-preserve-directives": "^1.1.1",
94
94
  "storybook": "^8.2.2",
95
- "@transferwise/less-config": "3.1.0",
96
95
  "@transferwise/neptune-css": "14.13.1",
97
- "@wise/components-theming": "1.5.0"
96
+ "@wise/components-theming": "1.5.0",
97
+ "@transferwise/less-config": "3.1.0"
98
98
  },
99
99
  "peerDependencies": {
100
100
  "@transferwise/icons": "^3.7.0",
@@ -123,6 +123,7 @@
123
123
  "lodash.debounce": "^4.0.8",
124
124
  "lodash.topairs": "^4.3.0",
125
125
  "merge-props": "^6.0.0",
126
+ "prop-types": "^15.8.1",
126
127
  "react-popper": "^2.3.0",
127
128
  "react-transition-group": "^4.4.5",
128
129
  "virtua": "^0.33.3"
@@ -141,6 +142,7 @@
141
142
  "build:clean": "rm -rf lib build",
142
143
  "build:crowdin-source-file": "formatjs extract 'src/**/*.messages.+(js|ts|tsx)' --out-file src/i18n/en.json --format simple && prettier --write src/i18n/*.json",
143
144
  "build:js": "rollup --config --sourcemap",
145
+ "build:generate-types": "node ./scripts/generate-type-declarations.js",
144
146
  "build:copy-files": "cpx 'src/**/!(db)/*.{json,svg}' build",
145
147
  "build:copy-css": "cpx 'src/main.css' build/ & cpx 'src/**/*.css' build/styles/",
146
148
  "build:copy-lang": "cpx 'src/i18n/*.json' build/i18n && cpx 'src/i18n/index.js' build/i18n",
package/src/index.ts CHANGED
@@ -67,12 +67,6 @@ export type { RadioProps } from './radio';
67
67
  export type { RadioGroupProps, RadioGroupRadio } from './radioGroup';
68
68
  export type { RadioOptionProps } from './radioOption';
69
69
  export type { SegmentedControlProps } from './segmentedControl';
70
- export type {
71
- SelectItem,
72
- SelectItemWithPlaceholder,
73
- SelectOptionItem,
74
- SelectProps,
75
- } from './select';
76
70
  export type { SlidingPanelProps } from './slidingPanel';
77
71
  export type { SnackbarProps } from './snackbar/Snackbar';
78
72
  export type { SnackbarContextType } from './snackbar/SnackbarContext';
@@ -1,11 +1,12 @@
1
1
  import { useTheme } from '@wise/components-theming';
2
2
  import { clsx } from 'clsx';
3
+ import PropTypes from 'prop-types';
3
4
  import { useState, useEffect, useRef, useMemo, useId } from 'react';
4
5
  import { useIntl } from 'react-intl';
5
6
 
6
- import Button, { ButtonProps } from '../button';
7
+ import Button from '../button';
7
8
  import Chevron from '../chevron';
8
- import { Position, Size } from '../common';
9
+ import { Position } from '../common';
9
10
  import BottomSheet from '../common/bottomSheet';
10
11
  import { stopPropagation } from '../common/domHelpers';
11
12
  import { useLayout } from '../common/hooks';
@@ -20,56 +21,35 @@ import SearchBox from './searchBox';
20
21
  const DEFAULT_SEARCH_VALUE = '';
21
22
  const DEFAULT_OPTIONS_PAGE_SIZE = 1000;
22
23
 
23
- const includesString = (string1: string, string2: string) =>
24
- string1.toLowerCase().includes(string2.toLowerCase());
24
+ const includesString = (string1, string2) => string1.toLowerCase().includes(string2.toLowerCase());
25
25
 
26
- export interface SelectOptionItem {
27
- value: any;
28
- label?: React.ReactNode;
29
- icon?: React.ReactNode;
30
- currency?: string;
31
- note?: React.ReactNode;
32
- secondary?: React.ReactNode;
33
- }
34
-
35
- export interface SelectItem extends Partial<SelectOptionItem> {
36
- header?: React.ReactNode;
37
- separator?: boolean;
38
- disabled?: boolean;
39
- searchStrings?: string[];
40
- }
41
-
42
- export interface SelectItemWithPlaceholder extends SelectItem {
43
- placeholder?: string;
44
- }
45
-
46
- function defaultFilterFunction(option: SelectItemWithPlaceholder, searchValue: string) {
26
+ function defaultFilterFunction(option, searchValue) {
47
27
  if (isPlaceholderOption(option)) {
48
28
  return true;
49
29
  }
50
30
  const { label, note, secondary, currency, searchStrings } = option;
51
31
  return (
52
- (typeof label === 'string' && includesString(label, searchValue)) ||
53
- (typeof note === 'string' && includesString(note, searchValue)) ||
54
- (typeof secondary === 'string' && includesString(secondary, searchValue)) ||
32
+ (!!label && includesString(label, searchValue)) ||
33
+ (!!note && includesString(note, searchValue)) ||
34
+ (!!secondary && includesString(secondary, searchValue)) ||
55
35
  (!!currency && includesString(currency, searchValue)) ||
56
36
  (!!searchStrings && searchStrings.some((string) => includesString(string, searchValue)))
57
37
  );
58
38
  }
59
39
 
60
- function isActionableOption(option: SelectItem) {
40
+ function isActionableOption(option) {
61
41
  return !option.header && !option.separator && !option.disabled;
62
42
  }
63
43
 
64
- function isHeaderOption(option: SelectItem | null) {
44
+ function isHeaderOption(option) {
65
45
  return option != null && 'header' in option;
66
46
  }
67
47
 
68
- function isSeparatorOption(option: SelectItem | null) {
48
+ function isSeparatorOption(option) {
69
49
  return option != null && 'separator' in option;
70
50
  }
71
51
 
72
- function clamp(from: number, to: number, value: number) {
52
+ function clamp(from, to, value) {
73
53
  return Math.max(Math.min(to, value), from);
74
54
  }
75
55
 
@@ -78,62 +58,24 @@ function clamp(from: number, to: number, value: number) {
78
58
  */
79
59
  const DEFAULT_SELECTED_OPTION = null;
80
60
 
81
- function isPlaceholderOption(option: SelectItemWithPlaceholder | null) {
61
+ function isPlaceholderOption(option) {
82
62
  return option === DEFAULT_SELECTED_OPTION || 'placeholder' in option;
83
63
  }
84
64
 
85
- function isSearchableOption(option: SelectItemWithPlaceholder | null) {
65
+ function isSearchableOption(option) {
86
66
  return !isHeaderOption(option) && !isSeparatorOption(option) && !isPlaceholderOption(option);
87
67
  }
88
68
 
89
- const getUniqueIdForOption = (parentId: string | undefined, option: SelectItem | null) => {
69
+ const getUniqueIdForOption = (parentId = '', option) => {
90
70
  if (option == null) {
91
71
  return undefined;
92
72
  }
93
73
 
94
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
95
- const uniqueOptionId =
96
- option.value || (typeof option.label === 'string' ? option.label.replace(/\s/g, '') : '');
74
+ const uniqueOptionId = option.value || (option.label?.replace(/\s/g, '') ?? '');
97
75
 
98
- return `option-${parentId ?? ''}-${uniqueOptionId}`;
76
+ return `option-${parentId}-${uniqueOptionId}`;
99
77
  };
100
78
 
101
- export interface SelectProps {
102
- placeholder?: string;
103
- id?: string;
104
- required?: boolean;
105
- disabled?: boolean;
106
- inverse?: boolean;
107
- dropdownRight?: `${Size.EXTRA_SMALL | Size.SMALL | Size.MEDIUM | Size.LARGE | Size.EXTRA_LARGE}`;
108
- dropdownWidth?: `${Size.SMALL | Size.MEDIUM | Size.LARGE}`;
109
- size?: `${Size.SMALL | Size.MEDIUM | Size.LARGE}`;
110
- block?: boolean;
111
- selected?: SelectOptionItem;
112
- /**
113
- * Search toggle
114
- * if `true` default search functionality being enabled (not case sensitive search in option labels & currency props)
115
- * if `function` you can define your own search function to implement custom search experience. This search function used while filtering the options array.
116
- */
117
- search?: boolean | ((option: SelectItemWithPlaceholder, searchValue: string) => boolean);
118
- options: SelectItem[];
119
- searchValue?: string;
120
- searchPlaceholder?: string;
121
- classNames?: Record<string, string>;
122
- dropdownUp?: boolean;
123
- buttonProps?: Extract<ButtonProps, { as?: 'button' }>;
124
- dropdownProps?: React.ComponentPropsWithoutRef<'ul'>;
125
- onChange: (value: SelectItem | typeof DEFAULT_SELECTED_OPTION) => void;
126
- onFocus?: React.FocusEventHandler<HTMLDivElement>;
127
- onBlur?: React.FocusEventHandler<HTMLDivElement>;
128
- /**
129
- * To have full control of your search value and response use `onSearchChange` function combined with `searchValue` and custom filtering on the options array.
130
- * DO NOT USE TOGETHER WITH `search` PROPERTY
131
- */
132
- onSearchChange?: (value: string) => void;
133
- }
134
-
135
- const defaultClassNames = {};
136
-
137
79
  /**
138
80
  * @deprecated Use `SelectInput` instead (https://neptune.wise.design/blog/2023-11-28-adopting-our-new-selectinput)
139
81
  */
@@ -144,8 +86,8 @@ export default function Select({
144
86
  disabled,
145
87
  inverse,
146
88
  dropdownWidth,
147
- size = 'md',
148
- block = true,
89
+ size,
90
+ block,
149
91
  selected,
150
92
  search,
151
93
  onChange,
@@ -153,28 +95,29 @@ export default function Select({
153
95
  onBlur,
154
96
  options: defaultOptions,
155
97
  onSearchChange,
156
- searchValue: initSearchValue = '',
98
+ searchValue: initSearchValue,
157
99
  searchPlaceholder,
158
- classNames: classNamesProp = defaultClassNames,
100
+ // eslint-disable-next-line unicorn/prevent-abbreviations
101
+ classNames: classNamesProp,
159
102
  dropdownUp,
160
103
  dropdownProps,
161
104
  buttonProps,
162
- }: SelectProps) {
105
+ }) {
163
106
  const inputAttributes = useInputAttributes();
164
107
 
165
108
  const { formatMessage } = useIntl();
166
109
  const { isModern } = useTheme();
167
- const s = (className: string) => classNamesProp[className] || className;
110
+ const s = (className) => classNamesProp[className] || className;
168
111
  const [open, setOpen] = useState(false);
169
112
  const [searchValue, setSearchValue] = useState(DEFAULT_SEARCH_VALUE);
170
- const [keyboardFocusedOptionIndex, setKeyboardFocusedOptionIndex] = useState(-1);
171
- const keyboardFocusedReference = useRef<HTMLLIElement>(null);
172
- const previousKeyboardFocusedOptionIndex = useRef(-1);
113
+ const [keyboardFocusedOptionIndex, setKeyboardFocusedOptionIndex] = useState(null);
114
+ const keyboardFocusedReference = useRef();
115
+ const previousKeyboardFocusedOptionIndex = useRef();
173
116
  const [numberOfOptionsShown, setNumberOfOptionsShown] = useState(DEFAULT_OPTIONS_PAGE_SIZE);
174
- const searchBoxReference = useRef<HTMLInputElement>(null);
175
- const selectReference = useRef<HTMLDivElement>(null);
176
- const dropdownButtonReference = useRef<HTMLButtonElement>(null);
177
- const optionsListReference = useRef<HTMLUListElement>(null);
117
+ const searchBoxReference = useRef(null);
118
+ const selectReference = useRef(null);
119
+ const dropdownButtonReference = useRef(null);
120
+ const optionsListReference = useRef(null);
178
121
  const isSearchEnabled = !!onSearchChange || !!search;
179
122
  const isDropdownAutoWidth = dropdownWidth == null;
180
123
 
@@ -202,7 +145,7 @@ export default function Select({
202
145
  const { isMobile } = useLayout();
203
146
 
204
147
  useEffect(() => {
205
- let cancelled = false;
148
+ let cancelled;
206
149
 
207
150
  if (keyboardFocusedOptionIndex >= 0) {
208
151
  requestAnimationFrame(() => {
@@ -225,40 +168,42 @@ export default function Select({
225
168
  setOpen(true);
226
169
  };
227
170
 
228
- const handleTouchStart: React.TouchEventHandler<HTMLDivElement> = (event) => {
171
+ const handleTouchStart = (event) => {
229
172
  if (event.currentTarget === event.target && open) {
230
173
  handleCloseOptions();
231
174
  }
232
175
  };
233
176
 
234
- const handleOnFocus: React.FocusEventHandler<HTMLDivElement> = (event) => {
235
- onFocus?.(event);
177
+ const handleOnFocus = (event) => {
178
+ if (onFocus) {
179
+ onFocus(event);
180
+ }
236
181
  };
237
182
 
238
- const handleOnBlur: React.FocusEventHandler<HTMLDivElement> = (event) => {
183
+ const handleOnBlur = (event) => {
239
184
  const { nativeEvent } = event;
240
185
  if (nativeEvent) {
241
186
  const elementReceivingFocus = nativeEvent.relatedTarget;
242
187
  const select = event.currentTarget;
243
- if (
244
- select &&
245
- elementReceivingFocus instanceof Node &&
246
- select.contains(elementReceivingFocus)
247
- ) {
188
+ if (select && elementReceivingFocus && select.contains(elementReceivingFocus)) {
248
189
  return;
249
190
  }
250
191
  }
251
192
 
252
- onBlur?.(event);
193
+ if (onBlur) {
194
+ onBlur(event);
195
+ }
253
196
  };
254
197
 
255
- const handleSearchChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
198
+ const handleSearchChange = (event) => {
256
199
  setNumberOfOptionsShown(DEFAULT_OPTIONS_PAGE_SIZE);
257
200
  setSearchValue(event.target.value);
258
- onSearchChange?.(event.target.value);
201
+ if (onSearchChange) {
202
+ onSearchChange(event.target.value);
203
+ }
259
204
  };
260
205
 
261
- const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
206
+ const handleKeyDown = (event) => {
262
207
  switch (event.key) {
263
208
  case 'ArrowUp':
264
209
  case 'ArrowDown':
@@ -302,25 +247,25 @@ export default function Select({
302
247
  };
303
248
 
304
249
  function selectKeyboardFocusedOption() {
305
- if (keyboardFocusedOptionIndex >= 0 && selectableOptions.length > 0) {
250
+ if (keyboardFocusedOptionIndex != null && selectableOptions.length > 0) {
306
251
  selectOption(selectableOptions[keyboardFocusedOptionIndex]);
307
252
  }
308
253
  }
309
254
 
310
- function moveFocusWithDifference(difference: number) {
255
+ function moveFocusWithDifference(difference) {
311
256
  const selectedOptionIndex = selectableOptions.reduce((optionIndex, current, index) => {
312
- if (optionIndex >= 0) {
257
+ if (optionIndex != null) {
313
258
  return optionIndex;
314
259
  }
315
260
  if (isOptionSelected(selected, current)) {
316
261
  return index;
317
262
  }
318
- return -1;
319
- }, -1);
320
- const previousFocusedIndex = previousKeyboardFocusedOptionIndex.current;
263
+ return null;
264
+ }, null);
265
+ const previousFocusedIndex = previousKeyboardFocusedOptionIndex.current ?? -1;
321
266
  let indexToStartMovingFrom = previousFocusedIndex;
322
- if (previousFocusedIndex < 0) {
323
- if (selectedOptionIndex < 0) {
267
+ if (previousFocusedIndex === -1) {
268
+ if (selectedOptionIndex == null) {
324
269
  setKeyboardFocusedOptionIndex(0);
325
270
  } else {
326
271
  indexToStartMovingFrom = selectedOptionIndex;
@@ -342,7 +287,8 @@ export default function Select({
342
287
  if (
343
288
  !isSearchEnabled &&
344
289
  optionsListReference.current &&
345
- previousKeyboardFocusedOptionIndex.current < 0
290
+ (previousKeyboardFocusedOptionIndex.current == null ||
291
+ Number.isNaN(previousKeyboardFocusedOptionIndex.current))
346
292
  ) {
347
293
  optionsListReference.current.focus();
348
294
  }
@@ -350,31 +296,31 @@ export default function Select({
350
296
 
351
297
  previousKeyboardFocusedOptionIndex.current = keyboardFocusedOptionIndex;
352
298
  } else {
353
- previousKeyboardFocusedOptionIndex.current = -1;
299
+ previousKeyboardFocusedOptionIndex.current = null;
354
300
  }
355
301
  }, [open, searchValue, isSearchEnabled, isMobile, keyboardFocusedOptionIndex]);
356
302
 
357
303
  const handleCloseOptions = () => {
358
304
  setOpen(false);
359
- setKeyboardFocusedOptionIndex(-1);
305
+ setKeyboardFocusedOptionIndex(null);
360
306
  if (dropdownButtonReference.current) {
361
307
  dropdownButtonReference.current.focus();
362
308
  }
363
309
  };
364
310
 
365
- function createSelectHandlerForOption(option: SelectItemWithPlaceholder) {
366
- return (event: React.SyntheticEvent) => {
311
+ function createSelectHandlerForOption(option) {
312
+ return (event) => {
367
313
  stopPropagation(event);
368
314
  selectOption(option);
369
315
  };
370
316
  }
371
317
 
372
- function selectOption(option: SelectItemWithPlaceholder) {
318
+ function selectOption(option) {
373
319
  onChange(isPlaceholderOption(option) ? DEFAULT_SELECTED_OPTION : option);
374
320
  handleCloseOptions();
375
321
  }
376
322
 
377
- function renderOptionsList({ className = '' } = {}) {
323
+ function renderOptionsList({ className } = {}) {
378
324
  const dropdownClass = clsx(
379
325
  s('np-dropdown-menu'),
380
326
  {
@@ -391,8 +337,8 @@ export default function Select({
391
337
  id={listboxId}
392
338
  role="listbox"
393
339
  aria-orientation="vertical"
394
- aria-activedescendant={getUniqueIdForOption(id, selected ?? null)}
395
- tabIndex={-1}
340
+ aria-activedescendant={getUniqueIdForOption(id, selected)}
341
+ tabIndex="-1"
396
342
  className={dropdownClass}
397
343
  {...dropdownProps}
398
344
  >
@@ -416,7 +362,7 @@ export default function Select({
416
362
  }
417
363
 
418
364
  function ShowMoreOption() {
419
- function handleOnClick(event: React.SyntheticEvent<HTMLLIElement>) {
365
+ function handleOnClick(event) {
420
366
  stopPropagation(event);
421
367
  setNumberOfOptionsShown(numberOfOptionsShown + DEFAULT_OPTIONS_PAGE_SIZE);
422
368
  }
@@ -454,7 +400,7 @@ export default function Select({
454
400
  }
455
401
 
456
402
  // eslint-disable-next-line react/prop-types
457
- function HeaderOption({ children }: { children?: React.ReactNode }) {
403
+ function HeaderOption({ children }) {
458
404
  return (
459
405
  <li // eslint-disable-line jsx-a11y/no-noninteractive-element-interactions
460
406
  className={clsx(s('np-dropdown-header'), s('np-text-title-group'))}
@@ -466,11 +412,11 @@ export default function Select({
466
412
  );
467
413
  }
468
414
 
469
- function isOptionSelected(selected: SelectOptionItem | undefined, option: SelectItem) {
415
+ function isOptionSelected(selected, option) {
470
416
  return selected?.value === option?.value;
471
417
  }
472
418
 
473
- const renderOption = (option: SelectItem, index: number) => {
419
+ const renderOption = (option, index) => {
474
420
  const separatorOption = option;
475
421
  if (isSeparatorOption(separatorOption) && separatorOption?.separator) {
476
422
  return <SeparatorOption key={index} />;
@@ -508,25 +454,20 @@ export default function Select({
508
454
  aria-selected={isActive}
509
455
  aria-disabled={option.disabled}
510
456
  role="option"
511
- tabIndex={-1}
457
+ tabIndex="-1"
512
458
  className={className}
513
459
  onClick={handleOnClick}
514
460
  onKeyPress={handleOnClick}
515
461
  >
516
462
  {/* @ts-expect-error options needs DOM refactoring */}
517
463
  <a disabled={selectOption.disabled}>
518
- <Option
519
- label={undefined}
520
- value={undefined}
521
- {...selectOption}
522
- classNames={classNamesProp}
523
- />
464
+ <Option {...selectOption} classNames={classNamesProp} />
524
465
  </a>
525
466
  </li>
526
467
  );
527
468
  };
528
469
 
529
- function getIndexWithoutHeadersForIndexWithHeaders(index: number) {
470
+ function getIndexWithoutHeadersForIndexWithHeaders(index) {
530
471
  return options.reduce((sum, option, currentIndex) => {
531
472
  if (currentIndex < index && isActionableOption(option)) {
532
473
  return sum + 1;
@@ -537,11 +478,11 @@ export default function Select({
537
478
 
538
479
  const hasActiveOptions = !!defaultOptions.length;
539
480
  if (open && (initSearchValue || searchValue)) {
540
- if (hasActiveOptions && keyboardFocusedOptionIndex < 0) {
481
+ if (hasActiveOptions && keyboardFocusedOptionIndex == null) {
541
482
  setKeyboardFocusedOptionIndex(0);
542
483
  }
543
- if (!hasActiveOptions && keyboardFocusedOptionIndex >= 0) {
544
- setKeyboardFocusedOptionIndex(-1);
484
+ if (!hasActiveOptions && keyboardFocusedOptionIndex != null) {
485
+ setKeyboardFocusedOptionIndex(null);
545
486
  }
546
487
  }
547
488
 
@@ -567,8 +508,8 @@ export default function Select({
567
508
  inverse ? s('np-dropdown-toggle-navy') : null,
568
509
  )}
569
510
  // reset Button's styles
570
- type={undefined}
571
- priority={undefined}
511
+ type={null}
512
+ priority={null}
572
513
  disabled={disabled}
573
514
  aria-controls={listboxId}
574
515
  aria-expanded={open}
@@ -576,7 +517,7 @@ export default function Select({
576
517
  {...buttonProps}
577
518
  >
578
519
  {selected ? (
579
- <Option label={undefined} {...selected} classNames={classNamesProp} selected />
520
+ <Option {...selected} classNames={classNamesProp} selected />
580
521
  ) : (
581
522
  <span className={s('form-control-placeholder')}>{placeholder}</span>
582
523
  )}
@@ -621,3 +562,80 @@ export default function Select({
621
562
  </div>
622
563
  );
623
564
  }
565
+
566
+ Select.propTypes = {
567
+ placeholder: PropTypes.string,
568
+ id: PropTypes.string,
569
+ required: PropTypes.bool,
570
+ disabled: PropTypes.bool,
571
+ inverse: PropTypes.bool,
572
+ dropdownRight: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
573
+ dropdownWidth: PropTypes.oneOf(['sm', 'md', 'lg']),
574
+ size: PropTypes.oneOf(['sm', 'md', 'lg']),
575
+ block: PropTypes.bool,
576
+ selected: PropTypes.shape({
577
+ value: PropTypes.any.isRequired,
578
+ label: PropTypes.node,
579
+ icon: PropTypes.node,
580
+ currency: PropTypes.string,
581
+ note: PropTypes.node,
582
+ secondary: PropTypes.node,
583
+ }),
584
+ /**
585
+ * Search toggle
586
+ * if `true` default search functionality being enabled (not case sensitive search in option labels & currency props)
587
+ * if `function` you can define your own search function to implement custom search experience. This search function used while filtering the options array. The custom search function takes two parameters. First is the option the second is the keyword.
588
+ */
589
+ search: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
590
+ options: PropTypes.arrayOf(
591
+ PropTypes.shape({
592
+ value: PropTypes.any,
593
+ label: PropTypes.node,
594
+ header: PropTypes.node,
595
+ icon: PropTypes.node,
596
+ currency: PropTypes.string,
597
+ note: PropTypes.node,
598
+ secondary: PropTypes.node,
599
+ separator: PropTypes.bool,
600
+ disabled: PropTypes.bool,
601
+ searchStrings: PropTypes.arrayOf(PropTypes.string),
602
+ }),
603
+ ).isRequired,
604
+ searchValue: PropTypes.string,
605
+ searchPlaceholder: PropTypes.string,
606
+ classNames: PropTypes.objectOf(PropTypes.string),
607
+ dropdownUp: PropTypes.bool,
608
+ buttonProps: PropTypes.object,
609
+ dropdownProps: PropTypes.object,
610
+ onChange: PropTypes.func.isRequired,
611
+ onFocus: PropTypes.func,
612
+ onBlur: PropTypes.func,
613
+ /**
614
+ * To have full control of your search value and response use `onSearchChange` function combined with `searchValue` and custom filtering on the options array.
615
+ * DO NOT USE TOGETHER WITH `search` PROPERTY
616
+ */
617
+ onSearchChange: PropTypes.func,
618
+ };
619
+
620
+ Select.defaultProps = {
621
+ id: undefined,
622
+ placeholder: undefined,
623
+ size: 'md',
624
+ dropdownRight: null,
625
+ dropdownWidth: null,
626
+ inverse: false,
627
+ required: false,
628
+ disabled: false,
629
+ block: true,
630
+ selected: null,
631
+ onFocus: null,
632
+ onBlur: null,
633
+ onSearchChange: undefined,
634
+ search: false,
635
+ searchValue: '',
636
+ searchPlaceholder: undefined,
637
+ classNames: {},
638
+ dropdownUp: false,
639
+ buttonProps: {},
640
+ dropdownProps: {},
641
+ };
@@ -2,7 +2,6 @@ import { Breakpoint } from '../common';
2
2
  import { render, screen, userEvent, waitFor, mockMatchMedia } from '../test-utils';
3
3
 
4
4
  import Select from '.';
5
- import { SelectItemWithPlaceholder } from './Select';
6
5
 
7
6
  mockMatchMedia();
8
7
 
@@ -43,7 +42,7 @@ describe('Select', () => {
43
42
  global.requestAnimationFrame = originalRAF;
44
43
  });
45
44
 
46
- const openSelect = async (container: HTMLElement) => {
45
+ const openSelect = async (container) => {
47
46
  const button = screen.getByRole('button');
48
47
  await userEvent.click(button);
49
48
  };
@@ -82,7 +81,7 @@ describe('Select', () => {
82
81
  });
83
82
 
84
83
  describe('when options contain searchables', () => {
85
- let input: HTMLElement;
84
+ let input;
86
85
 
87
86
  const searchableOptions = [
88
87
  {
@@ -208,8 +207,8 @@ describe('Select', () => {
208
207
  container.querySelector('.np-dropdown-item'),
209
208
  );
210
209
  expect(
211
- screen.getByText('Hungarian forint').parentElement?.parentElement?.parentElement
212
- ?.parentElement,
210
+ screen.getByText('Hungarian forint').parentElement.parentElement.parentElement
211
+ .parentElement,
213
212
  ).toHaveClass('np-dropdown-item clickable np-dropdown-item--focused');
214
213
  });
215
214
 
@@ -232,7 +231,7 @@ describe('Select', () => {
232
231
  expect(container.querySelector('.np-dropdown-item.np-dropdown-item--focused')).toStrictEqual(
233
232
  container.querySelector('.np-dropdown-item'),
234
233
  );
235
- expect(screen.getByText('Estonia').parentElement?.parentElement).toHaveClass(
234
+ expect(screen.getByText('Estonia').parentElement.parentElement).toHaveClass(
236
235
  'np-dropdown-item clickable np-dropdown-item--focused',
237
236
  );
238
237
  });
@@ -248,8 +247,7 @@ describe('Select', () => {
248
247
  });
249
248
 
250
249
  it('should filter the options with a custom search function with 3 results', async () => {
251
- const searchFunction = (option: SelectItemWithPlaceholder, searchValue: string) =>
252
- typeof option.label === 'string' && option.label.includes(searchValue);
250
+ const searchFunction = (option, searchValue) => option.label.includes(searchValue);
253
251
  const { container } = render(<Select {...props} search={searchFunction} />);
254
252
  await openSelect(container);
255
253
 
@@ -0,0 +1,3 @@
1
+ import Select from './Select';
2
+
3
+ export default Select;
@@ -107,7 +107,13 @@ export class Snackbar extends Component<SnackbarProps, SnackbarState> {
107
107
  }}
108
108
  unmountOnExit
109
109
  >
110
- <Body ref={this.bodyRef} as="span" className="snackbar__text" aria-live="polite">
110
+ <Body
111
+ ref={this.bodyRef}
112
+ as="span"
113
+ className="snackbar__text"
114
+ role="status"
115
+ aria-live="assertive"
116
+ >
111
117
  {text}
112
118
  {action ? (
113
119
  <ActionButton className="snackbar__text__action" onClick={action.onClick}>
@@ -1,7 +0,0 @@
1
- export { default } from './Select';
2
- export type {
3
- SelectItem,
4
- SelectItemWithPlaceholder,
5
- SelectOptionItem,
6
- SelectProps,
7
- } from './Select';
File without changes
File without changes