@transferwise/components 46.4.0 → 46.5.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 (29) hide show
  1. package/build/index.esm.js +110 -116
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +110 -116
  4. package/build/index.js.map +1 -1
  5. package/build/types/index.d.ts +1 -0
  6. package/build/types/index.d.ts.map +1 -1
  7. package/build/types/inputs/SelectInput.d.ts +2 -2
  8. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  9. package/build/types/moneyInput/MoneyInput.d.ts +45 -31
  10. package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
  11. package/build/types/moneyInput/MoneyInput.messages.d.ts +6 -6
  12. package/build/types/moneyInput/MoneyInput.messages.d.ts.map +1 -1
  13. package/build/types/moneyInput/currencyFormatting.d.ts +2 -2
  14. package/build/types/moneyInput/currencyFormatting.d.ts.map +1 -1
  15. package/build/types/moneyInput/index.d.ts +2 -1
  16. package/build/types/moneyInput/index.d.ts.map +1 -1
  17. package/package.json +1 -1
  18. package/src/flowNavigation/FlowNavigation.story.js +1 -1
  19. package/src/index.ts +6 -0
  20. package/src/inputs/SelectInput.tsx +2 -2
  21. package/src/moneyInput/{MoneyInput.rtl.spec.js → MoneyInput.rtl.spec.tsx} +4 -4
  22. package/src/moneyInput/MoneyInput.spec.js +109 -49
  23. package/src/moneyInput/MoneyInput.story.tsx +6 -14
  24. package/src/moneyInput/{MoneyInput.js → MoneyInput.tsx} +189 -173
  25. package/src/moneyInput/{currencyFormatting.spec.js → currencyFormatting.spec.ts} +2 -2
  26. package/src/moneyInput/{currencyFormatting.js → currencyFormatting.ts} +7 -10
  27. package/src/moneyInput/index.ts +7 -0
  28. package/src/moneyInput/index.js +0 -1
  29. /package/src/moneyInput/{MoneyInput.messages.js → MoneyInput.messages.ts} +0 -0
package/build/index.js CHANGED
@@ -1557,7 +1557,7 @@ class DimmerManager {
1557
1557
  /**
1558
1558
  * Dimmer refs
1559
1559
  */
1560
-
1560
+ dimmers;
1561
1561
  constructor() {
1562
1562
  this.dimmers = [];
1563
1563
  }
@@ -5433,6 +5433,7 @@ var DynamicFieldDefinitionList$1 = DynamicFieldDefinitionList;
5433
5433
  const ESCAPED_OPENING_CHEVRON = '<';
5434
5434
  const ESCAPED_CLOSING_CHEVRON = '>';
5435
5435
  class EmphasisHtmlTransformer {
5436
+ tags;
5436
5437
  constructor(whitelistedTags) {
5437
5438
  this.tags = (whitelistedTags || []).map(tag => {
5438
5439
  return {
@@ -7866,20 +7867,17 @@ function getValidLocale(locale) {
7866
7867
  Intl.NumberFormat(noUnderscoreLocale);
7867
7868
  return noUnderscoreLocale;
7868
7869
  } catch {
7869
- return 'en-GB';
7870
+ return DEFAULT_LOCALE;
7870
7871
  }
7871
7872
  }
7872
- function getCurrencyDecimals(currency = '') {
7873
+ function getCurrencyDecimals(currency) {
7873
7874
  const upperCaseCurrency = currency.toUpperCase();
7874
- if (Object.prototype.hasOwnProperty.call(currencyDecimals, upperCaseCurrency)) {
7875
- return currencyDecimals[upperCaseCurrency];
7876
- }
7877
- return DEFAULT_CURRENCY_DECIMALS;
7875
+ return currencyDecimals[upperCaseCurrency] ?? DEFAULT_CURRENCY_DECIMALS;
7878
7876
  }
7879
7877
  function getDecimalSeparator(locale) {
7880
7878
  return isNumberLocaleSupported() ? 1.1.toLocaleString(locale)[1] : '.';
7881
7879
  }
7882
- function parseAmount(number, currency, locale) {
7880
+ function parseAmount(number, currency, locale = DEFAULT_LOCALE) {
7883
7881
  const validLocale = getValidLocale(locale);
7884
7882
  const precision = getCurrencyDecimals(currency);
7885
7883
  const groupSeparator = isNumberLocaleSupported() ? 10000 .toLocaleString(validLocale)[2] : ',';
@@ -7889,53 +7887,67 @@ function parseAmount(number, currency, locale) {
7889
7887
  return Math.abs(parsedAmount);
7890
7888
  }
7891
7889
 
7892
- const Currency = PropTypes__default.default.shape({
7893
- header: PropTypes__default.default.string,
7894
- value: PropTypes__default.default.string,
7895
- label: PropTypes__default.default.string,
7896
- currency: PropTypes__default.default.string,
7897
- note: PropTypes__default.default.string,
7898
- searchable: PropTypes__default.default.string
7899
- });
7900
7890
  const isNumberOrNull = v => neptuneValidation.isNumber(v) || neptuneValidation.isNull(v);
7901
- const formatAmountIfSet = (amount, currency, locale, maxLengthOverride) => {
7891
+ const formatAmountIfSet = ({
7892
+ amount,
7893
+ currency,
7894
+ locale,
7895
+ maxLengthOverride
7896
+ }) => {
7902
7897
  if (maxLengthOverride) {
7903
- return amount || '';
7898
+ return amount != null ? String(amount) : '';
7904
7899
  } else {
7905
7900
  return typeof amount === 'number' ? formatting.formatAmount(amount, currency, locale) : '';
7906
7901
  }
7907
7902
  };
7908
- const parseNumber = (amount, currency, locale, maxLengthOverride) => {
7903
+ const parseNumber = ({
7904
+ amount,
7905
+ currency,
7906
+ locale,
7907
+ maxLengthOverride
7908
+ }) => {
7909
7909
  if (!maxLengthOverride) {
7910
7910
  return parseAmount(amount, currency, locale);
7911
7911
  }
7912
7912
  if (maxLengthOverride && amount.length > maxLengthOverride) {
7913
7913
  return 0;
7914
7914
  }
7915
- return +amount;
7915
+ return Number(amount);
7916
7916
  };
7917
7917
  const inputKeyCodeAllowlist = new Set([KeyCodes.BACKSPACE, KeyCodes.DELETE, KeyCodes.COMMA, KeyCodes.PERIOD, KeyCodes.DOWN, KeyCodes.UP, KeyCodes.LEFT, KeyCodes.RIGHT, KeyCodes.ENTER, KeyCodes.ESCAPE, KeyCodes.TAB]);
7918
7918
  const inputKeyAllowlist = new Set([Key.PERIOD, Key.COMMA]);
7919
7919
  class MoneyInput extends React.Component {
7920
+ static defaultProps = {
7921
+ size: exports.Size.LARGE,
7922
+ classNames: {},
7923
+ selectProps: {}
7924
+ };
7925
+ amountFocused = false;
7920
7926
  constructor(props) {
7921
7927
  super(props);
7922
- const {
7923
- locale
7924
- } = this.props.intl;
7925
- this.formatMessage = this.props.intl.formatMessage;
7926
7928
  this.state = {
7927
7929
  searchQuery: '',
7928
- formattedAmount: formatAmountIfSet(props.amount, props.selectedCurrency.currency, locale, props.maxLengthOverride),
7929
- locale
7930
+ formattedAmount: formatAmountIfSet({
7931
+ amount: props.amount,
7932
+ currency: props.selectedCurrency.currency,
7933
+ locale: props.intl.locale,
7934
+ maxLengthOverride: props.maxLengthOverride
7935
+ }),
7936
+ locale: props.intl.locale
7930
7937
  };
7931
7938
  }
7932
7939
  UNSAFE_componentWillReceiveProps(nextProps) {
7933
7940
  this.setState({
7934
- locale: nextProps?.intl?.locale
7941
+ locale: nextProps.intl.locale
7935
7942
  });
7936
7943
  if (!this.amountFocused) {
7937
7944
  this.setState({
7938
- formattedAmount: formatAmountIfSet(nextProps.amount, nextProps.selectedCurrency.currency, nextProps?.intl?.locale, nextProps.maxLengthOverride)
7945
+ formattedAmount: formatAmountIfSet({
7946
+ amount: nextProps.amount,
7947
+ currency: nextProps.selectedCurrency.currency,
7948
+ locale: nextProps.intl.locale,
7949
+ maxLengthOverride: nextProps.maxLengthOverride
7950
+ })
7939
7951
  });
7940
7952
  }
7941
7953
  }
@@ -7955,16 +7967,26 @@ class MoneyInput extends React.Component {
7955
7967
  }
7956
7968
  };
7957
7969
  handlePaste = event => {
7958
- const paste = (event.clipboardData || window.clipboardData).getData('text');
7970
+ const paste = event.clipboardData.getData('text');
7959
7971
  const {
7960
7972
  locale
7961
7973
  } = this.state;
7962
- const parsed = neptuneValidation.isEmpty(paste) ? null : parseNumber(paste, this.props.selectedCurrency.currency, locale, this.props.maxLengthOverride);
7974
+ const parsed = neptuneValidation.isEmpty(paste) ? null : parseNumber({
7975
+ amount: paste,
7976
+ currency: this.props.selectedCurrency.currency,
7977
+ locale: locale,
7978
+ maxLengthOverride: this.props.maxLengthOverride
7979
+ });
7963
7980
  if (isNumberOrNull(parsed)) {
7964
7981
  this.setState({
7965
- formattedAmount: formatAmountIfSet(parsed, this.props.selectedCurrency.currency, locale, this.props.maxLengthOverride)
7982
+ formattedAmount: formatAmountIfSet({
7983
+ amount: parsed,
7984
+ currency: this.props.selectedCurrency.currency,
7985
+ locale: locale,
7986
+ maxLengthOverride: this.props.maxLengthOverride
7987
+ })
7966
7988
  });
7967
- this.props.onAmountChange(parsed);
7989
+ this.props.onAmountChange?.(parsed);
7968
7990
  }
7969
7991
  event.preventDefault();
7970
7992
  };
@@ -7975,9 +7997,14 @@ class MoneyInput extends React.Component {
7975
7997
  this.setState({
7976
7998
  formattedAmount: value
7977
7999
  });
7978
- const parsed = neptuneValidation.isEmpty(value) ? null : parseNumber(value, this.props.selectedCurrency.currency, this.state.locale, this.props.maxLengthOverride);
8000
+ const parsed = neptuneValidation.isEmpty(value) ? null : parseNumber({
8001
+ amount: value,
8002
+ currency: this.props.selectedCurrency.currency,
8003
+ locale: this.state.locale,
8004
+ maxLengthOverride: this.props.maxLengthOverride
8005
+ });
7979
8006
  if (isNumberOrNull(parsed)) {
7980
- this.props.onAmountChange(parsed);
8007
+ this.props.onAmountChange?.(parsed);
7981
8008
  }
7982
8009
  };
7983
8010
  onAmountBlur = () => {
@@ -7987,68 +8014,67 @@ class MoneyInput extends React.Component {
7987
8014
  onAmountFocus = () => {
7988
8015
  this.amountFocused = true;
7989
8016
  };
7990
- mapOption = item => {
7991
- return {
7992
- type: 'option',
7993
- value: item,
7994
- filterMatchers: [item.value, item.label, item.note, item.searchable]
7995
- };
7996
- };
7997
8017
  getSelectOptions() {
7998
- const selectOptions = [...filterOptionsForQuery(this.props.currencies, this.state.searchQuery)];
7999
- let formattedOptions = [];
8000
- let groupIndex = null;
8018
+ const selectOptions = filterCurrenciesForQuery(this.props.currencies, this.state.searchQuery);
8019
+ const formattedOptions = [];
8020
+ let currentGroupOptions;
8001
8021
  selectOptions.forEach(item => {
8002
- if (item.header) {
8022
+ if (item.header != null) {
8023
+ currentGroupOptions = [];
8003
8024
  formattedOptions.push({
8004
8025
  type: 'group',
8005
8026
  label: item.header,
8006
- options: []
8027
+ options: currentGroupOptions
8007
8028
  });
8008
- groupIndex = formattedOptions.length - 1;
8009
8029
  } else {
8010
- if (groupIndex === null) {
8011
- formattedOptions.push(this.mapOption(item));
8012
- } else {
8013
- formattedOptions[groupIndex]?.options.push(this.mapOption(item));
8014
- }
8030
+ (currentGroupOptions ?? formattedOptions).push({
8031
+ type: 'option',
8032
+ value: item,
8033
+ filterMatchers: [item.value, item.label, item.note ?? '', item.searchable ?? '']
8034
+ });
8015
8035
  }
8016
8036
  });
8017
8037
  return formattedOptions;
8018
8038
  }
8019
8039
  setAmount() {
8020
8040
  this.setState(previousState => {
8021
- const parsed = parseNumber(previousState.formattedAmount, this.props.selectedCurrency.currency, previousState.locale, this.props.maxLengthOverride);
8041
+ const parsed = parseNumber({
8042
+ amount: previousState.formattedAmount,
8043
+ currency: this.props.selectedCurrency.currency,
8044
+ locale: previousState.locale,
8045
+ maxLengthOverride: this.props.maxLengthOverride
8046
+ });
8022
8047
  if (!isNumberOrNull(parsed)) {
8023
8048
  return {
8024
8049
  formattedAmount: previousState.formattedAmount
8025
8050
  };
8026
8051
  }
8027
8052
  return {
8028
- formattedAmount: formatAmountIfSet(parsed, this.props.selectedCurrency.currency, previousState.locale, this.props.maxLengthOverride)
8053
+ formattedAmount: formatAmountIfSet({
8054
+ amount: parsed,
8055
+ currency: this.props.selectedCurrency.currency,
8056
+ locale: previousState.locale,
8057
+ maxLengthOverride: this.props.maxLengthOverride
8058
+ })
8029
8059
  };
8030
8060
  });
8031
8061
  }
8032
8062
  handleSelectChange = value => {
8033
8063
  this.handleSearchChange('');
8034
- this.props.onCurrencyChange(value);
8064
+ this.props.onCurrencyChange?.(value);
8035
8065
  };
8036
8066
  handleCustomAction = () => {
8037
8067
  this.handleSearchChange('');
8038
- if (this.props.onCustomAction) {
8039
- this.props.onCustomAction();
8040
- }
8068
+ this.props.onCustomAction?.();
8041
8069
  };
8042
8070
  handleSearchChange = searchQuery => {
8043
8071
  this.setState({
8044
8072
  searchQuery
8045
8073
  });
8046
- if (this.props.onSearchChange) {
8047
- this.props.onSearchChange({
8048
- searchQuery,
8049
- filteredOptions: filterOptionsForQuery(this.props.currencies, searchQuery)
8050
- });
8051
- }
8074
+ this.props.onSearchChange?.({
8075
+ searchQuery,
8076
+ filteredOptions: filterCurrenciesForQuery(this.props.currencies, searchQuery)
8077
+ });
8052
8078
  };
8053
8079
  style = className => this.props.classNames[className] || className;
8054
8080
  render() {
@@ -8088,7 +8114,12 @@ class MoneyInput extends React.Component {
8088
8114
  inputMode: "decimal",
8089
8115
  disabled: disabled,
8090
8116
  maxLength: maxLengthOverride,
8091
- placeholder: formatAmountIfSet(this.props.placeholder, this.props.selectedCurrency.currency, this.state.locale, this.props.maxLengthOverride),
8117
+ placeholder: formatAmountIfSet({
8118
+ amount: this.props.placeholder,
8119
+ currency: this.props.selectedCurrency.currency,
8120
+ locale: this.state.locale,
8121
+ maxLengthOverride: this.props.maxLengthOverride
8122
+ }),
8092
8123
  autoComplete: "off",
8093
8124
  onKeyDown: this.handleKeyDown,
8094
8125
  onChange: this.onAmountChange,
@@ -8133,11 +8164,11 @@ class MoneyInput extends React.Component {
8133
8164
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events
8134
8165
  jsxRuntime.jsx("div", {
8135
8166
  role: "button",
8136
- tabIndex: "0",
8167
+ tabIndex: 0,
8137
8168
  onClick: this.handleCustomAction,
8138
8169
  children: this.props.customActionLabel
8139
- }) : null,
8140
- placeholder: this.formatMessage(messages$3.selectPlaceholder),
8170
+ }) : undefined,
8171
+ placeholder: this.props.intl.formatMessage(messages$3.selectPlaceholder),
8141
8172
  filterable: true,
8142
8173
  filterPlaceholder: this.props.searchPlaceholder,
8143
8174
  disabled: disabled,
@@ -8154,25 +8185,25 @@ class MoneyInput extends React.Component {
8154
8185
  });
8155
8186
  }
8156
8187
  }
8157
- function filterOptionsForQuery(options, query) {
8188
+ function filterCurrenciesForQuery(currencies, query) {
8158
8189
  if (!query) {
8159
- return options;
8190
+ return [...currencies];
8160
8191
  }
8161
- const filteredOptions = removeDuplicateValueOptions(options).filter(option => isCurrencyOptionAndFitsQuery(option, query));
8192
+ const options = currencies.filter(option => option.header == null);
8193
+ const filteredOptions = removeDuplicateValueOptions(options).filter(option => currencyOptionFitsQuery(option, query));
8162
8194
  return sortOptionsLabelsToFirst(filteredOptions, query);
8163
8195
  }
8164
8196
  function removeDuplicateValueOptions(options) {
8165
- const result = [];
8166
- const resultValues = [];
8167
- options.forEach(option => {
8168
- if (option.value && !resultValues.includes(option.value)) {
8169
- result.push(option);
8170
- resultValues.push(option.value);
8197
+ const uniqueValues = new Set();
8198
+ return options.filter(option => {
8199
+ if (!uniqueValues.has(option.value)) {
8200
+ uniqueValues.add(option.value);
8201
+ return true;
8171
8202
  }
8203
+ return false;
8172
8204
  });
8173
- return result;
8174
8205
  }
8175
- function isCurrencyOptionAndFitsQuery(option, query) {
8206
+ function currencyOptionFitsQuery(option, query) {
8176
8207
  if (!option.value) {
8177
8208
  return false;
8178
8209
  }
@@ -8197,43 +8228,6 @@ function sortOptionsLabelsToFirst(options, query) {
8197
8228
  return 0;
8198
8229
  });
8199
8230
  }
8200
- MoneyInput.propTypes = {
8201
- id: PropTypes__default.default.string,
8202
- currencies: PropTypes__default.default.arrayOf(Currency).isRequired,
8203
- selectedCurrency: Currency.isRequired,
8204
- onCurrencyChange: PropTypes__default.default.func,
8205
- placeholder: PropTypes__default.default.number,
8206
- amount: PropTypes__default.default.number,
8207
- size: PropTypes__default.default.oneOf(['sm', 'md', 'lg']),
8208
- onAmountChange: PropTypes__default.default.func,
8209
- addon: PropTypes__default.default.node,
8210
- searchPlaceholder: PropTypes__default.default.string,
8211
- /**
8212
- * Allows the consumer to react to searching, while the search itself is handled internally. Called with `{ searchQuery: string, filteredOptions: Currency[] }`
8213
- */
8214
- onSearchChange: PropTypes__default.default.func,
8215
- customActionLabel: PropTypes__default.default.node,
8216
- onCustomAction: PropTypes__default.default.func,
8217
- classNames: PropTypes__default.default.objectOf(PropTypes__default.default.string),
8218
- selectProps: PropTypes__default.default.object,
8219
- maxLengthOverride: PropTypes__default.default.number
8220
- };
8221
- MoneyInput.defaultProps = {
8222
- id: null,
8223
- size: exports.Size.LARGE,
8224
- addon: null,
8225
- searchPlaceholder: '',
8226
- onSearchChange: undefined,
8227
- onCurrencyChange: null,
8228
- placeholder: null,
8229
- amount: null,
8230
- onAmountChange: null,
8231
- customActionLabel: '',
8232
- onCustomAction: null,
8233
- classNames: {},
8234
- selectProps: {},
8235
- maxLengthOverride: null
8236
- };
8237
8231
  var MoneyInput$1 = reactIntl.injectIntl(MoneyInput);
8238
8232
 
8239
8233
  const NavigationOptionList = ({