@transferwise/components 46.10.1 → 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 (41) hide show
  1. package/README.md +14 -1
  2. package/build/i18n/zh-HK.json +2 -2
  3. package/build/index.esm.js +49 -147
  4. package/build/index.esm.js.map +1 -1
  5. package/build/index.js +49 -147
  6. package/build/index.js.map +1 -1
  7. package/build/mocks.esm.js +40 -0
  8. package/build/mocks.esm.js.map +1 -0
  9. package/build/mocks.js +43 -0
  10. package/build/mocks.js.map +1 -0
  11. package/build/types/index.d.ts +1 -0
  12. package/build/types/index.d.ts.map +1 -1
  13. package/build/types/mocks.d.ts +9 -0
  14. package/build/types/mocks.d.ts.map +1 -0
  15. package/build/types/test-utils/window-mock.d.ts.map +1 -1
  16. package/build/types/typeahead/Typeahead.d.ts +95 -57
  17. package/build/types/typeahead/Typeahead.d.ts.map +1 -1
  18. package/build/types/typeahead/index.d.ts +2 -2
  19. package/build/types/typeahead/index.d.ts.map +1 -1
  20. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts +23 -41
  21. package/build/types/typeahead/typeaheadInput/TypeaheadInput.d.ts.map +1 -1
  22. package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts +9 -17
  23. package/build/types/typeahead/typeaheadOption/TypeaheadOption.d.ts.map +1 -1
  24. package/build/types/typeahead/util/highlight.d.ts +2 -1
  25. package/build/types/typeahead/util/highlight.d.ts.map +1 -1
  26. package/package.json +26 -7
  27. package/src/dimmer/Dimmer.spec.js +0 -4
  28. package/src/i18n/zh-HK.json +2 -2
  29. package/src/index.ts +1 -0
  30. package/src/mocks.ts +48 -0
  31. package/src/snackbar/Snackbar.spec.js +0 -4
  32. package/src/test-utils/window-mock.ts +7 -23
  33. package/src/typeahead/{Typeahead.story.js → Typeahead.story.tsx} +8 -7
  34. package/src/typeahead/{Typeahead.js → Typeahead.tsx} +110 -111
  35. package/src/typeahead/index.ts +2 -0
  36. package/src/typeahead/typeaheadInput/TypeaheadInput.spec.js +1 -0
  37. package/src/typeahead/typeaheadInput/{TypeaheadInput.js → TypeaheadInput.tsx} +35 -46
  38. package/src/typeahead/typeaheadOption/{TypeaheadOption.js → TypeaheadOption.tsx} +10 -20
  39. package/src/typeahead/util/{highlight.js → highlight.tsx} +1 -1
  40. package/src/withNextPortal/withNextPortal.spec.js +0 -4
  41. package/src/typeahead/index.js +0 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![ci-cd](https://github.com/transferwise/neptune-web/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/transferwise/neptune-web/actions) [![NPM](https://badge.fury.io/js/%40transferwise%2Fcomponents.svg)](https://www.npmjs.com/package/@transferwise/components)
2
2
 
3
- # Neptune Web React Components Temp
3
+ # Neptune Web React Components
4
4
 
5
5
  Neptune is the Design System built by and used at TransferWise. Neptune Web is the Neptune framework for Web. Neptune Web provides a way to build high quality, consistent user experiences on the web with ease.
6
6
 
@@ -48,6 +48,19 @@ Note: types for some of components are not 100% accurate (some of them will be j
48
48
 
49
49
  Please follow [rules for JS components](https://github.com/transferwise/neptune-web/blob/main/packages/components/CONTRIBUTING.md#js-component-rules) in order to generate accurate types for them.
50
50
 
51
+ ### Mocks for testing
52
+
53
+ We expose reusable mocks for Jest and Vitest under an isolated entry point. They can be applied by passing the testing framework as the parameter:
54
+
55
+ ```ts
56
+ import { mockMatchMedia, mockResizeObserver } from '@transferwise/components/mocks';
57
+ import { jest } from '@jest/globals';
58
+ import { vi } from 'vitest';
59
+
60
+ mockMatchMedia(jest); // With Jest
61
+ mockResizeObserver(vi); // With Vitest
62
+ ```
63
+
51
64
  ## Documentation
52
65
 
53
66
  Visit the [docs](https://transferwise.github.io/neptune-web/about/Home) for information on getting started, usage information and examples for each component.
@@ -4,10 +4,10 @@
4
4
  "neptune.ClearButton.ariaLabel": "清除",
5
5
  "neptune.CloseButton.ariaLabel": "關閉",
6
6
  "neptune.DateInput.day.label": "日",
7
- "neptune.DateInput.day.placeholder": "",
7
+ "neptune.DateInput.day.placeholder": "DD",
8
8
  "neptune.DateInput.month.label": "月",
9
9
  "neptune.DateInput.year.label": "年",
10
- "neptune.DateInput.year.placeholder": "",
10
+ "neptune.DateInput.year.placeholder": "YYYY",
11
11
  "neptune.DateLookup.day": "日",
12
12
  "neptune.DateLookup.goTo20YearView": "切換至20年視圖",
13
13
  "neptune.DateLookup.month": "月",
@@ -12210,8 +12210,10 @@ const TextareaWithDisplayFormat = props => /*#__PURE__*/jsx(WithDisplayFormat, {
12210
12210
  /* eslint-disable jsx-a11y/no-static-element-interactions */
12211
12211
  const DEFAULT_INPUT_MIN_WIDTH = 10;
12212
12212
  class TypeaheadInput extends Component {
12213
- constructor() {
12214
- super();
12213
+ inputRef = /*#__PURE__*/createRef();
12214
+ sizerRef = /*#__PURE__*/createRef();
12215
+ constructor(props) {
12216
+ super(props);
12215
12217
  this.state = {
12216
12218
  inputWidth: DEFAULT_INPUT_MIN_WIDTH
12217
12219
  };
@@ -12221,7 +12223,7 @@ class TypeaheadInput extends Component {
12221
12223
  autoFocus
12222
12224
  } = this.props;
12223
12225
  if (autoFocus) {
12224
- this.inputRef.focus();
12226
+ this.inputRef.current?.focus();
12225
12227
  }
12226
12228
  }
12227
12229
  componentDidUpdate(previousProps) {
@@ -12232,7 +12234,7 @@ class TypeaheadInput extends Component {
12232
12234
  recalculateWidth = () => {
12233
12235
  requestAnimationFrame(() => {
12234
12236
  this.setState({
12235
- inputWidth: Math.max(DEFAULT_INPUT_MIN_WIDTH, this.sizerRef.scrollWidth + 10)
12237
+ inputWidth: Math.max(DEFAULT_INPUT_MIN_WIDTH, this.sizerRef.current?.scrollWidth ?? 0 + 10)
12236
12238
  });
12237
12239
  });
12238
12240
  };
@@ -12257,9 +12259,7 @@ class TypeaheadInput extends Component {
12257
12259
  } = this.state;
12258
12260
  const hasPlaceholder = !multiple || selected.length === 0;
12259
12261
  return /*#__PURE__*/jsx(Input, {
12260
- ref: reference => {
12261
- this.inputRef = reference;
12262
- },
12262
+ ref: this.inputRef,
12263
12263
  className: classNames(multiple && 'typeahead__input'),
12264
12264
  name: name,
12265
12265
  id: `input-${typeaheadId}`,
@@ -12292,11 +12292,11 @@ class TypeaheadInput extends Component {
12292
12292
  } = this.props;
12293
12293
  return multiple ? /*#__PURE__*/jsxs("div", {
12294
12294
  className: "form-control typeahead__input-container",
12295
- style: maxHeight && {
12295
+ style: {
12296
12296
  maxHeight
12297
12297
  },
12298
12298
  onClick: () => {
12299
- this.inputRef.focus();
12299
+ this.inputRef.current?.focus();
12300
12300
  },
12301
12301
  children: [/*#__PURE__*/jsxs("div", {
12302
12302
  className: "typeahead__input-wrapper",
@@ -12304,39 +12304,13 @@ class TypeaheadInput extends Component {
12304
12304
  className: "typeahead__input-aligner"
12305
12305
  })]
12306
12306
  }), /*#__PURE__*/jsx("div", {
12307
- ref: reference => {
12308
- this.sizerRef = reference;
12309
- },
12307
+ ref: this.sizerRef,
12310
12308
  className: "sizer form-control typeahead__input",
12311
12309
  children: value
12312
12310
  })]
12313
12311
  }) : this.renderInput();
12314
12312
  }
12315
12313
  }
12316
- TypeaheadInput.propTypes = {
12317
- typeaheadId: PropTypes.string.isRequired,
12318
- name: PropTypes.string.isRequired,
12319
- autoFocus: PropTypes.bool,
12320
- multiple: PropTypes.bool.isRequired,
12321
- value: PropTypes.string.isRequired,
12322
- selected: PropTypes.arrayOf(PropTypes.object),
12323
- placeholder: PropTypes.string,
12324
- optionsShown: PropTypes.bool,
12325
- maxHeight: PropTypes.number,
12326
- autoComplete: PropTypes.string.isRequired,
12327
- onChange: PropTypes.func.isRequired,
12328
- renderChip: PropTypes.func.isRequired,
12329
- onKeyDown: PropTypes.func.isRequired,
12330
- onFocus: PropTypes.func.isRequired,
12331
- onPaste: PropTypes.func.isRequired
12332
- };
12333
- TypeaheadInput.defaultProps = {
12334
- autoFocus: false,
12335
- maxHeight: null,
12336
- placeholder: '',
12337
- optionsShown: false,
12338
- selected: []
12339
- };
12340
12314
 
12341
12315
  function highlight(value, query) {
12342
12316
  if (value && query) {
@@ -12357,9 +12331,9 @@ function highlight(value, query) {
12357
12331
  const Option = props => {
12358
12332
  const {
12359
12333
  option,
12360
- selected,
12361
- onClick,
12362
- query
12334
+ selected = false,
12335
+ onClick = () => {},
12336
+ query = ''
12363
12337
  } = props;
12364
12338
  const {
12365
12339
  label,
@@ -12389,46 +12363,48 @@ const Option = props => {
12389
12363
  })
12390
12364
  });
12391
12365
  };
12392
- Option.propTypes = {
12393
- option: PropTypes.shape({
12394
- label: PropTypes.string.isRequired,
12395
- note: PropTypes.string,
12396
- secondary: PropTypes.string
12397
- }).isRequired,
12398
- query: PropTypes.string,
12399
- selected: PropTypes.bool,
12400
- onClick: PropTypes.func
12401
- };
12402
- Option.defaultProps = {
12403
- selected: false,
12404
- query: '',
12405
- onClick: () => {}
12406
- };
12407
- var TypeaheadOption = Option;
12408
12366
 
12409
12367
  /* eslint-disable jsx-a11y/anchor-is-valid */
12410
12368
  /* eslint-disable jsx-a11y/click-events-have-key-events */
12411
12369
  /* eslint-disable jsx-a11y/no-static-element-interactions */
12412
-
12413
12370
  const DEFAULT_MIN_QUERY_LENGTH = 3;
12414
12371
  const SEARCH_DELAY = 200;
12415
12372
  class Typeahead extends Component {
12373
+ static defaultProps = {
12374
+ allowNew: false,
12375
+ autoFillOnBlur: true,
12376
+ autoFocus: false,
12377
+ chipSeparators: [],
12378
+ clearable: true,
12379
+ initialValue: [],
12380
+ inputAutoComplete: 'new-password',
12381
+ minQueryLength: DEFAULT_MIN_QUERY_LENGTH,
12382
+ multiple: false,
12383
+ searchDelay: SEARCH_DELAY,
12384
+ showSuggestions: true,
12385
+ showNewEntry: true,
12386
+ size: Size.MEDIUM,
12387
+ validateChip: () => true
12388
+ };
12416
12389
  constructor(props) {
12417
12390
  super(props);
12418
12391
  const {
12419
12392
  searchDelay,
12420
12393
  initialValue,
12421
12394
  multiple
12422
- } = props;
12395
+ } = this.props;
12423
12396
  this.handleSearchDebounced = debounce(this.handleSearch, searchDelay);
12424
12397
  const initialQuery = !multiple && initialValue.length > 0 ? initialValue[0].label : '';
12425
12398
  this.state = {
12426
12399
  selected: initialValue,
12427
12400
  errorState: false,
12428
12401
  query: initialQuery,
12429
- keyboardFocusedOptionIndex: null
12402
+ keyboardFocusedOptionIndex: null,
12403
+ optionsShown: false,
12404
+ isFocused: false
12430
12405
  };
12431
12406
  }
12407
+ handleSearchDebounced;
12432
12408
  UNSAFE_componentWillReceiveProps(nextProps) {
12433
12409
  if (nextProps.multiple !== this.props.multiple) {
12434
12410
  this.setState(previousState => {
@@ -12442,6 +12418,7 @@ class Typeahead extends Component {
12442
12418
  };
12443
12419
  }
12444
12420
  return {
12421
+ selected: previousState.selected,
12445
12422
  query: ''
12446
12423
  };
12447
12424
  });
@@ -12451,13 +12428,8 @@ class Typeahead extends Component {
12451
12428
  this.handleSearchDebounced.cancel();
12452
12429
  }
12453
12430
  handleOnFocus = () => {
12454
- const {
12455
- onFocus
12456
- } = this.props;
12457
12431
  this.showMenu();
12458
- if (onFocus) {
12459
- this.props.onFocus();
12460
- }
12432
+ this.props.onFocus?.();
12461
12433
  };
12462
12434
  onOptionSelected = (event, item) => {
12463
12435
  event.preventDefault();
@@ -12540,7 +12512,7 @@ class Typeahead extends Component {
12540
12512
  break;
12541
12513
  case 'Enter':
12542
12514
  event.preventDefault();
12543
- if (options[keyboardFocusedOptionIndex]) {
12515
+ if (keyboardFocusedOptionIndex != null && options[keyboardFocusedOptionIndex]) {
12544
12516
  this.selectItem(options[keyboardFocusedOptionIndex]);
12545
12517
  } else if (allowNew && query.trim()) {
12546
12518
  this.selectItem({
@@ -12592,13 +12564,6 @@ class Typeahead extends Component {
12592
12564
  query
12593
12565
  });
12594
12566
  };
12595
- stopPropagation = event => {
12596
- event.stopPropagation();
12597
- event.preventDefault();
12598
- if (event.nativeEvent && event.nativeEvent.stopImmediatePropagation) {
12599
- event.nativeEvent.stopImmediatePropagation();
12600
- }
12601
- };
12602
12567
  handleSearch = query => {
12603
12568
  const {
12604
12569
  onSearch
@@ -12683,7 +12648,7 @@ class Typeahead extends Component {
12683
12648
  }
12684
12649
  };
12685
12650
  renderChip = (option, idx) => {
12686
- const valid = this.props.validateChip(option);
12651
+ const valid = this.props.validateChip?.(option);
12687
12652
  return /*#__PURE__*/jsx(Chip, {
12688
12653
  label: option.label,
12689
12654
  className: classNames('m-t-1', {
@@ -12718,7 +12683,7 @@ class Typeahead extends Component {
12718
12683
  className: "dropdown-menu",
12719
12684
  role: "menu",
12720
12685
  "aria-labelledby": "dropdownMenu1",
12721
- children: [optionsToRender.map((option, idx) => /*#__PURE__*/jsx(TypeaheadOption, {
12686
+ children: [optionsToRender.map((option, idx) => /*#__PURE__*/jsx(Option, {
12722
12687
  query: query,
12723
12688
  option: option,
12724
12689
  selected: keyboardFocusedOptionIndex === idx,
@@ -12761,16 +12726,18 @@ class Typeahead extends Component {
12761
12726
  const menu = this.renderMenu({
12762
12727
  footer,
12763
12728
  options,
12729
+ id,
12764
12730
  keyboardFocusedOptionIndex,
12765
12731
  query,
12766
12732
  allowNew,
12767
12733
  showNewEntry,
12768
12734
  dropdownOpen
12769
12735
  });
12770
- const hasError = errorState || alert && alert.type === Sentiment.ERROR;
12771
- const displayAlert = !errorState && alert || alert && alert.type === Sentiment.ERROR;
12772
- const hasWarning = displayAlert && alert.type === Sentiment.WARNING;
12773
- const hasInfo = displayAlert && alert.type === Sentiment.NEUTRAL;
12736
+ const alertType = alert?.type ?? Sentiment.NEUTRAL;
12737
+ const hasError = errorState || alert && alertType === Sentiment.ERROR;
12738
+ const displayAlert = !errorState && alert || alert && alertType === Sentiment.ERROR;
12739
+ const hasWarning = displayAlert && alertType === Sentiment.WARNING;
12740
+ const hasInfo = displayAlert && alertType === Sentiment.NEUTRAL;
12774
12741
  return /*#__PURE__*/jsx("div", {
12775
12742
  id: id,
12776
12743
  className: classNames('typeahead', `typeahead-${size}`, {
@@ -12779,7 +12746,7 @@ class Typeahead extends Component {
12779
12746
  'typeahead--multiple': multiple,
12780
12747
  open: dropdownOpen
12781
12748
  }),
12782
- onClick: this.stopPropagation,
12749
+ onClick: stopPropagation$1,
12783
12750
  children: /*#__PURE__*/jsxs("div", {
12784
12751
  className: classNames('form-group', {
12785
12752
  'has-error': hasError,
@@ -12800,6 +12767,7 @@ class Typeahead extends Component {
12800
12767
  placeholder,
12801
12768
  selected,
12802
12769
  maxHeight,
12770
+ id: id,
12803
12771
  name: name,
12804
12772
  value: query,
12805
12773
  typeaheadId: id,
@@ -12826,72 +12794,6 @@ class Typeahead extends Component {
12826
12794
  });
12827
12795
  }
12828
12796
  }
12829
- Typeahead.propTypes = {
12830
- id: PropTypes.string.isRequired,
12831
- name: PropTypes.string.isRequired,
12832
- options: PropTypes.arrayOf(PropTypes.shape({
12833
- label: PropTypes.string.isRequired,
12834
- note: PropTypes.string,
12835
- secondary: PropTypes.string,
12836
- value: PropTypes.object
12837
- })).isRequired,
12838
- initialValue: PropTypes.arrayOf(PropTypes.shape({
12839
- label: PropTypes.string.isRequired,
12840
- note: PropTypes.string,
12841
- secondary: PropTypes.string
12842
- })),
12843
- onChange: PropTypes.func.isRequired,
12844
- allowNew: PropTypes.bool,
12845
- autoFocus: PropTypes.bool,
12846
- clearable: PropTypes.bool,
12847
- multiple: PropTypes.bool,
12848
- showSuggestions: PropTypes.bool,
12849
- showNewEntry: PropTypes.bool,
12850
- searchDelay: PropTypes.number,
12851
- maxHeight: PropTypes.number,
12852
- minQueryLength: PropTypes.number,
12853
- addon: PropTypes.node,
12854
- placeholder: PropTypes.string,
12855
- alert: PropTypes.shape({
12856
- message: PropTypes.string.isRequired,
12857
- type: PropTypes.oneOf(['error', 'warning', 'neutral']).isRequired
12858
- }),
12859
- footer: PropTypes.node,
12860
- validateChip: PropTypes.func,
12861
- onSearch: PropTypes.func,
12862
- onBlur: PropTypes.func,
12863
- onInputChange: PropTypes.func,
12864
- onFocus: PropTypes.func,
12865
- chipSeparators: PropTypes.arrayOf(PropTypes.string),
12866
- size: PropTypes.oneOf(['md', 'lg']),
12867
- inputAutoComplete: PropTypes.string,
12868
- autoFillOnBlur: PropTypes.bool
12869
- };
12870
- Typeahead.defaultProps = {
12871
- allowNew: false,
12872
- autoFocus: false,
12873
- clearable: true,
12874
- multiple: false,
12875
- maxHeight: null,
12876
- showSuggestions: true,
12877
- showNewEntry: true,
12878
- searchDelay: SEARCH_DELAY,
12879
- minQueryLength: DEFAULT_MIN_QUERY_LENGTH,
12880
- addon: null,
12881
- placeholder: null,
12882
- alert: null,
12883
- footer: null,
12884
- size: Size.MEDIUM,
12885
- chipSeparators: [],
12886
- initialValue: [],
12887
- onSearch: null,
12888
- onBlur: null,
12889
- onInputChange: null,
12890
- onFocus: null,
12891
- validateChip: () => true,
12892
- inputAutoComplete: 'new-password',
12893
- autoFillOnBlur: true
12894
- };
12895
12797
 
12896
12798
  // TODO: consider to move this enum into component file once we migrate it on TypeScript or replace with some common enum
12897
12799
  var UploadStep;
@@ -15440,10 +15342,10 @@ var zhHK = {
15440
15342
  "neptune.ClearButton.ariaLabel": "清除",
15441
15343
  "neptune.CloseButton.ariaLabel": "關閉",
15442
15344
  "neptune.DateInput.day.label": "日",
15443
- "neptune.DateInput.day.placeholder": "",
15345
+ "neptune.DateInput.day.placeholder": "DD",
15444
15346
  "neptune.DateInput.month.label": "月",
15445
15347
  "neptune.DateInput.year.label": "年",
15446
- "neptune.DateInput.year.placeholder": "",
15348
+ "neptune.DateInput.year.placeholder": "YYYY",
15447
15349
  "neptune.DateLookup.day": "日",
15448
15350
  "neptune.DateLookup.goTo20YearView": "切換至20年視圖",
15449
15351
  "neptune.DateLookup.month": "月",