@transferwise/components 0.0.0-experimental-5bcac43 → 0.0.0-experimental-b300585

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.
@@ -1 +1 @@
1
- {"version":3,"file":"_BottomSheet.d.ts","sourceRoot":"","sources":["../../../src/inputs/_BottomSheet.tsx"],"names":[],"mappings":";AAiBA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QACrB,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAChC,mBAAmB,EAAE,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK;YACvE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,aAAa,EACb,KAAK,EACL,eAAe,EACf,OAAc,EACd,QAAQ,EACR,OAAO,EACP,UAAU,GACX,EAAE,gBAAgB,+BA+FlB"}
1
+ {"version":3,"file":"_BottomSheet.d.ts","sourceRoot":"","sources":["../../../src/inputs/_BottomSheet.tsx"],"names":[],"mappings":";AAkBA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QACrB,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAChC,mBAAmB,EAAE,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK;YACvE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,aAAa,EACb,KAAK,EACL,eAAe,EACf,OAAc,EACd,QAAQ,EACR,OAAO,EACP,UAAU,GACX,EAAE,gBAAgB,+BAmGlB"}
@@ -1 +1 @@
1
- {"version":3,"file":"_Popover.d.ts","sourceRoot":"","sources":["../../../src/inputs/_Popover.tsx"],"names":[],"mappings":";AAAA,OAAO,EAML,KAAK,SAAS,EAOf,MAAM,oBAAoB,CAAC;AAQ5B,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,EAAE,CAAC,IAAI,EAAE;QACpB,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAChC,mBAAmB,EAAE,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK;YACvE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAID,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,IAAI,EACJ,aAAa,EACb,KAAK,EACL,IAAW,EACX,OAAc,EACd,QAAQ,EACR,OAAO,EACP,UAAU,GACX,EAAE,YAAY,+BAoFd"}
1
+ {"version":3,"file":"_Popover.d.ts","sourceRoot":"","sources":["../../../src/inputs/_Popover.tsx"],"names":[],"mappings":";AAAA,OAAO,EAML,KAAK,SAAS,EAOf,MAAM,oBAAoB,CAAC;AAS5B,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,EAAE,CAAC,IAAI,EAAE;QACpB,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAChC,mBAAmB,EAAE,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK;YACvE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAID,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,IAAI,EACJ,aAAa,EACb,KAAK,EACL,IAAW,EACX,OAAc,EACd,QAAQ,EACR,OAAO,EACP,UAAU,GACX,EAAE,YAAY,+BAsFd"}
@@ -13,7 +13,7 @@ type SegmentWithControls = SegmentBase & {
13
13
  export type Segments = readonly Segment[] | readonly SegmentWithControls[];
14
14
  type SegmentedControlPropsBase = {
15
15
  name: string;
16
- defaultValue: string;
16
+ value: string;
17
17
  mode: 'input' | 'view';
18
18
  onChange: (value: string) => void;
19
19
  };
@@ -26,6 +26,6 @@ type SegmentedControlInputProps = {
26
26
  segments: readonly Segment[];
27
27
  };
28
28
  export type SegmentedControlProps = SegmentedControlPropsBase & (SegmentedControlViewProps | SegmentedControlInputProps);
29
- declare const SegmentedControl: ({ name, defaultValue, mode, segments, onChange, }: SegmentedControlProps) => import("react").JSX.Element;
29
+ declare const SegmentedControl: ({ name, value, mode, segments, onChange, }: SegmentedControlProps) => import("react").JSX.Element;
30
30
  export default SegmentedControl;
31
31
  //# sourceMappingURL=SegmentedControl.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../../src/segmentedControl/SegmentedControl.tsx"],"names":[],"mappings":";AAMA,KAAK,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,KAAK,OAAO,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAClD,KAAK,mBAAmB,GAAG,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,MAAM,MAAM,QAAQ,GAAG,SAAS,OAAO,EAAE,GAAG,SAAS,mBAAmB,EAAE,CAAC;AAE3E,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC1C,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,yBAAyB,GAC3D,CAAC,yBAAyB,GAAG,0BAA0B,CAAC,CAAC;AAE3D,QAAA,MAAM,gBAAgB,sDAMnB,qBAAqB,gCAiIvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../../src/segmentedControl/SegmentedControl.tsx"],"names":[],"mappings":";AAMA,KAAK,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhE,KAAK,OAAO,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAClD,KAAK,mBAAmB,GAAG,WAAW,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,MAAM,MAAM,QAAQ,GAAG,SAAS,OAAO,EAAE,GAAG,SAAS,mBAAmB,EAAE,CAAC;AAE3E,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC1C,CAAC;AAEF,KAAK,0BAA0B,GAAG;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,yBAAyB,GAC3D,CAAC,yBAAyB,GAAG,0BAA0B,CAAC,CAAC;AAE3D,QAAA,MAAM,gBAAgB,+CAMnB,qBAAqB,gCA0HvB,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "0.0.0-experimental-5bcac43",
3
+ "version": "0.0.0-experimental-b300585",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -67,7 +67,7 @@
67
67
  "@testing-library/react-hooks": "^8.0.0",
68
68
  "@testing-library/user-event": "^13.2.1",
69
69
  "@transferwise/icons": "^3.7.0",
70
- "@transferwise/neptune-tokens": "^8.9.0",
70
+ "@transferwise/neptune-tokens": "^8.8.1",
71
71
  "@tsconfig/recommended": "^1.0.2",
72
72
  "@types/babel__core": "^7.20.1",
73
73
  "@types/commonmark": "^0.27.9",
@@ -95,14 +95,14 @@
95
95
  "rollup": "^3.28.1",
96
96
  "storybook": "^7.6.17",
97
97
  "@transferwise/less-config": "3.1.0",
98
- "@transferwise/neptune-css": "0.0.0-experimental-5bcac43",
99
- "@wise/components-theming": "0.0.0-experimental-5bcac43"
98
+ "@transferwise/neptune-css": "14.9.6",
99
+ "@wise/components-theming": "1.1.0"
100
100
  },
101
101
  "peerDependencies": {
102
102
  "@transferwise/icons": "^3.7.0",
103
- "@transferwise/neptune-css": "0.0.0-experimental-5bcac43",
103
+ "@transferwise/neptune-css": "^14.9.6",
104
104
  "@wise/art": "^2.7.0",
105
- "@wise/components-theming": "0.0.0-experimental-5bcac43",
105
+ "@wise/components-theming": "^1.0.0",
106
106
  "react": ">=16.14",
107
107
  "react-dom": ">=16.14",
108
108
  "react-intl": "^5.10.0 || ^6"
@@ -7,6 +7,7 @@ import {
7
7
  useRole,
8
8
  } from '@floating-ui/react';
9
9
  import { Transition } from '@headlessui/react';
10
+ import { FocusScope } from '@react-aria/focus';
10
11
  import { ThemeProvider, useTheme } from '@wise/components-theming';
11
12
  import classNames from 'classnames';
12
13
  import { useState } from 'react';
@@ -97,37 +98,41 @@ export function BottomSheet({
97
98
  leave="np-bottom-sheet-v2-content--leave"
98
99
  leaveTo="np-bottom-sheet-v2-content--leave-to"
99
100
  >
100
- <FloatingFocusManager context={context} initialFocus={initialFocusRef}>
101
- <div
102
- key={floatingKey} // Force inner state invalidation on open
103
- ref={refs.setFloating}
104
- className="np-bottom-sheet-v2-content-inner-container"
105
- {...getFloatingProps()}
106
- >
107
- <div className="np-bottom-sheet-v2-header">
108
- <CloseButton
109
- size={Size.SMALL}
110
- onClick={() => {
111
- onClose?.();
112
- }}
113
- />
114
- </div>
101
+ <FocusScope>
102
+ <FloatingFocusManager context={context} initialFocus={initialFocusRef}>
115
103
  <div
116
- className={classNames(
117
- 'np-bottom-sheet-v2-content-inner',
118
- title && 'np-bottom-sheet-v2-content-inner--has-title',
119
- {
120
- 'np-bottom-sheet-v2-content-inner--padding-md': padding === 'md',
121
- },
122
- )}
104
+ key={floatingKey} // Force inner state invalidation on open
105
+ ref={refs.setFloating}
106
+ className="np-bottom-sheet-v2-content-inner-container"
107
+ {...getFloatingProps()}
123
108
  >
124
- {title ? (
125
- <h2 className="np-bottom-sheet-v2-title np-text-title-body">{title}</h2>
126
- ) : null}
127
- <div className="np-bottom-sheet-v2-body np-text-body-default">{children}</div>
109
+ <div className="np-bottom-sheet-v2-header">
110
+ <CloseButton
111
+ size={Size.SMALL}
112
+ onClick={() => {
113
+ onClose?.();
114
+ }}
115
+ />
116
+ </div>
117
+ <div
118
+ className={classNames(
119
+ 'np-bottom-sheet-v2-content-inner',
120
+ title && 'np-bottom-sheet-v2-content-inner--has-title',
121
+ {
122
+ 'np-bottom-sheet-v2-content-inner--padding-md': padding === 'md',
123
+ },
124
+ )}
125
+ >
126
+ {title ? (
127
+ <h2 className="np-bottom-sheet-v2-title np-text-title-body">{title}</h2>
128
+ ) : null}
129
+ <div className="np-bottom-sheet-v2-body np-text-body-default">
130
+ {children}
131
+ </div>
132
+ </div>
128
133
  </div>
129
- </div>
130
- </FloatingFocusManager>
134
+ </FloatingFocusManager>
135
+ </FocusScope>
131
136
  </Transition.Child>
132
137
  </div>
133
138
  </Transition>
@@ -13,6 +13,7 @@ import {
13
13
  useRole,
14
14
  } from '@floating-ui/react';
15
15
  import { Transition } from '@headlessui/react';
16
+ import { FocusScope } from '@react-aria/focus';
16
17
  import { ThemeProvider, useTheme } from '@wise/components-theming';
17
18
  import classNames from 'classnames';
18
19
  import { useState } from 'react';
@@ -103,30 +104,32 @@ export function Popover({
103
104
  }}
104
105
  afterLeave={onCloseEnd}
105
106
  >
106
- <FloatingFocusManager context={context}>
107
- <div
108
- key={floatingKey} // Force inner state invalidation on open
109
- ref={refs.setFloating}
110
- className={classNames('np-popover-v2-container', {
111
- 'np-popover-v2-container--size-md': size === 'md',
112
- 'np-popover-v2-container--size-lg': size === 'lg',
113
- })}
114
- // eslint-disable-next-line react/forbid-dom-props
115
- style={floatingStyles}
116
- {...getFloatingProps()}
117
- >
107
+ <FocusScope>
108
+ <FloatingFocusManager context={context}>
118
109
  <div
119
- className={classNames('np-popover-v2', title && 'np-popover-v2--has-title', {
120
- 'np-popover-v2--padding-md': padding === 'md',
110
+ key={floatingKey} // Force inner state invalidation on open
111
+ ref={refs.setFloating}
112
+ className={classNames('np-popover-v2-container', {
113
+ 'np-popover-v2-container--size-md': size === 'md',
114
+ 'np-popover-v2-container--size-lg': size === 'lg',
121
115
  })}
116
+ // eslint-disable-next-line react/forbid-dom-props
117
+ style={floatingStyles}
118
+ {...getFloatingProps()}
122
119
  >
123
- {title ? (
124
- <h2 className="np-popover-v2-title np-text-title-body">{title}</h2>
125
- ) : null}
126
- <div className="np-popover-v2-content np-text-body-default">{children}</div>
120
+ <div
121
+ className={classNames('np-popover-v2', title && 'np-popover-v2--has-title', {
122
+ 'np-popover-v2--padding-md': padding === 'md',
123
+ })}
124
+ >
125
+ {title ? (
126
+ <h2 className="np-popover-v2-title np-text-title-body">{title}</h2>
127
+ ) : null}
128
+ <div className="np-popover-v2-content np-text-body-default">{children}</div>
129
+ </div>
127
130
  </div>
128
- </div>
129
- </FloatingFocusManager>
131
+ </FloatingFocusManager>
132
+ </FocusScope>
130
133
  </Transition>
131
134
  </ThemeProvider>
132
135
  </FloatingPortal>
@@ -32,7 +32,7 @@ const onChange = jest.fn();
32
32
 
33
33
  const defaultProps: SegmentedControlProps = {
34
34
  name: 'segmentedControl',
35
- defaultValue: defaultSegments[0].value,
35
+ value: defaultSegments[0].value,
36
36
  mode: 'input',
37
37
  segments: defaultSegments,
38
38
  onChange,
@@ -134,6 +134,44 @@ describe('SegmentedControl', () => {
134
134
  });
135
135
  });
136
136
 
137
+ it('updates the selected segment when the selectedValue prop changes', () => {
138
+ const { rerender } = render(<SegmentedControl {...defaultProps} />);
139
+
140
+ const payroll = screen.getByRole('radio', { name: 'Payroll' });
141
+ userEvent.click(payroll);
142
+
143
+ expect(onChange).toHaveBeenCalledWith('payroll');
144
+
145
+ rerender(<SegmentedControl {...defaultProps} value="reporting" />);
146
+
147
+ const reporting = screen.getByRole('radio', { name: 'Reporting' });
148
+ expect(reporting).toBeChecked();
149
+ });
150
+
151
+ it('updates the options when the segments prop changes', () => {
152
+ const { rerender } = render(<SegmentedControl {...defaultProps} />);
153
+
154
+ const newSegments = [
155
+ {
156
+ id: '1',
157
+ value: 'payroll',
158
+ label: 'Payroll',
159
+ },
160
+ {
161
+ id: '3',
162
+ value: 'anotherOne',
163
+ label: 'Another One',
164
+ },
165
+ ];
166
+
167
+ rerender(<SegmentedControl {...defaultProps} segments={newSegments} />);
168
+
169
+ const anotherOne = screen.getByRole('radio', { name: 'Another One' });
170
+ userEvent.click(anotherOne);
171
+
172
+ expect(onChange).toHaveBeenCalledWith('anotherOne');
173
+ });
174
+
137
175
  it('throws error if user tries to add too many segments', () => {
138
176
  expect(() => {
139
177
  renderSegmentedControl({
@@ -1,40 +1,78 @@
1
1
  import { StoryFn } from '@storybook/react';
2
2
  import React from 'react';
3
3
 
4
- import SegmentedControl, { Segments } from './SegmentedControl';
4
+ import Button from '../button';
5
+
6
+ import SegmentedControl from './SegmentedControl';
5
7
 
6
8
  export default {
7
9
  component: SegmentedControl,
8
10
  title: 'Forms/SegmentedControl',
9
11
  };
10
12
 
11
- const segments: Segments = [
12
- { id: 'CUPCAKE', label: 'Cupcakes', value: 'cupcakes' },
13
- { id: 'SPONGECAKE', label: 'Sponge cake', value: 'spongecake' },
14
- { id: 'CARROT_CAKE', label: 'Carrot cake', value: 'carrotcake' },
15
- ];
13
+ const Template: StoryFn = (args) => {
14
+ const [segments, setSegments] = React.useState([
15
+ { id: 'CUPCAKE', label: 'Cupcakes', value: 'cupcakes' },
16
+ { id: 'SPONGECAKE', label: 'Sponge cake', value: 'spongecake' },
17
+ { id: 'CARROT_CAKE', label: 'Carrot cake', value: 'carrotcake' },
18
+ ]);
16
19
 
17
- const segmentsWithControls: Segments = [
18
- { id: 'CUPCAKE', label: 'Cupcakes', value: 'cupcakes', controls: 'aControlId' },
19
- { id: 'SPONGECAKE', label: 'Sponge cake', value: 'spongecake', controls: 'aControlId' },
20
- { id: 'CARROT_CAKE', label: 'Carrot cake', value: 'carrotcake', controls: 'aControlId' },
21
- ];
20
+ const [segmentsWithControls, setSegmentsWithControls] = React.useState([
21
+ { id: 'CUPCAKE', label: 'Cupcakes', value: 'cupcakes', controls: 'aControlId' },
22
+ { id: 'SPONGECAKE', label: 'Sponge cake', value: 'spongecake', controls: 'aControlId' },
23
+ { id: 'CARROT_CAKE', label: 'Carrot cake', value: 'carrotcake', controls: 'aControlId' },
24
+ ]);
22
25
 
23
- const Template: StoryFn = (args) => {
24
- const [selectedValue, setSelectedValue] = React.useState(segments[0].value);
26
+ const [value, setValue] = React.useState(segments[0].value);
25
27
 
28
+ console.log('render: segments.length', segments.length);
26
29
  return (
27
30
  <div className="p-a-2">
28
31
  <SegmentedControl
29
32
  name="aSegmentedControl"
30
- defaultValue={selectedValue}
31
- onChange={setSelectedValue}
33
+ value={value}
34
+ onChange={setValue}
32
35
  {...(args.mode === 'view'
33
36
  ? { segments: segmentsWithControls, mode: 'view', controls: 'aControlId' }
34
37
  : { segments, mode: 'input' })}
35
38
  />
36
39
  <div className="m-a-2" id="aControlId">
37
- <p>Selected value: {selectedValue}</p>
40
+ <p>Selected value: {value}</p>
41
+ </div>
42
+ <div className="m-a-2">
43
+ <p>
44
+ Force the <b>selectedValue</b> to be one of the following:
45
+ <ul>
46
+ {segments.map((segment) => (
47
+ <li key={segment.id}>
48
+ <a
49
+ href="/"
50
+ onClick={(e) => {
51
+ e.preventDefault();
52
+ setValue(segment.value);
53
+ }}
54
+ >
55
+ {segment.label}
56
+ </a>
57
+ </li>
58
+ ))}
59
+ </ul>
60
+ </p>
61
+ </div>
62
+ <div className="m-a-2">
63
+ <Button
64
+ priority="secondary"
65
+ type="danger"
66
+ size="sm"
67
+ disabled={segments.length < 2}
68
+ onClick={() => {
69
+ const index = segments.findIndex((s) => s.value !== value);
70
+ setSegments((prev) => prev.filter((_, i) => i !== index));
71
+ setSegmentsWithControls((prev) => prev.filter((_, i) => i !== index));
72
+ }}
73
+ >
74
+ Remove one segment
75
+ </Button>
38
76
  </div>
39
77
  </div>
40
78
  );
@@ -13,7 +13,7 @@ export type Segments = readonly Segment[] | readonly SegmentWithControls[];
13
13
 
14
14
  type SegmentedControlPropsBase = {
15
15
  name: string;
16
- defaultValue: string;
16
+ value: string;
17
17
  mode: 'input' | 'view';
18
18
  onChange: (value: string) => void;
19
19
  };
@@ -33,12 +33,11 @@ export type SegmentedControlProps = SegmentedControlPropsBase &
33
33
 
34
34
  const SegmentedControl = ({
35
35
  name,
36
- defaultValue,
36
+ value,
37
37
  mode = 'input',
38
38
  segments,
39
39
  onChange,
40
40
  }: SegmentedControlProps) => {
41
- const [selectedValue, setSelectedValue] = useState(defaultValue || segments[0].value);
42
41
  const [animate, setAnimate] = useState(false);
43
42
 
44
43
  const segmentsRef = useRef<HTMLDivElement>(null);
@@ -55,9 +54,7 @@ const SegmentedControl = ({
55
54
  }));
56
55
 
57
56
  const updateSegmentPosition = () => {
58
- const selectedSegmentRef = segmentsWithRefs.find(
59
- (segment) => segment.value === selectedValue,
60
- )?.ref;
57
+ const selectedSegmentRef = segmentsWithRefs.find((segment) => segment.value === value)?.ref;
61
58
 
62
59
  // We grab the active segments style object from the ref
63
60
  // and set the css variables to the selected segments width and x position.
@@ -70,6 +67,7 @@ const SegmentedControl = ({
70
67
  };
71
68
 
72
69
  useEffect(() => {
70
+ setAnimate(true);
73
71
  updateSegmentPosition();
74
72
 
75
73
  const handleWindowSizeChange = () => {
@@ -83,7 +81,7 @@ const SegmentedControl = ({
83
81
  };
84
82
 
85
83
  // eslint-disable-next-line react-hooks/exhaustive-deps
86
- }, [segmentsWithRefs, selectedValue]);
84
+ }, [segmentsWithRefs, value]);
87
85
 
88
86
  return (
89
87
  <div
@@ -102,7 +100,6 @@ const SegmentedControl = ({
102
100
  {segmentsWithRefs.map((segment) => {
103
101
  const onSelect = () => {
104
102
  setAnimate(true);
105
- setSelectedValue(segment.value);
106
103
  onChange(segment.value);
107
104
  };
108
105
  return mode === 'input' ? (
@@ -111,7 +108,7 @@ const SegmentedControl = ({
111
108
  key={segment.id}
112
109
  htmlFor={segment.id}
113
110
  className={classNames('segmented-control__segment', {
114
- 'segmented-control__selected-segment': selectedValue === segment.value,
111
+ 'segmented-control__selected-segment': value === segment.value,
115
112
  })}
116
113
  >
117
114
  <input
@@ -120,16 +117,14 @@ const SegmentedControl = ({
120
117
  id={segment.id}
121
118
  name={name}
122
119
  value={segment.value}
123
- checked={selectedValue === segment.value}
120
+ checked={value === segment.value}
124
121
  onChange={onSelect}
125
122
  />
126
123
  <Body
127
124
  className="segmented-control__text"
128
125
  as="span"
129
126
  type={
130
- selectedValue === segment.value
131
- ? Typography.BODY_DEFAULT_BOLD
132
- : Typography.BODY_DEFAULT
127
+ value === segment.value ? Typography.BODY_DEFAULT_BOLD : Typography.BODY_DEFAULT
133
128
  }
134
129
  >
135
130
  {segment.label}
@@ -143,9 +138,9 @@ const SegmentedControl = ({
143
138
  role="tab"
144
139
  id={segment.id}
145
140
  aria-controls={segment.controls}
146
- aria-selected={selectedValue === segment.value}
141
+ aria-selected={value === segment.value}
147
142
  className={classNames('segmented-control__segment', 'segmented-control__button', {
148
- 'segmented-control__selected-segment': selectedValue === segment.value,
143
+ 'segmented-control__selected-segment': value === segment.value,
149
144
  })}
150
145
  onClick={onSelect}
151
146
  >
@@ -153,9 +148,7 @@ const SegmentedControl = ({
153
148
  as="span"
154
149
  className="segmented-control__text"
155
150
  type={
156
- selectedValue === segment.value
157
- ? Typography.BODY_DEFAULT_BOLD
158
- : Typography.BODY_DEFAULT
151
+ value === segment.value ? Typography.BODY_DEFAULT_BOLD : Typography.BODY_DEFAULT
159
152
  }
160
153
  >
161
154
  {segment.label}
@@ -1,21 +0,0 @@
1
- import { Meta, StoryObj } from '@storybook/react';
2
- import { ThemeProvider } from '@wise/components-theming';
3
-
4
- import Button from '../../button';
5
-
6
- export default {
7
- component: ThemeProvider,
8
- title: 'Other/ThemeProvider',
9
- } satisfies Meta<typeof ThemeProvider>;
10
-
11
- type Story = StoryObj<typeof ThemeProvider>;
12
-
13
- export const PlatformTheme: Story = {
14
- render: () => (
15
- <>
16
- <ThemeProvider theme="platform">
17
- <Button>Button</Button>
18
- </ThemeProvider>
19
- </>
20
- ),
21
- };