@transferwise/components 0.0.0-experimental-9462b09 → 0.0.0-experimental-2645631
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/i18n/en.json +0 -2
- package/build/index.js +2340 -2433
- package/build/index.js.map +1 -1
- package/build/index.mjs +2343 -2435
- package/build/index.mjs.map +1 -1
- package/build/main.css +4 -49
- package/build/styles/main.css +4 -49
- package/build/styles/segmentedControl/SegmentedControl.css +4 -2
- package/build/types/checkboxButton/CheckboxButton.d.ts.map +1 -1
- package/build/types/common/responsivePanel/ResponsivePanel.d.ts.map +1 -1
- package/build/types/field/Field.d.ts +2 -1
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/index.d.ts +0 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputs/contexts.d.ts.map +1 -1
- package/build/types/segmentedControl/SegmentedControl.d.ts.map +1 -1
- package/build/types/tabs/TabList.d.ts +3 -8
- package/build/types/tabs/TabList.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/checkbox/Checkbox.spec.tsx +13 -0
- package/src/checkbox/Checkbox.story.tsx +13 -0
- package/src/checkboxButton/CheckboxButton.tsx +12 -4
- package/src/common/responsivePanel/ResponsivePanel.tsx +0 -2
- package/src/field/Field.tsx +9 -5
- package/src/i18n/en.json +0 -2
- package/src/index.ts +0 -2
- package/src/inputs/contexts.tsx +2 -1
- package/src/main.css +4 -49
- package/src/main.less +0 -1
- package/src/segmentedControl/SegmentedControl.css +4 -2
- package/src/segmentedControl/SegmentedControl.less +4 -1
- package/src/segmentedControl/SegmentedControl.spec.tsx +10 -0
- package/src/segmentedControl/SegmentedControl.story.tsx +42 -4
- package/src/segmentedControl/SegmentedControl.tsx +5 -1
- package/src/ssr.spec.js +0 -1
- package/src/tabs/TabList.tsx +15 -0
- package/src/tabs/Tabs.rtl.spec.tsx +40 -0
- package/src/tabs/Tabs.story.tsx +88 -0
- package/build/styles/selectOption/SelectOption.css +0 -47
- package/build/types/selectOption/SelectOption.d.ts +0 -21
- package/build/types/selectOption/SelectOption.d.ts.map +0 -1
- package/build/types/selectOption/SelectOption.messages.d.ts +0 -12
- package/build/types/selectOption/SelectOption.messages.d.ts.map +0 -1
- package/build/types/selectOption/index.d.ts +0 -3
- package/build/types/selectOption/index.d.ts.map +0 -1
- package/src/selectOption/SelectOption.css +0 -47
- package/src/selectOption/SelectOption.less +0 -45
- package/src/selectOption/SelectOption.messages.ts +0 -12
- package/src/selectOption/SelectOption.spec.tsx +0 -89
- package/src/selectOption/SelectOption.story.tsx +0 -269
- package/src/selectOption/SelectOption.tsx +0 -151
- package/src/selectOption/index.ts +0 -2
- package/src/tabs/TabList.js +0 -15
- package/src/tabs/Tabs.story.js +0 -135
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import { StoryFn } from '@storybook/react';
|
|
2
|
-
import React from 'react';
|
|
1
|
+
import { Meta, StoryFn, StoryObj } from '@storybook/react';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
3
|
|
|
4
4
|
import Button from '../button';
|
|
5
5
|
|
|
6
6
|
import SegmentedControl from './SegmentedControl';
|
|
7
|
+
import { Field } from '../field/Field';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
const meta = {
|
|
9
10
|
component: SegmentedControl,
|
|
10
11
|
title: 'Forms/SegmentedControl',
|
|
11
|
-
}
|
|
12
|
+
} satisfies Meta<typeof SegmentedControl>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
12
16
|
|
|
13
17
|
const Template: StoryFn = (args) => {
|
|
14
18
|
const [segments, setSegments] = React.useState([
|
|
@@ -91,3 +95,37 @@ export const SegmentedControlView = {
|
|
|
91
95
|
mode: 'view',
|
|
92
96
|
},
|
|
93
97
|
};
|
|
98
|
+
|
|
99
|
+
export const WithinField = {
|
|
100
|
+
args: {
|
|
101
|
+
name: 'aSegmentedControl',
|
|
102
|
+
mode: 'input',
|
|
103
|
+
value: 'cupcakes',
|
|
104
|
+
segments: [
|
|
105
|
+
{ id: 'CUPCAKE', label: 'Cupcakes', value: 'cupcakes' },
|
|
106
|
+
{ id: 'SPONGECAKE', label: 'Sponge cake', value: 'spongecake' },
|
|
107
|
+
{ id: 'CARROT_CAKE', label: 'Carrot cake', value: 'carrotcake' },
|
|
108
|
+
],
|
|
109
|
+
onChange: () => {},
|
|
110
|
+
},
|
|
111
|
+
render: function Render({ value: initialValue, onChange, ...args }) {
|
|
112
|
+
const [value, setValue] = useState(initialValue);
|
|
113
|
+
return (
|
|
114
|
+
<SegmentedControl
|
|
115
|
+
value={value}
|
|
116
|
+
onChange={(nextValue) => {
|
|
117
|
+
setValue(nextValue);
|
|
118
|
+
onChange?.(nextValue);
|
|
119
|
+
}}
|
|
120
|
+
{...args}
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
decorators: [
|
|
125
|
+
(Story) => (
|
|
126
|
+
<Field label="Label">
|
|
127
|
+
<Story />
|
|
128
|
+
</Field>
|
|
129
|
+
),
|
|
130
|
+
],
|
|
131
|
+
} satisfies Story;
|
|
@@ -3,6 +3,7 @@ import { createRef, useEffect, useRef, useState } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import Body from '../body';
|
|
5
5
|
import { Typography } from '../common';
|
|
6
|
+
import { useInputAttributes } from '../inputs/contexts';
|
|
6
7
|
|
|
7
8
|
type SegmentBase = { id: string; label: string; value: string };
|
|
8
9
|
|
|
@@ -38,6 +39,8 @@ const SegmentedControl = ({
|
|
|
38
39
|
segments,
|
|
39
40
|
onChange,
|
|
40
41
|
}: SegmentedControlProps) => {
|
|
42
|
+
const inputAttributes = useInputAttributes({ nonLabelable: true });
|
|
43
|
+
|
|
41
44
|
const [animate, setAnimate] = useState(false);
|
|
42
45
|
|
|
43
46
|
const segmentsRef = useRef<HTMLDivElement>(null);
|
|
@@ -95,7 +98,8 @@ const SegmentedControl = ({
|
|
|
95
98
|
className={classNames('segmented-control__segments', {
|
|
96
99
|
'segmented-control__segments--no-animate': !animate,
|
|
97
100
|
})}
|
|
98
|
-
role={mode !== 'input' ? 'tablist' :
|
|
101
|
+
role={mode !== 'input' ? 'tablist' : 'group'}
|
|
102
|
+
{...inputAttributes}
|
|
99
103
|
>
|
|
100
104
|
{segmentsWithRefs.map((segment) => {
|
|
101
105
|
const onSelect = () => {
|
package/src/ssr.spec.js
CHANGED
|
@@ -226,7 +226,6 @@ describe('Server side rendering', () => {
|
|
|
226
226
|
DateLookup: { value: new Date() },
|
|
227
227
|
Link: { size: 16 },
|
|
228
228
|
Tooltip: { children: <>yo</> },
|
|
229
|
-
SelectOption: { placeholder: { media: <img alt="img" /> } },
|
|
230
229
|
};
|
|
231
230
|
|
|
232
231
|
const { Provider } = exposedLibraryItems;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useInputAttributes } from '../inputs/contexts';
|
|
2
|
+
|
|
3
|
+
export interface TabListProps {
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default function TabList({ children }: TabListProps) {
|
|
8
|
+
const inputAttributes = useInputAttributes({ nonLabelable: true });
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<ul className="tabs__tab-list" role="tablist" {...inputAttributes}>
|
|
12
|
+
{children}
|
|
13
|
+
</ul>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Field } from '../field/Field';
|
|
2
|
+
import { render, screen } from '../test-utils';
|
|
3
|
+
|
|
4
|
+
import Tabs from '.';
|
|
5
|
+
|
|
6
|
+
describe('Tabs', () => {
|
|
7
|
+
it('supports `Field` for labeling', () => {
|
|
8
|
+
render(
|
|
9
|
+
<Field label="Label">
|
|
10
|
+
<Tabs
|
|
11
|
+
tabs={[
|
|
12
|
+
{
|
|
13
|
+
title: 'Title 1',
|
|
14
|
+
disabled: false,
|
|
15
|
+
content: (
|
|
16
|
+
<div className="p-a-2">
|
|
17
|
+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum totam debitis
|
|
18
|
+
similique
|
|
19
|
+
</div>
|
|
20
|
+
),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
title: 'Title 2',
|
|
24
|
+
disabled: false,
|
|
25
|
+
content: (
|
|
26
|
+
<div className="p-a-2">
|
|
27
|
+
Lorem ipsum dolor, sit amet consectetur adipisicing elit.
|
|
28
|
+
</div>
|
|
29
|
+
),
|
|
30
|
+
},
|
|
31
|
+
]}
|
|
32
|
+
name="tabs-test"
|
|
33
|
+
selected={0}
|
|
34
|
+
onTabSelect={() => {}}
|
|
35
|
+
/>
|
|
36
|
+
</Field>,
|
|
37
|
+
);
|
|
38
|
+
expect(screen.getByRole('tablist')).toHaveAccessibleName(/^Label/);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Size, Width } from '../common';
|
|
5
|
+
import { Field } from '../field/Field';
|
|
6
|
+
|
|
7
|
+
import Tabs from './Tabs';
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
component: Tabs,
|
|
11
|
+
title: 'Navigation/Tabs',
|
|
12
|
+
} satisfies Meta<typeof Tabs>;
|
|
13
|
+
|
|
14
|
+
export default meta;
|
|
15
|
+
type Story = StoryObj<typeof meta>;
|
|
16
|
+
|
|
17
|
+
export const Basic = {
|
|
18
|
+
args: {
|
|
19
|
+
tabs: [
|
|
20
|
+
{
|
|
21
|
+
title: 'Title 1',
|
|
22
|
+
disabled: false,
|
|
23
|
+
content: (
|
|
24
|
+
<div className="p-a-2">
|
|
25
|
+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum totam debitis similique
|
|
26
|
+
</div>
|
|
27
|
+
),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
title: 'Title 2',
|
|
31
|
+
disabled: false,
|
|
32
|
+
content: (
|
|
33
|
+
<div className="p-a-2">Lorem ipsum dolor, sit amet consectetur adipisicing elit.</div>
|
|
34
|
+
),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
title: 'Title 3',
|
|
38
|
+
disabled: true,
|
|
39
|
+
content: (
|
|
40
|
+
<div className="p-a-2">
|
|
41
|
+
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum totam debitis similique
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
render: function Render(args) {
|
|
48
|
+
const [selected, setSelected] = useState(0);
|
|
49
|
+
return (
|
|
50
|
+
<Tabs
|
|
51
|
+
className="tabs-custom-class"
|
|
52
|
+
name="tabs-docs"
|
|
53
|
+
transitionSpacing={Size.MEDIUM}
|
|
54
|
+
selected={selected}
|
|
55
|
+
animatePanelsOnClick
|
|
56
|
+
onTabSelect={(index: number) => setSelected(index)}
|
|
57
|
+
{...args}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
} satisfies Story;
|
|
62
|
+
|
|
63
|
+
export const AutoTabHeaderWidth = {
|
|
64
|
+
...Basic,
|
|
65
|
+
args: {
|
|
66
|
+
...Basic.args,
|
|
67
|
+
headerWidth: Width.AUTO,
|
|
68
|
+
},
|
|
69
|
+
} satisfies Story;
|
|
70
|
+
|
|
71
|
+
export const NoPanelAnimation = {
|
|
72
|
+
...Basic,
|
|
73
|
+
args: {
|
|
74
|
+
...Basic.args,
|
|
75
|
+
animatePanelsOnClick: false,
|
|
76
|
+
},
|
|
77
|
+
} satisfies Story;
|
|
78
|
+
|
|
79
|
+
export const WithinField = {
|
|
80
|
+
...Basic,
|
|
81
|
+
decorators: [
|
|
82
|
+
(Story) => (
|
|
83
|
+
<Field label="Label">
|
|
84
|
+
<Story />
|
|
85
|
+
</Field>
|
|
86
|
+
),
|
|
87
|
+
],
|
|
88
|
+
} satisfies Story;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
.np-select-option {
|
|
2
|
-
border-radius: 10px;
|
|
3
|
-
border-radius: var(--radius-small);
|
|
4
|
-
padding: 16px;
|
|
5
|
-
padding: var(--size-16);
|
|
6
|
-
}
|
|
7
|
-
.np-select-option-placeholder {
|
|
8
|
-
background-color: rgba(134,167,189,0.10196);
|
|
9
|
-
background-color: var(--color-background-neutral);
|
|
10
|
-
}
|
|
11
|
-
.np-select-option-placeholder:not(.disabled):hover {
|
|
12
|
-
background-color: var(--color-background-neutral-hover);
|
|
13
|
-
}
|
|
14
|
-
.np-select-option-placeholder:not(.disabled):focus,
|
|
15
|
-
.np-select-option-placeholder:not(.disabled):active {
|
|
16
|
-
background-color: var(--color-background-neutral-active);
|
|
17
|
-
}
|
|
18
|
-
.np-select-option-selected {
|
|
19
|
-
border: 1px solid #c9cbce;
|
|
20
|
-
border: 1px solid var(--color-interactive-secondary);
|
|
21
|
-
}
|
|
22
|
-
.np-select-option-list {
|
|
23
|
-
max-height: 350px;
|
|
24
|
-
overflow-y: auto;
|
|
25
|
-
}
|
|
26
|
-
.np-select-option-list > .np-section {
|
|
27
|
-
margin-top: 0;
|
|
28
|
-
}
|
|
29
|
-
.form-group label > .np-select-option {
|
|
30
|
-
margin-bottom: 0;
|
|
31
|
-
}
|
|
32
|
-
.has-error * .np-select-option {
|
|
33
|
-
--ring-outline-color: var(--color-sentiment-negative);
|
|
34
|
-
--ring-outline-width: 3px;
|
|
35
|
-
--ring-outline-offset: calc(-1 * var(--ring-outline-width));
|
|
36
|
-
outline: var(--ring-outline-color) solid 3px;
|
|
37
|
-
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
38
|
-
outline-offset: calc(-1 * 3px);
|
|
39
|
-
outline-offset: var(--ring-outline-offset);
|
|
40
|
-
}
|
|
41
|
-
.has-error * .np-select-option:focus {
|
|
42
|
-
outline: none;
|
|
43
|
-
}
|
|
44
|
-
.has-error * .np-select-option:focus-visible {
|
|
45
|
-
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
46
|
-
outline-offset: var(--ring-outline-offset);
|
|
47
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { ActionButtonProps } from '../actionButton';
|
|
2
|
-
import type { OptionProps } from '../common/Option/Option';
|
|
3
|
-
import { HeaderProps } from '../header/Header';
|
|
4
|
-
export type SelectOptiopsSection<T = unknown> = {
|
|
5
|
-
title?: HeaderProps['title'];
|
|
6
|
-
options: SelectOptionValue<T>[];
|
|
7
|
-
};
|
|
8
|
-
export type SelectOptionValue<T = unknown> = Pick<OptionProps, 'media' | 'title' | 'content' | 'disabled'> & {
|
|
9
|
-
value?: T;
|
|
10
|
-
};
|
|
11
|
-
export type SelectOptionPlaceholder = Pick<OptionProps, 'media' | 'title' | 'content'> & {
|
|
12
|
-
actionLabel?: ActionButtonProps['children'];
|
|
13
|
-
};
|
|
14
|
-
export type SelectOptionProps<T = unknown> = {
|
|
15
|
-
onChange: (selected: SelectOptionValue<T>) => void;
|
|
16
|
-
selected?: SelectOptionValue<T>;
|
|
17
|
-
options: SelectOptiopsSection<T>[];
|
|
18
|
-
placeholder: SelectOptionPlaceholder;
|
|
19
|
-
} & Omit<OptionProps, 'as' | 'title' | 'media' | 'content' | 'onClick' | 'onChange' | 'showMediaAtAllSizes' | 'decision'>;
|
|
20
|
-
export default function SelectOption<T>({ selected: selectedValueProp, options, onChange, placeholder, disabled, ...props }: SelectOptionProps<T>): import("react").JSX.Element;
|
|
21
|
-
//# sourceMappingURL=SelectOption.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SelectOption.d.ts","sourceRoot":"","sources":["../../../src/selectOption/SelectOption.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAI3D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAU/C,MAAM,MAAM,oBAAoB,CAAC,CAAC,GAAG,OAAO,IAAI;IAC9C,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,CAC/C,WAAW,EACX,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAC3C,GAAG;IACF,KAAK,CAAC,EAAE,CAAC,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC,GAAG;IACvF,WAAW,CAAC,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,OAAO,IAAI;IAC3C,QAAQ,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACnD,QAAQ,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;IACnC,WAAW,EAAE,uBAAuB,CAAC;CACtC,GAAG,IAAI,CACN,WAAW,EACX,IAAI,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,qBAAqB,GAAG,UAAU,CACnG,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,CAAC,EAAE,EACtC,QAAQ,EAAE,iBAA6B,EACvC,OAAO,EACP,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,iBAAiB,CAAC,CAAC,CAAC,+BAmGtB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SelectOption.messages.d.ts","sourceRoot":"","sources":["../../../src/selectOption/SelectOption.messages.ts"],"names":[],"mappings":";;;;;;;;;;AAEA,wBASG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/selectOption/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
.np-select-option {
|
|
2
|
-
border-radius: 10px;
|
|
3
|
-
border-radius: var(--radius-small);
|
|
4
|
-
padding: 16px;
|
|
5
|
-
padding: var(--size-16);
|
|
6
|
-
}
|
|
7
|
-
.np-select-option-placeholder {
|
|
8
|
-
background-color: rgba(134,167,189,0.10196);
|
|
9
|
-
background-color: var(--color-background-neutral);
|
|
10
|
-
}
|
|
11
|
-
.np-select-option-placeholder:not(.disabled):hover {
|
|
12
|
-
background-color: var(--color-background-neutral-hover);
|
|
13
|
-
}
|
|
14
|
-
.np-select-option-placeholder:not(.disabled):focus,
|
|
15
|
-
.np-select-option-placeholder:not(.disabled):active {
|
|
16
|
-
background-color: var(--color-background-neutral-active);
|
|
17
|
-
}
|
|
18
|
-
.np-select-option-selected {
|
|
19
|
-
border: 1px solid #c9cbce;
|
|
20
|
-
border: 1px solid var(--color-interactive-secondary);
|
|
21
|
-
}
|
|
22
|
-
.np-select-option-list {
|
|
23
|
-
max-height: 350px;
|
|
24
|
-
overflow-y: auto;
|
|
25
|
-
}
|
|
26
|
-
.np-select-option-list > .np-section {
|
|
27
|
-
margin-top: 0;
|
|
28
|
-
}
|
|
29
|
-
.form-group label > .np-select-option {
|
|
30
|
-
margin-bottom: 0;
|
|
31
|
-
}
|
|
32
|
-
.has-error * .np-select-option {
|
|
33
|
-
--ring-outline-color: var(--color-sentiment-negative);
|
|
34
|
-
--ring-outline-width: 3px;
|
|
35
|
-
--ring-outline-offset: calc(-1 * var(--ring-outline-width));
|
|
36
|
-
outline: var(--ring-outline-color) solid 3px;
|
|
37
|
-
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
38
|
-
outline-offset: calc(-1 * 3px);
|
|
39
|
-
outline-offset: var(--ring-outline-offset);
|
|
40
|
-
}
|
|
41
|
-
.has-error * .np-select-option:focus {
|
|
42
|
-
outline: none;
|
|
43
|
-
}
|
|
44
|
-
.has-error * .np-select-option:focus-visible {
|
|
45
|
-
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
46
|
-
outline-offset: var(--ring-outline-offset);
|
|
47
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
@import (reference) "../../node_modules/@transferwise/neptune-css/src/less/ring.less";
|
|
2
|
-
|
|
3
|
-
.np-select-option {
|
|
4
|
-
border-radius: var(--radius-small);
|
|
5
|
-
padding: var(--size-16);
|
|
6
|
-
|
|
7
|
-
&-placeholder {
|
|
8
|
-
background-color: var(--color-background-neutral);
|
|
9
|
-
|
|
10
|
-
&:not(.disabled):hover {
|
|
11
|
-
background-color: var(--color-background-neutral-hover);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
&:not(.disabled):focus,
|
|
15
|
-
&:not(.disabled):active {
|
|
16
|
-
background-color: var(--color-background-neutral-active);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
&-selected {
|
|
21
|
-
border: 1px solid var(--color-interactive-secondary);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
&-list {
|
|
25
|
-
max-height: 350px;
|
|
26
|
-
overflow-y: auto;
|
|
27
|
-
|
|
28
|
-
& > .np-section {
|
|
29
|
-
// remove large margin in Section & Header
|
|
30
|
-
margin-top: 0;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.form-group label > & {
|
|
35
|
-
// remove margin-bottom for label
|
|
36
|
-
margin-bottom: 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.has-error * & {
|
|
40
|
-
.ring-negative();
|
|
41
|
-
.ring-inset();
|
|
42
|
-
.ring();
|
|
43
|
-
.focus-ring();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineMessages } from 'react-intl';
|
|
2
|
-
|
|
3
|
-
export default defineMessages({
|
|
4
|
-
actionLabel: {
|
|
5
|
-
id: 'neptune.SelectOption.action.label',
|
|
6
|
-
defaultMessage: 'Choose',
|
|
7
|
-
},
|
|
8
|
-
selectedActionLabel: {
|
|
9
|
-
id: 'neptune.SelectOption.selected.action.label',
|
|
10
|
-
defaultMessage: 'Change chosen option',
|
|
11
|
-
},
|
|
12
|
-
});
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { render, userEvent, screen, within, mockMatchMedia } from '../test-utils';
|
|
2
|
-
import { composeStories } from '@storybook/react';
|
|
3
|
-
import * as stories from './SelectOption.story';
|
|
4
|
-
import { Breakpoint } from '../common';
|
|
5
|
-
import { wait } from '../test-utils/wait';
|
|
6
|
-
|
|
7
|
-
const { Basic: SelectOptionExample } = composeStories(stories);
|
|
8
|
-
|
|
9
|
-
mockMatchMedia();
|
|
10
|
-
|
|
11
|
-
describe('Select Option', () => {
|
|
12
|
-
it('opens dropdown on desktop view', async () => {
|
|
13
|
-
render(<SelectOptionExample />);
|
|
14
|
-
|
|
15
|
-
let dialog = screen.queryByRole('dialog');
|
|
16
|
-
expect(dialog).not.toBeInTheDocument();
|
|
17
|
-
|
|
18
|
-
const button = screen.getByRole('button');
|
|
19
|
-
|
|
20
|
-
await userEvent.click(button);
|
|
21
|
-
|
|
22
|
-
dialog = screen.queryByRole('dialog');
|
|
23
|
-
expect(dialog).toBeInTheDocument();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('opens bottom sheet on mobile view', async () => {
|
|
27
|
-
// mock mobile view
|
|
28
|
-
window.innerWidth = Breakpoint.EXTRA_SMALL;
|
|
29
|
-
|
|
30
|
-
render(<SelectOptionExample />);
|
|
31
|
-
|
|
32
|
-
let dialog = screen.queryByRole('dialog');
|
|
33
|
-
expect(dialog).not.toBeInTheDocument();
|
|
34
|
-
|
|
35
|
-
const button = screen.getByRole('button');
|
|
36
|
-
|
|
37
|
-
await userEvent.click(button);
|
|
38
|
-
|
|
39
|
-
dialog = screen.queryByRole('dialog');
|
|
40
|
-
expect(dialog).toBeInTheDocument();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('shows options', async () => {
|
|
44
|
-
render(<SelectOptionExample />);
|
|
45
|
-
|
|
46
|
-
const button = screen.getByRole('button');
|
|
47
|
-
|
|
48
|
-
await userEvent.click(button);
|
|
49
|
-
|
|
50
|
-
const dialog = screen.getByRole('dialog');
|
|
51
|
-
const sectionHeaders = within(dialog).getAllByRole('heading', { name: /balances|jars/i });
|
|
52
|
-
expect(sectionHeaders).toHaveLength(2);
|
|
53
|
-
|
|
54
|
-
const options = within(dialog).getAllByRole('heading', { level: 4 });
|
|
55
|
-
expect(options).toHaveLength(5);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('choose option', async () => {
|
|
59
|
-
// mock mobile view
|
|
60
|
-
window.innerWidth = Breakpoint.EXTRA_SMALL;
|
|
61
|
-
|
|
62
|
-
const onClickHandler = jest.fn();
|
|
63
|
-
render(
|
|
64
|
-
<SelectOptionExample
|
|
65
|
-
placeholder={{ title: 'Please choose balance', actionLabel: 'Pick' }}
|
|
66
|
-
onChange={onClickHandler}
|
|
67
|
-
/>,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
const button = screen.getByRole('button');
|
|
71
|
-
|
|
72
|
-
expect(button).toHaveTextContent('Pick');
|
|
73
|
-
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
74
|
-
await userEvent.click(button);
|
|
75
|
-
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
76
|
-
|
|
77
|
-
const pickedOption = within(screen.getByRole('dialog')).getByText('Trip to Mars');
|
|
78
|
-
|
|
79
|
-
expect(onClickHandler).not.toHaveBeenCalled();
|
|
80
|
-
await userEvent.click(pickedOption);
|
|
81
|
-
expect(onClickHandler).toHaveBeenCalledTimes(1);
|
|
82
|
-
|
|
83
|
-
// wait for dialog to close
|
|
84
|
-
await wait(500);
|
|
85
|
-
expect(screen.getByText('Trip to Mars')).toBeInTheDocument();
|
|
86
|
-
expect(screen.queryAllByTestId('chevron-down-icon')).toHaveLength(1);
|
|
87
|
-
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
88
|
-
});
|
|
89
|
-
});
|