@transferwise/components 46.64.0 → 46.66.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.
- package/build/common/bottomSheet/BottomSheet.js +8 -2
- package/build/common/bottomSheet/BottomSheet.js.map +1 -1
- package/build/common/bottomSheet/BottomSheet.mjs +8 -2
- package/build/common/bottomSheet/BottomSheet.mjs.map +1 -1
- package/build/decision/Decision.js +4 -0
- package/build/decision/Decision.js.map +1 -1
- package/build/decision/Decision.mjs +4 -0
- package/build/decision/Decision.mjs.map +1 -1
- package/build/drawer/Drawer.js +5 -3
- package/build/drawer/Drawer.js.map +1 -1
- package/build/drawer/Drawer.mjs +5 -3
- package/build/drawer/Drawer.mjs.map +1 -1
- package/build/flowNavigation/FlowNavigation.js +1 -1
- package/build/flowNavigation/FlowNavigation.js.map +1 -1
- package/build/flowNavigation/FlowNavigation.mjs +1 -1
- package/build/flowNavigation/FlowNavigation.mjs.map +1 -1
- package/build/flowNavigation/animatedLabel/AnimatedLabel.js +89 -15
- package/build/flowNavigation/animatedLabel/AnimatedLabel.js.map +1 -1
- package/build/flowNavigation/animatedLabel/AnimatedLabel.mjs +90 -16
- package/build/flowNavigation/animatedLabel/AnimatedLabel.mjs.map +1 -1
- package/build/main.css +10 -1
- package/build/styles/flowNavigation/animatedLabel/AnimatedLabel.css +10 -1
- package/build/styles/main.css +10 -1
- package/build/switch/Switch.js +3 -27
- package/build/switch/Switch.js.map +1 -1
- package/build/switch/Switch.mjs +3 -27
- package/build/switch/Switch.mjs.map +1 -1
- package/build/tile/Tile.js +2 -0
- package/build/tile/Tile.js.map +1 -1
- package/build/tile/Tile.mjs +2 -0
- package/build/tile/Tile.mjs.map +1 -1
- package/build/types/common/bottomSheet/BottomSheet.d.ts +3 -3
- package/build/types/common/bottomSheet/BottomSheet.d.ts.map +1 -1
- package/build/types/decision/Decision.d.ts +1 -0
- package/build/types/decision/Decision.d.ts.map +1 -1
- package/build/types/drawer/Drawer.d.ts +4 -3
- package/build/types/drawer/Drawer.d.ts.map +1 -1
- package/build/types/flowNavigation/FlowNavigation.d.ts.map +1 -1
- package/build/types/flowNavigation/animatedLabel/AnimatedLabel.d.ts +3 -3
- package/build/types/flowNavigation/animatedLabel/AnimatedLabel.d.ts.map +1 -1
- package/build/types/switch/Switch.d.ts.map +1 -1
- package/build/types/tile/Tile.d.ts +3 -1
- package/build/types/tile/Tile.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/bottomSheet/BottomSheet.tsx +13 -4
- package/src/decision/Decision.spec.tsx +166 -0
- package/src/decision/Decision.story.tsx +208 -202
- package/src/decision/Decision.tsx +26 -2
- package/src/drawer/Drawer.tsx +7 -5
- package/src/flowNavigation/FlowNavigation.story.js +69 -17
- package/src/flowNavigation/FlowNavigation.tsx +1 -5
- package/src/flowNavigation/animatedLabel/AnimatedLabel.css +10 -1
- package/src/flowNavigation/animatedLabel/AnimatedLabel.less +10 -1
- package/src/flowNavigation/animatedLabel/AnimatedLabel.spec.js +64 -27
- package/src/flowNavigation/animatedLabel/AnimatedLabel.tsx +102 -20
- package/src/main.css +10 -1
- package/src/switch/Switch.story.tsx +4 -7
- package/src/switch/Switch.tsx +1 -24
- package/src/switch/__snapshots__/Switch.spec.tsx.snap +2 -44
- package/src/tile/Tile.tsx +4 -0
- package/src/decision/Decision.spec.js +0 -127
- package/src/flowNavigation/animatedLabel/__snapshots__/AnimatedLabel.spec.js.snap +0 -25
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
transform: translateX(-8px);
|
|
12
12
|
transition: all 0.3s ease-in;
|
|
13
13
|
}
|
|
14
|
-
.np-animated-label--
|
|
14
|
+
.np-animated-label--active {
|
|
15
15
|
height: auto;
|
|
16
16
|
opacity: 1;
|
|
17
17
|
position: relative;
|
|
@@ -19,3 +19,12 @@
|
|
|
19
19
|
transform: translateX(0);
|
|
20
20
|
transition: all 0.3s ease-in 0.3s;
|
|
21
21
|
}
|
|
22
|
+
.np-animated-label-option {
|
|
23
|
+
border-radius: 10px;
|
|
24
|
+
border-radius: var(--radius-small);
|
|
25
|
+
}
|
|
26
|
+
.np-animated-label-option:not(.disabled):hover,
|
|
27
|
+
.np-animated-label-option:not(.disabled):focus-visible {
|
|
28
|
+
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
29
|
+
outline-offset: var(--ring-outline-offset);
|
|
30
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
@import (reference) "../../../node_modules/@transferwise/neptune-css/src/less/addons/_spacing-utilities.less";
|
|
2
|
+
@import (reference) "../../../node_modules/@transferwise/neptune-css/src/less/ring.less";
|
|
2
3
|
|
|
3
4
|
@transition-timing-function: ease-in-out;
|
|
4
5
|
@transition-duration: 0.3s;
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
transition: all @transition-duration ease-in;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
&--
|
|
24
|
+
&--active {
|
|
24
25
|
height: auto;
|
|
25
26
|
opacity: 1;
|
|
26
27
|
position: relative;
|
|
@@ -28,4 +29,12 @@
|
|
|
28
29
|
transform: translateX(0);
|
|
29
30
|
transition: all @transition-duration ease-in @transition-duration;
|
|
30
31
|
}
|
|
32
|
+
|
|
33
|
+
&-option {
|
|
34
|
+
border-radius: var(--radius-small);
|
|
35
|
+
&:not(.disabled):hover,
|
|
36
|
+
&:not(.disabled):focus-visible {
|
|
37
|
+
.ring();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
31
40
|
}
|
|
@@ -1,53 +1,90 @@
|
|
|
1
|
-
import { render, screen } from '../../test-utils';
|
|
1
|
+
import { render, screen, mockMatchMedia, userEvent, waitFor } from '../../test-utils';
|
|
2
2
|
|
|
3
3
|
import AnimatedLabel from '.';
|
|
4
4
|
|
|
5
|
+
mockMatchMedia();
|
|
6
|
+
|
|
5
7
|
const props = {
|
|
6
8
|
activeLabel: 0,
|
|
7
|
-
|
|
9
|
+
steps: [{ label: 'label1' }, { label: 'label2' }, { label: 'label3' }],
|
|
8
10
|
};
|
|
9
11
|
jest.useFakeTimers();
|
|
10
12
|
describe('AnimatedLabel', () => {
|
|
11
|
-
it('renders all labels', () => {
|
|
12
|
-
const { container } = render(<AnimatedLabel {...props} />);
|
|
13
|
-
expect(container).toMatchSnapshot();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
13
|
it('renders only one label with class in', () => {
|
|
17
14
|
const { container } = render(<AnimatedLabel {...props} />);
|
|
18
|
-
expect(screen.getByText(props.
|
|
19
|
-
expect(container.querySelectorAll('.np-animated-label--
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('renders only one label with class out', () => {
|
|
23
|
-
const { container } = render(<AnimatedLabel {...props} />);
|
|
24
|
-
expect(screen.getByText(props.labels[1])).not.toHaveClass('np-animated-label--in');
|
|
25
|
-
expect(container.querySelectorAll('.np-animated-label--in')).toHaveLength(1);
|
|
15
|
+
expect(screen.getByText(props.steps[0].label)).toHaveClass('np-animated-label--active');
|
|
16
|
+
expect(container.querySelectorAll('.np-animated-label--active')).toHaveLength(1);
|
|
26
17
|
});
|
|
27
18
|
|
|
28
19
|
it('when activeLabel increase it switches class accordingly', () => {
|
|
29
20
|
const { rerender } = render(<AnimatedLabel {...props} />);
|
|
30
|
-
expect(screen.getByText(props.
|
|
31
|
-
expect(screen.getByText(props.
|
|
32
|
-
expect(screen.getByText(props.
|
|
21
|
+
expect(screen.getByText(props.steps[0].label)).toHaveClass('np-animated-label--active');
|
|
22
|
+
expect(screen.getByText(props.steps[1].label)).not.toHaveClass('np-animated-label--active');
|
|
23
|
+
expect(screen.getByText(props.steps[2].label)).not.toHaveClass('np-animated-label--active');
|
|
33
24
|
|
|
34
25
|
rerender(<AnimatedLabel {...props} activeLabel={1} />);
|
|
35
26
|
|
|
36
|
-
expect(screen.getByText(props.
|
|
37
|
-
expect(screen.getByText(props.
|
|
38
|
-
expect(screen.getByText(props.
|
|
27
|
+
expect(screen.getByText(props.steps[0].label)).not.toHaveClass('np-animated-label--active');
|
|
28
|
+
expect(screen.getByText(props.steps[1].label)).toHaveClass('np-animated-label--active');
|
|
29
|
+
expect(screen.getByText(props.steps[2].label)).not.toHaveClass('np-animated-label--active');
|
|
39
30
|
});
|
|
40
31
|
|
|
41
32
|
it('when activeLabel decrease it switches class accordingly', () => {
|
|
42
33
|
const { rerender } = render(<AnimatedLabel {...props} activeLabel={1} />);
|
|
43
|
-
expect(screen.getByText(props.
|
|
44
|
-
expect(screen.getByText(props.
|
|
45
|
-
expect(screen.getByText(props.
|
|
34
|
+
expect(screen.getByText(props.steps[0].label)).not.toHaveClass('np-animated-label--active');
|
|
35
|
+
expect(screen.getByText(props.steps[1].label)).toHaveClass('np-animated-label--active');
|
|
36
|
+
expect(screen.getByText(props.steps[2].label)).not.toHaveClass('np-animated-label--active');
|
|
46
37
|
|
|
47
38
|
rerender(<AnimatedLabel {...props} activeLabel={0} />);
|
|
48
39
|
|
|
49
|
-
expect(screen.getByText(props.
|
|
50
|
-
expect(screen.getByText(props.
|
|
51
|
-
expect(screen.getByText(props.
|
|
40
|
+
expect(screen.getByText(props.steps[0].label)).toHaveClass('np-animated-label--active');
|
|
41
|
+
expect(screen.getByText(props.steps[1].label)).not.toHaveClass('np-animated-label--active');
|
|
42
|
+
expect(screen.getByText(props.steps[2].label)).not.toHaveClass('np-animated-label--active');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('shows all steps in menu when click on the label', async () => {
|
|
46
|
+
render(<AnimatedLabel {...props} activeLabel={1} />);
|
|
47
|
+
expect(screen.queryByRole('menu')).not.toBeInTheDocument();
|
|
48
|
+
userEvent.click(screen.getByRole('button', { name: /label2/i }));
|
|
49
|
+
await waitFor(() => {
|
|
50
|
+
expect(screen.getByRole('menu')).toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
expect(screen.getAllByRole('menuitem')).toHaveLength(props.steps.length);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('switches label when click on one of steps as menu itme', async () => {
|
|
56
|
+
const handleClick = jest.fn();
|
|
57
|
+
const handleStepClick = (stepNumber) => {
|
|
58
|
+
return () => handleClick(stepNumber);
|
|
59
|
+
};
|
|
60
|
+
render(
|
|
61
|
+
<AnimatedLabel
|
|
62
|
+
{...{
|
|
63
|
+
activeLabel: 1,
|
|
64
|
+
steps: [
|
|
65
|
+
{ label: 'label1', onClick: handleStepClick(0) },
|
|
66
|
+
{ label: 'label2', onClick: handleStepClick(1) },
|
|
67
|
+
{ label: 'label3', onClick: handleStepClick(2) },
|
|
68
|
+
],
|
|
69
|
+
}}
|
|
70
|
+
/>,
|
|
71
|
+
);
|
|
72
|
+
expect(screen.getByRole('button', { name: /label2/i })).toBeInTheDocument();
|
|
73
|
+
expect(screen.queryByRole('button', { name: /label1/i })).not.toBeInTheDocument();
|
|
74
|
+
|
|
75
|
+
userEvent.click(screen.getByRole('button', { name: /label2/i }));
|
|
76
|
+
|
|
77
|
+
await waitFor(() => {
|
|
78
|
+
expect(screen.getByRole('menu')).toBeInTheDocument();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
userEvent.click(screen.getByRole('menuitem', { name: /label1/i }));
|
|
82
|
+
|
|
83
|
+
await waitFor(() => {
|
|
84
|
+
expect(screen.queryByRole('menu')).not.toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
88
|
+
expect(handleClick).toHaveBeenCalledWith(0);
|
|
52
89
|
});
|
|
53
90
|
});
|
|
@@ -1,32 +1,114 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
|
-
import
|
|
2
|
+
import { useId, useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import type { Step } from '../../stepper/Stepper';
|
|
5
|
+
import BottomSheet from '../../common/bottomSheet';
|
|
6
|
+
import Option from '../../common/Option';
|
|
7
|
+
import { Check, ChevronDown } from '@transferwise/icons';
|
|
8
|
+
import { OverlayIdContext, OverlayIdProvider } from '../../provider/overlay/OverlayIdProvider';
|
|
9
|
+
import { List } from '../../listItem';
|
|
6
10
|
|
|
7
11
|
export interface AnimatedLabelProps {
|
|
8
12
|
activeLabel: number;
|
|
9
13
|
className?: string;
|
|
10
|
-
|
|
14
|
+
steps: readonly Step[];
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
const AnimatedLabel = ({ activeLabel, className,
|
|
17
|
+
const AnimatedLabel = ({ activeLabel, className, steps }: AnimatedLabelProps) => {
|
|
18
|
+
const labelId = useId();
|
|
19
|
+
const [showSteps, setShowSteps] = useState(false);
|
|
20
|
+
|
|
21
|
+
function handleStepAction(onClick: Step['onClick']) {
|
|
22
|
+
return () => {
|
|
23
|
+
setShowSteps(false);
|
|
24
|
+
onClick?.();
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
return (
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
<OverlayIdProvider open={showSteps}>
|
|
30
|
+
<OverlayIdContext.Consumer>
|
|
31
|
+
{(overlayId) => {
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<button
|
|
35
|
+
type="button"
|
|
36
|
+
id={labelId}
|
|
37
|
+
aria-haspopup="menu"
|
|
38
|
+
aria-controls={overlayId}
|
|
39
|
+
aria-expanded={showSteps}
|
|
40
|
+
className={clsx(
|
|
41
|
+
'np-animated-label',
|
|
42
|
+
'btn-unstyled',
|
|
43
|
+
'np-text-body-large-bold',
|
|
44
|
+
className,
|
|
45
|
+
)}
|
|
46
|
+
onClick={() => setShowSteps(true)}
|
|
47
|
+
>
|
|
48
|
+
{steps.map(({ label }, index) => {
|
|
49
|
+
const isCurrentStep = activeLabel === index;
|
|
50
|
+
const previousIndex = index - 1;
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
key={previousIndex}
|
|
54
|
+
aria-hidden={!isCurrentStep}
|
|
55
|
+
className={clsx('text-xs-center', 'd-inline-flex', {
|
|
56
|
+
'np-animated-label--active text-ellipsis': isCurrentStep,
|
|
57
|
+
})}
|
|
58
|
+
>
|
|
59
|
+
{label} <ChevronDown className="m-l-1" size={24} />
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
})}
|
|
63
|
+
</button>
|
|
64
|
+
<BottomSheet
|
|
65
|
+
role="menu"
|
|
66
|
+
aria-labelledby={labelId}
|
|
67
|
+
open={showSteps}
|
|
68
|
+
onClose={() => setShowSteps(false)}
|
|
69
|
+
>
|
|
70
|
+
<List className="m-b-0 p-a-1">
|
|
71
|
+
{steps.map((step, index) => {
|
|
72
|
+
const isCurrentStep = activeLabel === index;
|
|
73
|
+
const isDisabled = activeLabel < index;
|
|
74
|
+
const itemId = `step-${index}`;
|
|
75
|
+
return (
|
|
76
|
+
<Option
|
|
77
|
+
key={itemId}
|
|
78
|
+
id={itemId}
|
|
79
|
+
as="li"
|
|
80
|
+
role="menuitem"
|
|
81
|
+
decision={false}
|
|
82
|
+
className={clsx('np-animated-label-option', 'p-x-3', 'p-y-1', 'm-y-1', {
|
|
83
|
+
clickable: !isDisabled,
|
|
84
|
+
})}
|
|
85
|
+
title={step.label}
|
|
86
|
+
content={step.hoverLabel}
|
|
87
|
+
button={isCurrentStep ? <Check size={24} /> : null}
|
|
88
|
+
aria-current={isCurrentStep ? 'step' : false}
|
|
89
|
+
aria-disabled={isDisabled}
|
|
90
|
+
disabled={isDisabled}
|
|
91
|
+
isContainerAligned
|
|
92
|
+
{...(!isDisabled && {
|
|
93
|
+
tabIndex: 0,
|
|
94
|
+
onClick: handleStepAction(step.onClick),
|
|
95
|
+
onKeyDown: (event) => {
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
if (event.code === 'Enter' || event.code === 'Space') {
|
|
98
|
+
handleStepAction(step.onClick)();
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
})}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
})}
|
|
105
|
+
</List>
|
|
106
|
+
</BottomSheet>
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
}}
|
|
110
|
+
</OverlayIdContext.Consumer>
|
|
111
|
+
</OverlayIdProvider>
|
|
30
112
|
);
|
|
31
113
|
};
|
|
32
114
|
|
package/src/main.css
CHANGED
|
@@ -2098,7 +2098,7 @@ button.np-option {
|
|
|
2098
2098
|
transform: translateX(-8px);
|
|
2099
2099
|
transition: all 0.15s ease-in;
|
|
2100
2100
|
}
|
|
2101
|
-
.np-animated-label--
|
|
2101
|
+
.np-animated-label--active {
|
|
2102
2102
|
height: auto;
|
|
2103
2103
|
opacity: 1;
|
|
2104
2104
|
position: relative;
|
|
@@ -2106,6 +2106,15 @@ button.np-option {
|
|
|
2106
2106
|
transform: translateX(0);
|
|
2107
2107
|
transition: all 0.15s ease-in 0.15s;
|
|
2108
2108
|
}
|
|
2109
|
+
.np-animated-label-option {
|
|
2110
|
+
border-radius: 10px;
|
|
2111
|
+
border-radius: var(--radius-small);
|
|
2112
|
+
}
|
|
2113
|
+
.np-animated-label-option:not(.disabled):hover,
|
|
2114
|
+
.np-animated-label-option:not(.disabled):focus-visible {
|
|
2115
|
+
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
2116
|
+
outline-offset: var(--ring-outline-offset);
|
|
2117
|
+
}
|
|
2109
2118
|
.np-back-button {
|
|
2110
2119
|
color: #00a2dd;
|
|
2111
2120
|
color: var(--color-interactive-accent);
|
|
@@ -10,7 +10,7 @@ export default {
|
|
|
10
10
|
|
|
11
11
|
export const Basic = () => {
|
|
12
12
|
const [checked1, setCheck1] = useState(false);
|
|
13
|
-
const [checked2, setCheck2] = useState(
|
|
13
|
+
const [checked2, setCheck2] = useState(true);
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
16
|
<div className="d-flex flex-column">
|
|
@@ -22,11 +22,10 @@ export const Basic = () => {
|
|
|
22
22
|
onClick={() => setCheck1(!checked1)}
|
|
23
23
|
/>
|
|
24
24
|
</Field>
|
|
25
|
-
|
|
26
25
|
<Switch
|
|
27
|
-
checked={checked2}
|
|
28
|
-
className="a-class-name m-t-4"
|
|
29
26
|
aria-label="I'm a switch without label"
|
|
27
|
+
checked={checked2}
|
|
28
|
+
className="a-class-name"
|
|
30
29
|
onClick={() => setCheck2(!checked2)}
|
|
31
30
|
/>
|
|
32
31
|
</div>
|
|
@@ -43,17 +42,15 @@ export const Disabled = () => {
|
|
|
43
42
|
checked={checked}
|
|
44
43
|
disabled
|
|
45
44
|
className="a-class-name"
|
|
46
|
-
aria-labelledby="labelID"
|
|
47
45
|
id="switchId"
|
|
48
46
|
onClick={() => setCheck(!checked)}
|
|
49
47
|
/>
|
|
50
48
|
</Field>
|
|
51
|
-
|
|
52
49
|
<Switch
|
|
50
|
+
aria-label="I'm a switch without label"
|
|
53
51
|
checked={!checked}
|
|
54
52
|
disabled
|
|
55
53
|
className="a-class-name"
|
|
56
|
-
aria-labelledby="labelID"
|
|
57
54
|
id="switchId1"
|
|
58
55
|
onClick={() => setCheck(!checked)}
|
|
59
56
|
/>
|
package/src/switch/Switch.tsx
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { CheckCircleFill, CrossCircleFill } from '@transferwise/icons';
|
|
2
|
-
import { useTheme } from '@wise/components-theming';
|
|
3
1
|
import { clsx } from 'clsx';
|
|
4
2
|
import type { KeyboardEventHandler, MouseEvent } from 'react';
|
|
5
3
|
|
|
@@ -26,7 +24,6 @@ export type SwitchProps = CommonProps & {
|
|
|
26
24
|
const Switch = (props: SwitchProps) => {
|
|
27
25
|
const inputAttributes = useInputAttributes({ nonLabelable: true });
|
|
28
26
|
|
|
29
|
-
const { isModern } = useTheme();
|
|
30
27
|
const {
|
|
31
28
|
checked,
|
|
32
29
|
className,
|
|
@@ -44,25 +41,6 @@ const Switch = (props: SwitchProps) => {
|
|
|
44
41
|
}
|
|
45
42
|
};
|
|
46
43
|
|
|
47
|
-
const returnIcon = () => {
|
|
48
|
-
if (isModern) {
|
|
49
|
-
return <span className="np-switch--thumb" />;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (checked) {
|
|
53
|
-
return (
|
|
54
|
-
<span className="np-switch--thumb">
|
|
55
|
-
<CheckCircleFill size={24} />
|
|
56
|
-
</span>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
return (
|
|
60
|
-
<span className="np-switch--thumb">
|
|
61
|
-
<CrossCircleFill size={24} />
|
|
62
|
-
</span>
|
|
63
|
-
);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
44
|
const ariaLabelledby =
|
|
67
45
|
(ariaLabel ? undefined : ariaLabelledbyProp) ?? inputAttributes['aria-labelledby'];
|
|
68
46
|
|
|
@@ -70,7 +48,6 @@ const Switch = (props: SwitchProps) => {
|
|
|
70
48
|
<span
|
|
71
49
|
className={clsx(
|
|
72
50
|
'np-switch',
|
|
73
|
-
|
|
74
51
|
{
|
|
75
52
|
'np-switch--unchecked': !checked,
|
|
76
53
|
'np-switch--checked': checked,
|
|
@@ -89,7 +66,7 @@ const Switch = (props: SwitchProps) => {
|
|
|
89
66
|
onClick={!disabled ? onClick : undefined}
|
|
90
67
|
onKeyDown={!disabled ? handleKeyDown : undefined}
|
|
91
68
|
>
|
|
92
|
-
|
|
69
|
+
<span className="np-switch--thumb" />
|
|
93
70
|
<input type="checkbox" checked={checked} readOnly />
|
|
94
71
|
</span>
|
|
95
72
|
);
|
|
@@ -12,28 +12,7 @@ exports[`Switch renders component when checked 1`] = `
|
|
|
12
12
|
>
|
|
13
13
|
<span
|
|
14
14
|
class="np-switch--thumb"
|
|
15
|
-
|
|
16
|
-
<span
|
|
17
|
-
aria-hidden="true"
|
|
18
|
-
class="tw-icon tw-icon-check-circle-fill "
|
|
19
|
-
data-testid="check-circle-fill-icon"
|
|
20
|
-
role="presentation"
|
|
21
|
-
>
|
|
22
|
-
<svg
|
|
23
|
-
fill="currentColor"
|
|
24
|
-
focusable="false"
|
|
25
|
-
height="24"
|
|
26
|
-
viewBox="0 0 24 24"
|
|
27
|
-
width="24"
|
|
28
|
-
>
|
|
29
|
-
<path
|
|
30
|
-
clip-rule="evenodd"
|
|
31
|
-
d="M12 22.286c5.68 0 10.286-4.605 10.286-10.286C22.286 6.32 17.68 1.714 12 1.714 6.32 1.714 1.714 6.32 1.714 12c0 5.68 4.605 10.286 10.286 10.286Zm-1.32-5.397 7.712-7.711-1.212-1.213-7.109 7.109-3.465-3.466-1.212 1.212 4.068 4.069a.861.861 0 0 0 1.219 0Z"
|
|
32
|
-
fill-rule="evenodd"
|
|
33
|
-
/>
|
|
34
|
-
</svg>
|
|
35
|
-
</span>
|
|
36
|
-
</span>
|
|
15
|
+
/>
|
|
37
16
|
<input
|
|
38
17
|
checked=""
|
|
39
18
|
readonly=""
|
|
@@ -55,28 +34,7 @@ exports[`Switch renders component when unchecked 1`] = `
|
|
|
55
34
|
>
|
|
56
35
|
<span
|
|
57
36
|
class="np-switch--thumb"
|
|
58
|
-
|
|
59
|
-
<span
|
|
60
|
-
aria-hidden="true"
|
|
61
|
-
class="tw-icon tw-icon-cross-circle-fill "
|
|
62
|
-
data-testid="cross-circle-fill-icon"
|
|
63
|
-
role="presentation"
|
|
64
|
-
>
|
|
65
|
-
<svg
|
|
66
|
-
fill="currentColor"
|
|
67
|
-
focusable="false"
|
|
68
|
-
height="24"
|
|
69
|
-
viewBox="0 0 24 24"
|
|
70
|
-
width="24"
|
|
71
|
-
>
|
|
72
|
-
<path
|
|
73
|
-
clip-rule="evenodd"
|
|
74
|
-
d="M1.714 12C1.714 6.344 6.343 1.716 12 1.716S22.286 6.343 22.286 12c0 5.657-4.629 10.285-10.286 10.285S1.714 17.658 1.714 12.001ZM12 10.8l4.243-4.244 1.2 1.2L13.2 12l4.243 4.243-1.2 1.2L12 13.2l-4.243 4.243-1.2-1.2L10.8 12 6.557 7.756l1.2-1.2L12 10.8Z"
|
|
75
|
-
fill-rule="evenodd"
|
|
76
|
-
/>
|
|
77
|
-
</svg>
|
|
78
|
-
</span>
|
|
79
|
-
</span>
|
|
37
|
+
/>
|
|
80
38
|
<input
|
|
81
39
|
readonly=""
|
|
82
40
|
type="checkbox"
|
package/src/tile/Tile.tsx
CHANGED
|
@@ -6,6 +6,8 @@ import Title from '../title';
|
|
|
6
6
|
|
|
7
7
|
export interface TileProps {
|
|
8
8
|
/** Classes to apply to the Tile container */
|
|
9
|
+
/** A label for the accordion item, used for accessibility purposes. */
|
|
10
|
+
'aria-label'?: string;
|
|
9
11
|
className?: string;
|
|
10
12
|
description?: React.ReactNode;
|
|
11
13
|
disabled?: boolean;
|
|
@@ -21,6 +23,7 @@ export interface TileProps {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export default function Tile({
|
|
26
|
+
'aria-label': ariaLabel,
|
|
24
27
|
className,
|
|
25
28
|
description,
|
|
26
29
|
disabled,
|
|
@@ -51,6 +54,7 @@ export default function Tile({
|
|
|
51
54
|
)}
|
|
52
55
|
href={href}
|
|
53
56
|
target={target}
|
|
57
|
+
aria-label={ariaLabel}
|
|
54
58
|
onClick={disabled ? undefined : onClick}
|
|
55
59
|
onKeyDown={
|
|
56
60
|
disabled
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import Avatar from '../avatar';
|
|
2
|
-
import { Breakpoint, Size } from '../common';
|
|
3
|
-
import { mockMatchMedia } from '../mocks';
|
|
4
|
-
import { render, fireEvent, screen } from '../test-utils';
|
|
5
|
-
|
|
6
|
-
import Decision, { DecisionPresentation, DecisionType } from '.';
|
|
7
|
-
|
|
8
|
-
mockMatchMedia(jest);
|
|
9
|
-
|
|
10
|
-
describe('Decision', () => {
|
|
11
|
-
const commonProps = {
|
|
12
|
-
options: [
|
|
13
|
-
{
|
|
14
|
-
media: {
|
|
15
|
-
list: <Avatar type="initials">HM</Avatar>,
|
|
16
|
-
block: <img src="img.jpg" alt="alt" />,
|
|
17
|
-
},
|
|
18
|
-
href: '#href',
|
|
19
|
-
title: 'title',
|
|
20
|
-
description: 'description',
|
|
21
|
-
onClick: jest.fn(),
|
|
22
|
-
},
|
|
23
|
-
],
|
|
24
|
-
presentation: DecisionPresentation.LIST_BLOCK,
|
|
25
|
-
type: DecisionType.NAVIGATION,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const initialInnerWidth = window.innerWidth;
|
|
29
|
-
const resetClientWidth = (width) => {
|
|
30
|
-
window.innerWidth = width;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
afterAll(() => {
|
|
34
|
-
window.innerWidth = initialInnerWidth;
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
resetClientWidth(Breakpoint.EXTRA_SMALL - 1);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe(`when presentation is ${DecisionPresentation.LIST_BLOCK}`, () => {
|
|
42
|
-
const props = { ...commonProps };
|
|
43
|
-
|
|
44
|
-
it('renders only Navigation Option before first breakpoint', () => {
|
|
45
|
-
const { container } = render(<Decision {...props} />);
|
|
46
|
-
|
|
47
|
-
expect(getNavigationOption(container)).toBeInTheDocument();
|
|
48
|
-
expect(getTile(container)).not.toBeInTheDocument();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('renders only Tile after first breakpoint', () => {
|
|
52
|
-
resetClientWidth(Breakpoint.SMALL);
|
|
53
|
-
const { container } = render(<Decision {...props} />);
|
|
54
|
-
|
|
55
|
-
expect(getNavigationOption(container)).not.toBeInTheDocument();
|
|
56
|
-
expect(getTile(container)).toBeInTheDocument();
|
|
57
|
-
expect(getTile(container)).not.toHaveClass('np-tile--small');
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe(`when presentation is ${DecisionPresentation.LIST_BLOCK_GRID}`, () => {
|
|
62
|
-
const props = { ...commonProps, presentation: DecisionPresentation.LIST_BLOCK_GRID };
|
|
63
|
-
|
|
64
|
-
it('renders only Navigation Option before first breakpoint', () => {
|
|
65
|
-
const { container } = render(<Decision {...props} />);
|
|
66
|
-
|
|
67
|
-
expect(getNavigationOption(container)).toBeInTheDocument();
|
|
68
|
-
expect(getTile(container)).not.toBeInTheDocument();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('renders only Tile after first breakpoint', () => {
|
|
72
|
-
resetClientWidth(Breakpoint.SMALL);
|
|
73
|
-
const { container } = render(<Decision {...props} />);
|
|
74
|
-
|
|
75
|
-
expect(getNavigationOption(container)).not.toBeInTheDocument();
|
|
76
|
-
expect(getTile(container)).toBeInTheDocument();
|
|
77
|
-
expect(getTile(container)).not.toHaveClass('np-tile--small');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('renders container as a grid', () => {
|
|
81
|
-
resetClientWidth(Breakpoint.SMALL);
|
|
82
|
-
const { container } = render(<Decision {...props} />);
|
|
83
|
-
|
|
84
|
-
const decisionElement = container.querySelector('.np-decision');
|
|
85
|
-
expect(decisionElement).toHaveClass('np-decision--grid');
|
|
86
|
-
expect(decisionElement).toHaveClass('flex-wrap');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe(`when presentation is ${DecisionPresentation.LIST_BLOCK} and size is Small`, () => {
|
|
91
|
-
const props = {
|
|
92
|
-
...commonProps,
|
|
93
|
-
presentation: DecisionPresentation.LIST_BLOCK,
|
|
94
|
-
size: Size.SMALL,
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
it('renders only Navigation Option before breakpoint', () => {
|
|
98
|
-
const { container } = render(<Decision {...props} />);
|
|
99
|
-
|
|
100
|
-
expect(getNavigationOption(container)).toBeInTheDocument();
|
|
101
|
-
expect(getTile(container)).not.toBeInTheDocument();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('renders Small Tile after breakpoint', () => {
|
|
105
|
-
resetClientWidth(Breakpoint.EXTRA_SMALL);
|
|
106
|
-
const { container } = render(<Decision {...props} />);
|
|
107
|
-
|
|
108
|
-
expect(getNavigationOption(container)).not.toBeInTheDocument();
|
|
109
|
-
expect(getTile(container)).toBeInTheDocument();
|
|
110
|
-
expect(getTile(container)).toHaveClass('np-tile--small');
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe(`when presentation is ${DecisionPresentation.LIST}`, () => {
|
|
115
|
-
const props = { ...commonProps, presentation: DecisionPresentation.LIST };
|
|
116
|
-
|
|
117
|
-
it('renders Navigation Option before breakpoint', () => {
|
|
118
|
-
const { container } = render(<Decision {...props} />);
|
|
119
|
-
|
|
120
|
-
expect(getNavigationOption(container)).toBeInTheDocument();
|
|
121
|
-
expect(getTile(container)).not.toBeInTheDocument();
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const getNavigationOption = (container) => container.querySelector('.np-navigation-option');
|
|
126
|
-
const getTile = (container) => container.querySelector('.np-tile');
|
|
127
|
-
});
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`AnimatedLabel renders all labels 1`] = `
|
|
4
|
-
<div>
|
|
5
|
-
<div
|
|
6
|
-
class="np-text-body-large-bold np-animated-label"
|
|
7
|
-
>
|
|
8
|
-
<div
|
|
9
|
-
class="text-xs-center np-animated-label--in text-ellipsis"
|
|
10
|
-
>
|
|
11
|
-
label1
|
|
12
|
-
</div>
|
|
13
|
-
<div
|
|
14
|
-
class="text-xs-center"
|
|
15
|
-
>
|
|
16
|
-
label2
|
|
17
|
-
</div>
|
|
18
|
-
<div
|
|
19
|
-
class="text-xs-center"
|
|
20
|
-
>
|
|
21
|
-
label3
|
|
22
|
-
</div>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
`;
|