@transferwise/components 46.7.0 → 46.9.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 (50) hide show
  1. package/build/index.esm.js +154 -76
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +155 -76
  4. package/build/index.js.map +1 -1
  5. package/build/main.css +101 -0
  6. package/build/styles/main.css +101 -0
  7. package/build/styles/segmentedControl/SegmentedControl.css +101 -0
  8. package/build/types/checkboxOption/CheckboxOption.d.ts +2 -2
  9. package/build/types/checkboxOption/CheckboxOption.d.ts.map +1 -1
  10. package/build/types/index.d.ts +4 -0
  11. package/build/types/index.d.ts.map +1 -1
  12. package/build/types/segmentedControl/SegmentedControl.d.ts +31 -0
  13. package/build/types/segmentedControl/SegmentedControl.d.ts.map +1 -0
  14. package/build/types/segmentedControl/index.d.ts +3 -0
  15. package/build/types/segmentedControl/index.d.ts.map +1 -0
  16. package/build/types/snackbar/Snackbar.d.ts +30 -22
  17. package/build/types/snackbar/Snackbar.d.ts.map +1 -1
  18. package/build/types/snackbar/SnackbarContext.d.ts +7 -2
  19. package/build/types/snackbar/SnackbarContext.d.ts.map +1 -1
  20. package/build/types/snackbar/SnackbarProvider.d.ts +7 -12
  21. package/build/types/snackbar/SnackbarProvider.d.ts.map +1 -1
  22. package/build/types/snackbar/index.d.ts +2 -0
  23. package/build/types/snackbar/index.d.ts.map +1 -0
  24. package/build/types/snackbar/useSnackbar.d.ts +1 -1
  25. package/build/types/snackbar/useSnackbar.d.ts.map +1 -1
  26. package/build/types/withNextPortal/withNextPortal.d.ts +1 -1
  27. package/build/types/withNextPortal/withNextPortal.d.ts.map +1 -1
  28. package/package.json +9 -18
  29. package/src/checkboxOption/CheckboxOption.tsx +2 -2
  30. package/src/index.ts +4 -0
  31. package/src/main.css +101 -0
  32. package/src/main.less +1 -0
  33. package/src/segmentedControl/SegmentedControl.css +101 -0
  34. package/src/segmentedControl/SegmentedControl.less +101 -0
  35. package/src/segmentedControl/SegmentedControl.spec.tsx +106 -0
  36. package/src/segmentedControl/SegmentedControl.story.tsx +55 -0
  37. package/src/segmentedControl/SegmentedControl.tsx +175 -0
  38. package/src/segmentedControl/index.ts +2 -0
  39. package/src/snackbar/{Snackbar.story.js → Snackbar.story.tsx} +2 -1
  40. package/src/snackbar/{Snackbar.js → Snackbar.tsx} +31 -32
  41. package/src/snackbar/SnackbarContext.ts +11 -0
  42. package/src/snackbar/SnackbarProvider.tsx +39 -0
  43. package/src/ssr.spec.js +17 -0
  44. package/src/withDisplayFormat/WithDisplayFormat.spec.js +1 -1
  45. package/src/withDisplayFormat/WithDisplayFormat.tsx +1 -1
  46. package/src/withNextPortal/withNextPortal.tsx +1 -1
  47. package/src/snackbar/SnackbarContext.js +0 -4
  48. package/src/snackbar/SnackbarProvider.js +0 -51
  49. /package/src/snackbar/{index.js → index.ts} +0 -0
  50. /package/src/snackbar/{useSnackbar.js → useSnackbar.ts} +0 -0
package/build/index.js CHANGED
@@ -2612,7 +2612,7 @@ const CheckboxOption = /*#__PURE__*/React.forwardRef(({
2612
2612
  button: /*#__PURE__*/jsxRuntime.jsx(CheckboxButton$1, {
2613
2613
  checked: checked,
2614
2614
  disabled: disabled,
2615
- onChange: () => onChange(!checked)
2615
+ onChange: () => onChange?.(!checked)
2616
2616
  })
2617
2617
  });
2618
2618
  });
@@ -7544,7 +7544,7 @@ class WithDisplayFormat extends React.Component {
7544
7544
  const cursorPosition = getCursorPositionAfterKeystroke(action, selectionStart, selectionEnd, displayPattern, pastedLength);
7545
7545
  setTimeout(() => {
7546
7546
  if (triggerEvent) {
7547
- triggerEvent.currentTarget.setSelectionRange(cursorPosition, cursorPosition);
7547
+ triggerEvent.target.setSelectionRange(cursorPosition, cursorPosition);
7548
7548
  }
7549
7549
  this.setState({
7550
7550
  selectionStart: cursorPosition,
@@ -9913,7 +9913,6 @@ const PromoCard = /*#__PURE__*/React.forwardRef(({
9913
9913
  setChecked(!checked); // Update local state for checkbox
9914
9914
  }
9915
9915
  };
9916
-
9917
9916
  const componentId = `${id || generateRandomId()}`;
9918
9917
  // Set the icon to `'arrow'` if `href` is truthy and `type` is falsy, or
9919
9918
  // `'download'` if `download` is truthy. If neither condition is true, set
@@ -11077,20 +11076,124 @@ Select.defaultProps = {
11077
11076
  dropdownProps: {}
11078
11077
  };
11079
11078
 
11079
+ const SegmentedControl = ({
11080
+ name,
11081
+ defaultValue,
11082
+ mode = 'input',
11083
+ segments,
11084
+ onChange
11085
+ }) => {
11086
+ const [selectedValue, setSelectedValue] = React.useState(defaultValue || segments[0].value);
11087
+ const [animate, setAnimate] = React.useState(false);
11088
+ const segmentsRef = React.useRef(null);
11089
+ if (segments.length > 3) {
11090
+ throw new Error('SegmentedControl only supports up to 3 segments. Please refer to: https://wise.design/components/segmented-control');
11091
+ }
11092
+ const segmentsWithRefs = segments.map(segment => ({
11093
+ ...segment,
11094
+ ref: /*#__PURE__*/React.createRef()
11095
+ }));
11096
+ const updateSegmentPosition = () => {
11097
+ const selectedSegmentRef = segmentsWithRefs.find(segment => segment.value === selectedValue)?.ref;
11098
+ // We grab the active segments style object from the ref
11099
+ // and set the css variables to the selected segments width and x position.
11100
+ // This is so we can animate the highlight to the selected segment
11101
+ if (selectedSegmentRef?.current && segmentsRef.current) {
11102
+ const {
11103
+ style
11104
+ } = segmentsRef.current;
11105
+ style.setProperty('--segment-highlight-width', `${selectedSegmentRef.current.offsetWidth}px`);
11106
+ style.setProperty('--segment-highlight-x', `${selectedSegmentRef.current.offsetLeft}px`);
11107
+ }
11108
+ };
11109
+ React.useEffect(() => {
11110
+ updateSegmentPosition();
11111
+ const handleWindowSizeChange = () => {
11112
+ setAnimate(false);
11113
+ updateSegmentPosition();
11114
+ };
11115
+ window.addEventListener('resize', handleWindowSizeChange);
11116
+ return () => {
11117
+ window.removeEventListener('resize', handleWindowSizeChange);
11118
+ };
11119
+ // eslint-disable-next-line react-hooks/exhaustive-deps
11120
+ }, [segmentsWithRefs, selectedValue]);
11121
+ React.useEffect(() => {
11122
+ onChange(selectedValue);
11123
+ }, [onChange, selectedValue]);
11124
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
11125
+ ref: segmentsRef,
11126
+ "data-testid": "segmented-control",
11127
+ className: classNames__default.default('segmented-control', {
11128
+ 'segmented-control--input': mode === 'input'
11129
+ }),
11130
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
11131
+ className: classNames__default.default('segmented-control__segments', {
11132
+ 'segmented-control__segments--no-animate': !animate
11133
+ }),
11134
+ children: segmentsWithRefs.map(segment => mode === 'input' ? /*#__PURE__*/jsxRuntime.jsxs("label", {
11135
+ ref: segment.ref,
11136
+ htmlFor: segment.id,
11137
+ className: classNames__default.default('segmented-control__segment', {
11138
+ 'segmented-control__selected-segment': selectedValue === segment.value
11139
+ }),
11140
+ children: [/*#__PURE__*/jsxRuntime.jsx("input", {
11141
+ type: "radio",
11142
+ className: "segmented-control__radio-input",
11143
+ id: segment.id,
11144
+ name: name,
11145
+ value: segment.value,
11146
+ checked: selectedValue === segment.value,
11147
+ onChange: () => {
11148
+ setAnimate(true);
11149
+ setSelectedValue(segment.value);
11150
+ }
11151
+ }), /*#__PURE__*/jsxRuntime.jsx(Body, {
11152
+ className: "segmented-control__text",
11153
+ as: "span",
11154
+ type: selectedValue === segment.value ? exports.Typography.BODY_DEFAULT_BOLD : exports.Typography.BODY_DEFAULT,
11155
+ children: segment.label
11156
+ })]
11157
+ }, segment.id) : /*#__PURE__*/jsxRuntime.jsx("button", {
11158
+ ref: segment.ref,
11159
+ type: "button",
11160
+ role: "tab",
11161
+ id: segment.id,
11162
+ "aria-controls": segment.controls,
11163
+ "aria-selected": selectedValue === segment.value,
11164
+ className: classNames__default.default('segmented-control__segment', 'segmented-control__button', {
11165
+ 'segmented-control__selected-segment': selectedValue === segment.value
11166
+ }),
11167
+ onClick: () => {
11168
+ setAnimate(true);
11169
+ setSelectedValue(segment.value);
11170
+ },
11171
+ children: /*#__PURE__*/jsxRuntime.jsx(Body, {
11172
+ as: "span",
11173
+ className: "segmented-control__text",
11174
+ type: selectedValue === segment.value ? exports.Typography.BODY_DEFAULT_BOLD : exports.Typography.BODY_DEFAULT,
11175
+ children: segment.label
11176
+ })
11177
+ }, segment.id))
11178
+ })
11179
+ });
11180
+ };
11181
+
11080
11182
  const CSS_TRANSITION_DURATION = 400;
11081
11183
  class Snackbar extends React.Component {
11082
- /** @type {RefObject<HTMLSpanElement>} */
11083
11184
  bodyRef = /*#__PURE__*/React.createRef();
11084
- constructor() {
11085
- super();
11185
+ timeout = 0;
11186
+ transitionTimeout = 0;
11187
+ constructor(props) {
11188
+ super(props);
11086
11189
  this.state = {
11087
11190
  visible: false,
11088
11191
  text: ''
11089
11192
  };
11090
11193
  }
11091
11194
  componentWillUnmount() {
11092
- clearTimeout(this.timeout);
11093
- clearTimeout(this.transitionTimeout);
11195
+ window.clearTimeout(this.timeout);
11196
+ window.clearTimeout(this.transitionTimeout);
11094
11197
  }
11095
11198
  shouldComponentUpdate(nextProps, nextState) {
11096
11199
  if (!nextProps.text) {
@@ -11105,7 +11208,7 @@ class Snackbar extends React.Component {
11105
11208
  const {
11106
11209
  timeout
11107
11210
  } = this.props;
11108
- this.timeout = setTimeout(() => {
11211
+ this.timeout = window.setTimeout(() => {
11109
11212
  this.setState({
11110
11213
  visible: false
11111
11214
  });
@@ -11128,12 +11231,12 @@ class Snackbar extends React.Component {
11128
11231
  this.setLeaveTimeout();
11129
11232
  });
11130
11233
  } else if (previousProps.timestamp !== timestamp) {
11131
- clearTimeout(this.timeout);
11234
+ window.clearTimeout(this.timeout);
11132
11235
  if (this.state.visible) {
11133
11236
  this.setState({
11134
11237
  visible: false
11135
11238
  }, () => {
11136
- this.transitionTimeout = setTimeout(() => {
11239
+ this.transitionTimeout = window.setTimeout(() => {
11137
11240
  this.setState({
11138
11241
  visible: true,
11139
11242
  action,
@@ -11158,7 +11261,7 @@ class Snackbar extends React.Component {
11158
11261
  const {
11159
11262
  action,
11160
11263
  text,
11161
- theme,
11264
+ theme = exports.Theme.LIGHT,
11162
11265
  visible
11163
11266
  } = this.state;
11164
11267
  const {
@@ -11192,74 +11295,51 @@ class Snackbar extends React.Component {
11192
11295
  }
11193
11296
  }
11194
11297
  Snackbar.contextType = DirectionContext;
11195
- Snackbar.propTypes = {
11196
- action: PropTypes__default.default.shape({
11197
- label: PropTypes__default.default.string.isRequired,
11198
- onClick: PropTypes__default.default.func
11199
- }),
11200
- text: PropTypes__default.default.node.isRequired,
11201
- theme: PropTypes__default.default.oneOf(['light', 'dark']),
11202
- timeout: PropTypes__default.default.number.isRequired,
11203
- timestamp: PropTypes__default.default.number.isRequired
11204
- };
11205
- Snackbar.defaultProps = {
11206
- action: null,
11207
- theme: exports.Theme.LIGHT
11208
- };
11209
11298
  var SnackbarPortal = withNextPortalWrapper(Snackbar);
11210
11299
 
11211
- const SnackbarContext = /*#__PURE__*/React.createContext();
11300
+ const SnackbarContext = /*#__PURE__*/React.createContext({
11301
+ createSnackbar: () => {}
11302
+ });
11212
11303
  const SnackbarConsumer = SnackbarContext.Consumer;
11213
11304
 
11214
- class SnackbarProvider extends React.Component {
11215
- constructor() {
11216
- super();
11217
- this.state = {
11218
- text: '',
11219
- timestamp: 0
11220
- };
11221
- }
11222
- create = ({
11305
+ function SnackbarProvider({
11306
+ timeout = 4500,
11307
+ children
11308
+ }) {
11309
+ const [state, setState] = React.useState({
11310
+ text: '',
11311
+ timestamp: 0
11312
+ });
11313
+ const {
11223
11314
  action,
11224
11315
  text,
11225
- theme
11226
- }) => {
11227
- this.setState({
11228
- action,
11229
- text,
11230
- theme,
11231
- timestamp: Date.now()
11232
- });
11233
- };
11234
- render() {
11235
- const {
11236
- action,
11237
- text,
11238
- theme,
11239
- timestamp
11240
- } = this.state;
11241
- return /*#__PURE__*/jsxRuntime.jsxs(SnackbarContext.Provider, {
11242
- value: {
11243
- createSnackbar: this.create
11244
- },
11245
- children: [/*#__PURE__*/jsxRuntime.jsx(SnackbarPortal, {
11246
- action: action,
11247
- text: text,
11248
- timestamp: timestamp,
11249
- timeout: this.props.timeout,
11250
- theme: theme
11251
- }), this.props.children]
11252
- });
11253
- }
11316
+ theme,
11317
+ timestamp
11318
+ } = state;
11319
+ return /*#__PURE__*/jsxRuntime.jsxs(SnackbarContext.Provider, {
11320
+ value: React.useMemo(() => ({
11321
+ createSnackbar: ({
11322
+ action,
11323
+ text,
11324
+ theme
11325
+ }) => {
11326
+ setState({
11327
+ action,
11328
+ text,
11329
+ theme,
11330
+ timestamp: Date.now()
11331
+ });
11332
+ }
11333
+ }), []),
11334
+ children: [/*#__PURE__*/jsxRuntime.jsx(SnackbarPortal, {
11335
+ action: action,
11336
+ text: text,
11337
+ timestamp: timestamp,
11338
+ timeout: timeout,
11339
+ theme: theme
11340
+ }), children]
11341
+ });
11254
11342
  }
11255
- SnackbarProvider.propTypes = {
11256
- children: PropTypes__default.default.node.isRequired,
11257
- timeout: PropTypes__default.default.number
11258
- };
11259
- SnackbarProvider.defaultProps = {
11260
- timeout: 4500
11261
- };
11262
- var SnackbarProvider$1 = SnackbarProvider;
11263
11343
 
11264
11344
  const Sticky = ({
11265
11345
  open,
@@ -13861,7 +13941,6 @@ const UploadButton = ({
13861
13941
  if (areAllFilesAllowed) {
13862
13942
  return null; //file input by default allows all files
13863
13943
  }
13864
-
13865
13944
  if (Array.isArray(fileTypes)) {
13866
13945
  return {
13867
13946
  accept: fileTypes.join(',')
@@ -14059,7 +14138,6 @@ const UploadItem = ({
14059
14138
  children: processIndicator
14060
14139
  }); // Scale down ProcessIndicator to be 20px*20px to match `icons`
14061
14140
  };
14062
-
14063
14141
  const getErrorMessage = () => typeof error === 'object' && error.message || error || formatMessage(MESSAGES.uploadingFailed);
14064
14142
  const getDescription = () => {
14065
14143
  if (error || status === exports.Status.FAILED) {
@@ -15546,6 +15624,7 @@ exports.RadioOption = RadioOption$1;
15546
15624
  exports.SUPPORTED_LANGUAGES = SUPPORTED_LANGUAGES;
15547
15625
  exports.SearchInput = SearchInput;
15548
15626
  exports.Section = Section;
15627
+ exports.SegmentedControl = SegmentedControl;
15549
15628
  exports.Select = Select;
15550
15629
  exports.SelectInput = SelectInput;
15551
15630
  exports.SelectInputOptionContent = SelectInputOptionContent;
@@ -15554,7 +15633,7 @@ exports.SlidingPanel = SlidingPanel$1;
15554
15633
  exports.SnackbarConsumer = SnackbarConsumer;
15555
15634
  exports.SnackbarContext = SnackbarContext;
15556
15635
  exports.SnackbarPortal = SnackbarPortal;
15557
- exports.SnackbarProvider = SnackbarProvider$1;
15636
+ exports.SnackbarProvider = SnackbarProvider;
15558
15637
  exports.StatusIcon = StatusIcon;
15559
15638
  exports.Stepper = Stepper;
15560
15639
  exports.Sticky = Sticky$1;