@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.
- package/build/index.js +69 -66
- package/build/index.js.map +1 -1
- package/build/index.mjs +69 -66
- package/build/index.mjs.map +1 -1
- package/build/types/inputs/_BottomSheet.d.ts.map +1 -1
- package/build/types/inputs/_Popover.d.ts.map +1 -1
- package/build/types/segmentedControl/SegmentedControl.d.ts +2 -2
- package/build/types/segmentedControl/SegmentedControl.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/inputs/_BottomSheet.tsx +33 -28
- package/src/inputs/_Popover.tsx +23 -20
- package/src/segmentedControl/SegmentedControl.spec.tsx +39 -1
- package/src/segmentedControl/SegmentedControl.story.tsx +54 -16
- package/src/segmentedControl/SegmentedControl.tsx +11 -18
- package/src/provider/theme/ThemeProvider.story.tsx +0 -21
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_BottomSheet.d.ts","sourceRoot":"","sources":["../../../src/inputs/_BottomSheet.tsx"],"names":[],"mappings":";
|
|
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;
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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-
|
|
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.
|
|
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": "
|
|
99
|
-
"@wise/components-theming": "
|
|
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": "
|
|
103
|
+
"@transferwise/neptune-css": "^14.9.6",
|
|
104
104
|
"@wise/art": "^2.7.0",
|
|
105
|
-
"@wise/components-theming": "
|
|
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
|
-
<
|
|
101
|
-
<
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
</
|
|
130
|
-
</
|
|
134
|
+
</FloatingFocusManager>
|
|
135
|
+
</FocusScope>
|
|
131
136
|
</Transition.Child>
|
|
132
137
|
</div>
|
|
133
138
|
</Transition>
|
package/src/inputs/_Popover.tsx
CHANGED
|
@@ -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
|
-
<
|
|
107
|
-
<
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
</
|
|
129
|
-
</
|
|
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
|
-
|
|
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
|
|
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
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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
|
-
|
|
31
|
-
onChange={
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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':
|
|
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={
|
|
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
|
-
|
|
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={
|
|
141
|
+
aria-selected={value === segment.value}
|
|
147
142
|
className={classNames('segmented-control__segment', 'segmented-control__button', {
|
|
148
|
-
'segmented-control__selected-segment':
|
|
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
|
-
|
|
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
|
-
};
|