@transferwise/components 46.160.0 → 46.160.1
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/ExpressiveMoneyInput/Chevron/Chevron.js +10 -2
- package/build/ExpressiveMoneyInput/Chevron/Chevron.js.map +1 -1
- package/build/ExpressiveMoneyInput/Chevron/Chevron.mjs +11 -3
- package/build/ExpressiveMoneyInput/Chevron/Chevron.mjs.map +1 -1
- package/build/ProcessIndicator/ProcessIndicator.js +1 -1
- package/build/ProcessIndicator/ProcessIndicator.js.map +1 -1
- package/build/ProcessIndicator/ProcessIndicator.mjs +1 -1
- package/build/ProcessIndicator/ProcessIndicator.mjs.map +1 -1
- package/build/Prompt/CriticalBanner/CriticalBanner.js +10 -1
- package/build/Prompt/CriticalBanner/CriticalBanner.js.map +1 -1
- package/build/Prompt/CriticalBanner/CriticalBanner.mjs +10 -1
- package/build/Prompt/CriticalBanner/CriticalBanner.mjs.map +1 -1
- package/build/common/Panel/Panel.js +13 -25
- package/build/common/Panel/Panel.js.map +1 -1
- package/build/common/Panel/Panel.mjs +10 -22
- package/build/common/Panel/Panel.mjs.map +1 -1
- package/build/common/Panel/Panel.positionUtils.js +68 -0
- package/build/common/Panel/Panel.positionUtils.js.map +1 -0
- package/build/common/Panel/Panel.positionUtils.mjs +63 -0
- package/build/common/Panel/Panel.positionUtils.mjs.map +1 -0
- package/build/common/PropsValues/position.js +0 -25
- package/build/common/PropsValues/position.js.map +1 -1
- package/build/common/PropsValues/position.mjs +1 -24
- package/build/common/PropsValues/position.mjs.map +1 -1
- package/build/main.css +102 -68
- package/build/styles/AvatarView/AvatarView.css +8 -0
- package/build/styles/AvatarView/Dot/Dot.css +8 -0
- package/build/styles/CheckboxButton/CheckboxButton.css +3 -0
- package/build/styles/Nudge/Nudge.css +4 -38
- package/build/styles/Prompt/CriticalBanner/CriticalBanner.css +34 -0
- package/build/styles/SlidingPanel/SlidingPanel.css +8 -0
- package/build/styles/UploadInput/UploadItem/UploadItem.css +4 -5
- package/build/styles/_deprecated/Badge/Badge.css +4 -2
- package/build/styles/common/Panel/Panel.css +12 -4
- package/build/styles/css/accordion.css +3 -0
- package/build/styles/css/buttons.css +1 -7
- package/build/styles/css/chevron.css +3 -0
- package/build/styles/css/neptune.css +25 -19
- package/build/styles/css/process.css +15 -9
- package/build/styles/css/tick.css +6 -3
- package/build/styles/main.css +102 -68
- package/build/styles/styles/less/accordion.css +3 -0
- package/build/styles/styles/less/buttons.css +1 -7
- package/build/styles/styles/less/chevron.css +3 -0
- package/build/styles/styles/less/neptune.css +25 -19
- package/build/styles/styles/less/process.css +15 -9
- package/build/styles/styles/less/tick.css +6 -3
- package/build/types/ExpressiveMoneyInput/Chevron/Chevron.d.ts.map +1 -1
- package/build/types/Prompt/CriticalBanner/CriticalBanner.d.ts +6 -1
- package/build/types/Prompt/CriticalBanner/CriticalBanner.d.ts.map +1 -1
- package/build/types/common/Panel/Panel.d.ts +1 -1
- package/build/types/common/Panel/Panel.d.ts.map +1 -1
- package/build/types/common/Panel/Panel.positionUtils.d.ts +7 -0
- package/build/types/common/Panel/Panel.positionUtils.d.ts.map +1 -0
- package/build/types/common/PropsValues/position.d.ts +0 -13
- package/build/types/common/PropsValues/position.d.ts.map +1 -1
- package/build/types/common/ResponsivePanel/ResponsivePanel.d.ts +1 -1
- package/package.json +5 -5
- package/src/AvatarView/AvatarView.css +8 -0
- package/src/AvatarView/Dot/Dot.css +8 -0
- package/src/AvatarView/Dot/Dot.less +7 -1
- package/src/Button/_stories/Button.test.story.tsx +1 -1
- package/src/Checkbox/_stories/Checkbox.story.tsx +1 -1
- package/src/Checkbox/_stories/Checkbox.test.story.tsx +3 -2
- package/src/CheckboxButton/CheckboxButton.css +3 -0
- package/src/CheckboxButton/CheckboxButton.less +4 -0
- package/src/Chevron/_stories/Chevron.test.story.tsx +2 -2
- package/src/ExpressiveMoneyInput/Chevron/Chevron.tsx +8 -3
- package/src/Header/_stories/Header.test.story.tsx +1 -1
- package/src/Info/_stories/Info.rtl.test.story.tsx +35 -10
- package/src/Info/_stories/Info.story.tsx +1 -1
- package/src/Inputs/SelectInput/_stories/SelectInput.test.story.tsx +1 -1
- package/src/ListItem/_stories/subcomponents.tsx +1 -1
- package/src/Loader/_stories/Loader.test.story.tsx +23 -0
- package/src/MoneyInput/_stories/MoneyInput.rtl.test.story.tsx +1 -1
- package/src/Nudge/Nudge.css +4 -38
- package/src/Nudge/Nudge.less +4 -4
- package/src/Nudge/_stories/Nudge.test.story.tsx +29 -12
- package/src/PhoneNumberInput/_stories/{PhoneNumberInput.test.story.tsx → PhoneNumberInput.rtl.test.story.tsx} +18 -10
- package/src/ProcessIndicator/ProcessIndicator.tsx +1 -1
- package/src/ProcessIndicator/_stories/ProcessIndicator.test.story.tsx +32 -0
- package/src/ProgressBar/_stories/ProgressBar.test.story.tsx +33 -19
- package/src/Prompt/CriticalBanner/CriticalBanner.css +34 -0
- package/src/Prompt/CriticalBanner/CriticalBanner.less +45 -0
- package/src/Prompt/CriticalBanner/CriticalBanner.test.tsx +26 -3
- package/src/Prompt/CriticalBanner/CriticalBanner.tsx +15 -1
- package/src/Prompt/CriticalBanner/_stories/CriticalBanner.accessibility.docs.mdx +4 -1
- package/src/Prompt/CriticalBanner/_stories/CriticalBanner.story.tsx +52 -0
- package/src/Prompt/CriticalBanner/_stories/CriticalBanner.test.story.tsx +22 -1
- package/src/SlidingPanel/SlidingPanel.css +8 -0
- package/src/SlidingPanel/SlidingPanel.less +8 -0
- package/src/SlidingPanel/_stories/SlidingPanel.rtl.test.story.tsx +4 -3
- package/src/Snackbar/_stories/Snackbar.rtl.test.story.tsx +14 -16
- package/src/Snackbar/_stories/Snackbar.story.tsx +1 -1
- package/src/Snackbar/_stories/Snackbar.test.story.tsx +1 -1
- package/src/StatusIcon/_stories/StatusIcon.test.story.tsx +12 -4
- package/src/UploadInput/UploadItem/UploadItem.css +4 -5
- package/src/UploadInput/UploadItem/UploadItem.less +7 -6
- package/src/UploadInput/_stories/UploadInput.test.story.tsx +1 -1
- package/src/_deprecated/Badge/Badge.css +4 -2
- package/src/_deprecated/Badge/Badge.less +7 -14
- package/src/_deprecated/CriticalBanner/_stories/CriticalCommsBanner.test.story.tsx +0 -16
- package/src/_deprecated/OverlayHeader/_stories/OverlayHeader.test.story.tsx +15 -0
- package/src/common/Panel/Panel.css +12 -4
- package/src/common/Panel/Panel.less +27 -9
- package/src/common/Panel/Panel.positionUtils.ts +79 -0
- package/src/common/Panel/Panel.test.tsx +40 -0
- package/src/common/Panel/Panel.tsx +13 -24
- package/src/common/Panel/_stories/Panel.rtl.test.story.tsx +110 -0
- package/src/common/PropsValues/position.ts +0 -50
- package/src/main.css +102 -68
- package/src/styles/less/accordion.css +3 -0
- package/src/styles/less/buttons.css +1 -7
- package/src/styles/less/buttons.less +3 -11
- package/src/styles/less/chevron.css +3 -0
- package/src/styles/less/chevron.less +4 -0
- package/src/styles/less/neptune.css +25 -19
- package/src/styles/less/process.css +15 -9
- package/src/styles/less/process.less +13 -6
- package/src/styles/less/tick.css +6 -3
- package/src/styles/less/tick.less +7 -3
|
@@ -41,8 +41,7 @@
|
|
|
41
41
|
.np-upload-input__item .np-upload-input__title,
|
|
42
42
|
.np-upload-input__item .np-upload-input__text {
|
|
43
43
|
margin: 0;
|
|
44
|
-
|
|
45
|
-
text-align-last: left;
|
|
44
|
+
text-align: start;
|
|
46
45
|
color: #5d7079;
|
|
47
46
|
color: var(--color-content-secondary);
|
|
48
47
|
}
|
|
@@ -98,7 +97,7 @@
|
|
|
98
97
|
justify-content: center;
|
|
99
98
|
}
|
|
100
99
|
.np-upload-input__item .np-upload-input__item-action .np-upload-input__item-button:before {
|
|
101
|
-
content:
|
|
100
|
+
content: "";
|
|
102
101
|
display: block;
|
|
103
102
|
inline-size: var(--clickAreaSize);
|
|
104
103
|
block-size: var(--clickAreaSize);
|
|
@@ -153,8 +152,8 @@
|
|
|
153
152
|
padding-inline-start: var(--size-16);
|
|
154
153
|
}
|
|
155
154
|
.np-upload-input__item .np-upload-input-errors > li::before {
|
|
156
|
-
content:
|
|
157
|
-
content:
|
|
155
|
+
content: "•" ;
|
|
156
|
+
content: "•" / "";
|
|
158
157
|
position: absolute;
|
|
159
158
|
display: block;
|
|
160
159
|
inset-inline-start: 0;
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
.np-upload-input__title,
|
|
40
40
|
.np-upload-input__text {
|
|
41
41
|
margin: 0;
|
|
42
|
-
text-align
|
|
42
|
+
text-align: left;
|
|
43
43
|
color: var(--color-content-secondary);
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
--buttonTopRightOffset: var(--size-16);
|
|
65
65
|
// Offset towards the parent's top-right edge
|
|
66
66
|
// ensuring no overflow on smaller screens
|
|
67
|
-
--clickAreaTopRightOffset: calc((var(--clickAreaSize) - var(--iconSize)) *
|
|
67
|
+
--clickAreaTopRightOffset: calc((var(--clickAreaSize) - var(--iconSize)) * -0.5);
|
|
68
68
|
|
|
69
69
|
position: absolute;
|
|
70
70
|
right: var(--buttonTopRightOffset);
|
|
@@ -84,14 +84,16 @@
|
|
|
84
84
|
border: 0;
|
|
85
85
|
background-color: var(--color-background-neutral);
|
|
86
86
|
color: var(--color-interactive-primary);
|
|
87
|
-
transition:
|
|
87
|
+
transition:
|
|
88
|
+
color,
|
|
89
|
+
background-color 0.15s ease-in-out;
|
|
88
90
|
outline-offset: 0;
|
|
89
91
|
display: flex;
|
|
90
92
|
align-items: center;
|
|
91
93
|
justify-content: center;
|
|
92
94
|
|
|
93
95
|
&:before {
|
|
94
|
-
content:
|
|
96
|
+
content: "";
|
|
95
97
|
display: block;
|
|
96
98
|
width: var(--clickAreaSize);
|
|
97
99
|
height: var(--clickAreaSize);
|
|
@@ -116,7 +118,6 @@
|
|
|
116
118
|
padding: 0;
|
|
117
119
|
|
|
118
120
|
&:hover:not(:has(.np-upload-input__item-button:hover)) {
|
|
119
|
-
|
|
120
121
|
&:before,
|
|
121
122
|
& + .np-upload-input__item:before {
|
|
122
123
|
width: 100%;
|
|
@@ -158,7 +159,7 @@
|
|
|
158
159
|
padding-left: var(--size-16);
|
|
159
160
|
|
|
160
161
|
&::before {
|
|
161
|
-
content:
|
|
162
|
+
content: "•" / "";
|
|
162
163
|
position: absolute;
|
|
163
164
|
display: block;
|
|
164
165
|
left: 0;
|
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
--badge-border-color: rgba(0, 0, 0, 0.08);
|
|
16
16
|
}
|
|
17
17
|
.tw-badge > .tw-badge__children {
|
|
18
|
+
--badge-h-side: left;
|
|
18
19
|
-webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) left calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
19
20
|
mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) left calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
21
|
+
-webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) var(--badge-h-side) calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
22
|
+
mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) var(--badge-h-side) calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
20
23
|
}
|
|
21
24
|
[dir="rtl"] .tw-badge > .tw-badge__children {
|
|
22
|
-
|
|
23
|
-
mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
25
|
+
--badge-h-side: right;
|
|
24
26
|
}
|
|
25
27
|
.tw-badge > .tw-badge__content {
|
|
26
28
|
position: absolute;
|
|
@@ -29,26 +29,19 @@
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
& > &__children {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
--badge-h-side: left;
|
|
33
|
+
|
|
34
|
+
[dir="rtl"] & {
|
|
35
|
+
--badge-h-side: right;
|
|
36
|
+
}
|
|
34
37
|
|
|
35
38
|
mask-image: radial-gradient(
|
|
36
|
-
circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position))
|
|
37
|
-
calc(100% - var(--badge-mask-offset) - var(--badge-content-position)),
|
|
39
|
+
circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position))
|
|
40
|
+
var(--badge-h-side) calc(100% - var(--badge-mask-offset) - var(--badge-content-position)),
|
|
38
41
|
transparent 0,
|
|
39
42
|
transparent calc(var(--badge-size) / 2 + var(--badge-mask)),
|
|
40
43
|
black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px)
|
|
41
44
|
);
|
|
42
|
-
|
|
43
|
-
[dir="rtl"] & {
|
|
44
|
-
mask-image: radial-gradient(
|
|
45
|
-
circle at top calc(100% - var(--badge-mask-offset)) right
|
|
46
|
-
calc(100% - var(--badge-mask-offset)),
|
|
47
|
-
transparent 0,
|
|
48
|
-
transparent calc(var(--badge-size) / 2 + var(--badge-mask)),
|
|
49
|
-
black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px)
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
45
|
}
|
|
53
46
|
|
|
54
47
|
& > &__content {
|
|
@@ -36,22 +36,6 @@ export const Sentiments: Story = {
|
|
|
36
36
|
]),
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export const RTL: Story = {
|
|
40
|
-
render: (args) => (
|
|
41
|
-
<>
|
|
42
|
-
<CriticalCommsBanner {...args} sentiment="negative" />
|
|
43
|
-
<CriticalCommsBanner {...args} sentiment="warning" />
|
|
44
|
-
<CriticalCommsBanner {...args} sentiment="neutral" />
|
|
45
|
-
</>
|
|
46
|
-
),
|
|
47
|
-
args: {
|
|
48
|
-
subtitle: 'Add money within the next 30 days',
|
|
49
|
-
action: { label: 'Take action', href: 'https://wise.com' },
|
|
50
|
-
className: 'm-b-1',
|
|
51
|
-
},
|
|
52
|
-
...withVariantConfig(['rtl']),
|
|
53
|
-
};
|
|
54
|
-
|
|
55
39
|
export const Mobile: Story = {
|
|
56
40
|
args: {
|
|
57
41
|
subtitle: 'Add money within the next 30 days',
|
|
@@ -40,3 +40,18 @@ export const RTLDiff: Story = {
|
|
|
40
40
|
),
|
|
41
41
|
...withRTLDiff(),
|
|
42
42
|
};
|
|
43
|
+
|
|
44
|
+
export const RTLDiffMobile: Story = {
|
|
45
|
+
render: () => (
|
|
46
|
+
<OverlayHeader
|
|
47
|
+
logo={<Logo />}
|
|
48
|
+
avatar={
|
|
49
|
+
<AvatarView>
|
|
50
|
+
<Person size="24" />
|
|
51
|
+
</AvatarView>
|
|
52
|
+
}
|
|
53
|
+
onClose={() => {}}
|
|
54
|
+
/>
|
|
55
|
+
),
|
|
56
|
+
...withRTLDiff({ viewport: 'largeMobile' }),
|
|
57
|
+
};
|
|
@@ -39,27 +39,35 @@
|
|
|
39
39
|
.np-panel[data-popper-placement^="top"] > .np-panel__content {
|
|
40
40
|
transform: translateY(10px) scale(0.95);
|
|
41
41
|
}
|
|
42
|
-
.np-panel[data-popper-placement^="top"]
|
|
42
|
+
.np-panel[data-popper-placement^="top"] .np-panel__arrow {
|
|
43
43
|
inset-block-end: -6px;
|
|
44
44
|
}
|
|
45
45
|
.np-panel[data-popper-placement^="right"] > .np-panel__content {
|
|
46
46
|
transform: translateX(-10px) scale(0.95);
|
|
47
47
|
}
|
|
48
|
-
.np-panel[data-popper-placement^="right"]
|
|
48
|
+
.np-panel[data-popper-placement^="right"] .np-panel__arrow {
|
|
49
49
|
inset-inline-start: -6px;
|
|
50
50
|
}
|
|
51
|
+
[dir="rtl"] .np-panel[data-popper-placement^="right"] .np-panel__arrow {
|
|
52
|
+
inset-inline-start: unset;
|
|
53
|
+
inset-inline-end: -6px;
|
|
54
|
+
}
|
|
51
55
|
.np-panel[data-popper-placement^="bottom"] > .np-panel__content {
|
|
52
56
|
transform: translateY(-10px) scale(0.95);
|
|
53
57
|
}
|
|
54
|
-
.np-panel[data-popper-placement^="bottom"]
|
|
58
|
+
.np-panel[data-popper-placement^="bottom"] .np-panel__arrow {
|
|
55
59
|
inset-block-start: -6px;
|
|
56
60
|
}
|
|
57
61
|
.np-panel[data-popper-placement^="left"] > .np-panel__content {
|
|
58
62
|
transform: translateX(10px) scale(0.95);
|
|
59
63
|
}
|
|
60
|
-
.np-panel[data-popper-placement^="left"]
|
|
64
|
+
.np-panel[data-popper-placement^="left"] .np-panel__arrow {
|
|
61
65
|
inset-inline-end: -6px;
|
|
62
66
|
}
|
|
67
|
+
[dir="rtl"] .np-panel[data-popper-placement^="left"] .np-panel__arrow {
|
|
68
|
+
inset-inline-end: unset;
|
|
69
|
+
inset-inline-start: -6px;
|
|
70
|
+
}
|
|
63
71
|
.np-theme-personal .np-panel__content {
|
|
64
72
|
border-radius: 16px;
|
|
65
73
|
border-radius: var(--radius-medium);
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
opacity 0.2s cubic-bezier(0.6, 0.2, 0.1, 1),
|
|
12
12
|
transform 0.2s cubic-bezier(0.6, 0.2, 0.1, 1),
|
|
13
13
|
visibility 0s linear 0.2s;
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
.np-theme-personal & {
|
|
16
16
|
border-radius: var(--radius-small);
|
|
17
17
|
}
|
|
@@ -45,35 +45,53 @@
|
|
|
45
45
|
background: var(--color-background-elevated);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
&[data-popper-placement^="top"]
|
|
49
|
-
|
|
48
|
+
&[data-popper-placement^="top"] {
|
|
49
|
+
> .np-panel__content {
|
|
50
|
+
transform: translateY(10px) scale(0.95);
|
|
51
|
+
}
|
|
50
52
|
|
|
51
53
|
.np-panel__arrow {
|
|
52
54
|
bottom: -6px;
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
&[data-popper-placement^="right"]
|
|
57
|
-
|
|
58
|
+
&[data-popper-placement^="right"] {
|
|
59
|
+
> .np-panel__content {
|
|
60
|
+
transform: translateX(-10px) scale(0.95);
|
|
61
|
+
}
|
|
58
62
|
|
|
59
63
|
.np-panel__arrow {
|
|
60
64
|
left: -6px;
|
|
65
|
+
|
|
66
|
+
[dir="rtl"] & {
|
|
67
|
+
left: unset;
|
|
68
|
+
right: -6px;
|
|
69
|
+
}
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
&[data-popper-placement^="bottom"]
|
|
65
|
-
|
|
73
|
+
&[data-popper-placement^="bottom"] {
|
|
74
|
+
> .np-panel__content {
|
|
75
|
+
transform: translateY(-10px) scale(0.95);
|
|
76
|
+
}
|
|
66
77
|
|
|
67
78
|
.np-panel__arrow {
|
|
68
79
|
top: -6px;
|
|
69
80
|
}
|
|
70
81
|
}
|
|
71
82
|
|
|
72
|
-
&[data-popper-placement^="left"]
|
|
73
|
-
|
|
83
|
+
&[data-popper-placement^="left"] {
|
|
84
|
+
> .np-panel__content {
|
|
85
|
+
transform: translateX(10px) scale(0.95);
|
|
86
|
+
}
|
|
74
87
|
|
|
75
88
|
.np-panel__arrow {
|
|
76
89
|
right: -6px;
|
|
90
|
+
|
|
91
|
+
[dir="rtl"] & {
|
|
92
|
+
right: unset;
|
|
93
|
+
left: -6px;
|
|
94
|
+
}
|
|
77
95
|
}
|
|
78
96
|
}
|
|
79
97
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Placement } from '@floating-ui/react';
|
|
2
|
+
|
|
3
|
+
export type FloatingPlacement =
|
|
4
|
+
| 'top'
|
|
5
|
+
| 'right'
|
|
6
|
+
| 'bottom'
|
|
7
|
+
| 'left'
|
|
8
|
+
| 'top-left'
|
|
9
|
+
| 'top-right'
|
|
10
|
+
| 'right-top'
|
|
11
|
+
| 'right-bottom'
|
|
12
|
+
| 'bottom-left'
|
|
13
|
+
| 'bottom-right'
|
|
14
|
+
| 'left-top'
|
|
15
|
+
| 'left-bottom';
|
|
16
|
+
|
|
17
|
+
export const POPOVER_OFFSET = 16;
|
|
18
|
+
|
|
19
|
+
// By default the flip positioning explores only the opposite alternative. So if left is passed and there's no enough space
|
|
20
|
+
// the right one gets chosen. If there's no space on both sides popover goes back to the initially chosen one left.
|
|
21
|
+
// This mapping forces popover to try the four available positions before going back to the initial chosen one.
|
|
22
|
+
export const fallbackPlacements: Record<Placement, Placement[]> = {
|
|
23
|
+
top: ['bottom', 'right', 'left'],
|
|
24
|
+
bottom: ['top', 'right', 'left'],
|
|
25
|
+
left: ['right', 'top', 'bottom'],
|
|
26
|
+
right: ['left', 'top', 'bottom'],
|
|
27
|
+
'top-start': ['bottom-start', 'top-end', 'bottom-end'],
|
|
28
|
+
'top-end': ['bottom-end', 'top-start', 'bottom-start'],
|
|
29
|
+
'bottom-start': ['top-start', 'bottom-end', 'top-end'],
|
|
30
|
+
'bottom-end': ['top-end', 'bottom-start', 'top-start'],
|
|
31
|
+
'left-start': ['right-start', 'left-end', 'right-end'],
|
|
32
|
+
'left-end': ['right-end', 'left-start', 'right-start'],
|
|
33
|
+
'right-start': ['left-start', 'right-end', 'left-end'],
|
|
34
|
+
'right-end': ['left-end', 'right-start', 'left-start'],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const positionToPlacement = (position: FloatingPlacement): Placement => {
|
|
38
|
+
switch (position) {
|
|
39
|
+
case 'top-left':
|
|
40
|
+
return 'top-start';
|
|
41
|
+
case 'top-right':
|
|
42
|
+
return 'top-end';
|
|
43
|
+
case 'bottom-left':
|
|
44
|
+
return 'bottom-start';
|
|
45
|
+
case 'bottom-right':
|
|
46
|
+
return 'bottom-end';
|
|
47
|
+
case 'left-top':
|
|
48
|
+
return 'left-start';
|
|
49
|
+
case 'left-bottom':
|
|
50
|
+
return 'left-end';
|
|
51
|
+
case 'right-top':
|
|
52
|
+
return 'right-start';
|
|
53
|
+
case 'right-bottom':
|
|
54
|
+
return 'right-end';
|
|
55
|
+
default:
|
|
56
|
+
return position;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const mirrorPositionForRtl = (position: FloatingPlacement): FloatingPlacement => {
|
|
61
|
+
switch (position) {
|
|
62
|
+
case 'left':
|
|
63
|
+
return 'right';
|
|
64
|
+
case 'right':
|
|
65
|
+
return 'left';
|
|
66
|
+
case 'left-top':
|
|
67
|
+
return 'right-top';
|
|
68
|
+
case 'left-bottom':
|
|
69
|
+
return 'right-bottom';
|
|
70
|
+
case 'right-top':
|
|
71
|
+
return 'left-top';
|
|
72
|
+
case 'right-bottom':
|
|
73
|
+
return 'left-bottom';
|
|
74
|
+
default:
|
|
75
|
+
// top-left/top-right/bottom-left/bottom-right map to start/end via
|
|
76
|
+
// positionToPlacement, and floating-ui already flips start/end in RTL.
|
|
77
|
+
return position;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRef } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Position } from '..';
|
|
4
|
+
import { DirectionProvider } from '../../Provider/Direction/DirectionProvider';
|
|
4
5
|
import { render } from '../../test-utils';
|
|
5
6
|
|
|
6
7
|
import Panel from './Panel';
|
|
@@ -93,4 +94,43 @@ describe('Panel', () => {
|
|
|
93
94
|
expect(getPanel()).toHaveAttribute('data-popper-placement', expectedPlacement);
|
|
94
95
|
});
|
|
95
96
|
});
|
|
97
|
+
|
|
98
|
+
describe('RTL position mirroring', () => {
|
|
99
|
+
it.each<[PanelProps['position'], string]>([
|
|
100
|
+
[Position.LEFT, 'right'],
|
|
101
|
+
[Position.RIGHT, 'left'],
|
|
102
|
+
[Position.LEFT_TOP, 'right-start'],
|
|
103
|
+
[Position.LEFT_BOTTOM, 'right-end'],
|
|
104
|
+
[Position.RIGHT_TOP, 'left-start'],
|
|
105
|
+
[Position.RIGHT_BOTTOM, 'left-end'],
|
|
106
|
+
])('mirrors %s in RTL to floating-ui placement %s', (position, expectedPlacement) => {
|
|
107
|
+
render(
|
|
108
|
+
<DirectionProvider direction="rtl">
|
|
109
|
+
<Panel {...props} position={position} />
|
|
110
|
+
</DirectionProvider>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(getPanel()).toHaveAttribute('data-popper-placement', expectedPlacement);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it.each<[PanelProps['position'], string]>([
|
|
117
|
+
[Position.TOP, 'top'],
|
|
118
|
+
[Position.BOTTOM, 'bottom'],
|
|
119
|
+
[Position.TOP_LEFT, 'top-start'],
|
|
120
|
+
[Position.TOP_RIGHT, 'top-end'],
|
|
121
|
+
[Position.BOTTOM_LEFT, 'bottom-start'],
|
|
122
|
+
[Position.BOTTOM_RIGHT, 'bottom-end'],
|
|
123
|
+
])(
|
|
124
|
+
'does not mirror %s in RTL (floating-ui handles start/end flip)',
|
|
125
|
+
(position, expectedPlacement) => {
|
|
126
|
+
render(
|
|
127
|
+
<DirectionProvider direction="rtl">
|
|
128
|
+
<Panel {...props} position={position} />
|
|
129
|
+
</DirectionProvider>,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(getPanel()).toHaveAttribute('data-popper-placement', expectedPlacement);
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
});
|
|
96
136
|
});
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
autoUpdate,
|
|
4
4
|
flip as flipMiddleware,
|
|
5
5
|
offset,
|
|
6
|
-
type Placement,
|
|
7
6
|
shift,
|
|
8
7
|
size as sizeMiddleware,
|
|
9
8
|
useFloating,
|
|
@@ -20,29 +19,17 @@ import {
|
|
|
20
19
|
useRef,
|
|
21
20
|
} from 'react';
|
|
22
21
|
|
|
23
|
-
import { Position, type FloatingPlacement, positionToPlacement } from '..';
|
|
24
|
-
import Dimmer from '../../Dimmer';
|
|
25
22
|
import { OverlayIdContext } from '../../Provider/overlay/OverlayIdProvider';
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
[Position.RIGHT]: [Position.LEFT, Position.TOP, Position.BOTTOM],
|
|
37
|
-
'top-start': ['bottom-start', 'top-end', 'bottom-end'],
|
|
38
|
-
'top-end': ['bottom-end', 'top-start', 'bottom-start'],
|
|
39
|
-
'bottom-start': ['top-start', 'bottom-end', 'top-end'],
|
|
40
|
-
'bottom-end': ['top-end', 'bottom-start', 'top-start'],
|
|
41
|
-
'left-start': ['right-start', 'left-end', 'right-end'],
|
|
42
|
-
'left-end': ['right-end', 'left-start', 'right-start'],
|
|
43
|
-
'right-start': ['left-start', 'right-end', 'left-end'],
|
|
44
|
-
'right-end': ['left-end', 'right-start', 'left-start'],
|
|
45
|
-
};
|
|
23
|
+
import Dimmer from '../../Dimmer';
|
|
24
|
+
import useDirection from '../hooks/useDirection';
|
|
25
|
+
import { Position } from '..';
|
|
26
|
+
import {
|
|
27
|
+
type FloatingPlacement,
|
|
28
|
+
POPOVER_OFFSET,
|
|
29
|
+
fallbackPlacements,
|
|
30
|
+
mirrorPositionForRtl,
|
|
31
|
+
positionToPlacement,
|
|
32
|
+
} from './Panel.positionUtils';
|
|
46
33
|
|
|
47
34
|
export type PanelProps = PropsWithChildren<{
|
|
48
35
|
arrow?: boolean;
|
|
@@ -74,7 +61,9 @@ const Panel = forwardRef<HTMLDivElement, PanelProps>(function Panel(
|
|
|
74
61
|
reference,
|
|
75
62
|
) {
|
|
76
63
|
const arrowRef = useRef<HTMLDivElement | null>(null);
|
|
77
|
-
const
|
|
64
|
+
const { isRTL } = useDirection();
|
|
65
|
+
const resolvedPosition = isRTL ? mirrorPositionForRtl(position) : position;
|
|
66
|
+
const floatingPlacement = positionToPlacement(resolvedPosition);
|
|
78
67
|
|
|
79
68
|
const middleware = [];
|
|
80
69
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { withRTLDiff } from '../../../../.storybook/decorators/RtlDiffDecorator';
|
|
4
|
+
import Button from '../../../Button';
|
|
5
|
+
import { Position } from '../../PropsValues/position';
|
|
6
|
+
import Panel from '../Panel';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
component: Panel,
|
|
10
|
+
title: 'Dialogs/Panel/Tests/RTL Diff',
|
|
11
|
+
tags: ['!autodocs', '!manifest'],
|
|
12
|
+
} satisfies Meta<typeof Panel>;
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof Panel>;
|
|
15
|
+
|
|
16
|
+
function PanelStory({
|
|
17
|
+
position,
|
|
18
|
+
label,
|
|
19
|
+
}: {
|
|
20
|
+
position: Parameters<typeof Panel>[0]['position'];
|
|
21
|
+
label: string;
|
|
22
|
+
}) {
|
|
23
|
+
const anchorRef = useRef<HTMLButtonElement>(null);
|
|
24
|
+
const margin =
|
|
25
|
+
position?.startsWith('bottom') || position?.startsWith('top') ? '3.5rem 0' : '2.5rem 0';
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<Button ref={anchorRef} style={{ margin }}>
|
|
30
|
+
{label}
|
|
31
|
+
</Button>
|
|
32
|
+
<Panel
|
|
33
|
+
open
|
|
34
|
+
position={position}
|
|
35
|
+
anchorRef={anchorRef}
|
|
36
|
+
arrow
|
|
37
|
+
aria-label={`Panel positioned ${label}`}
|
|
38
|
+
>
|
|
39
|
+
<div style={{ padding: '16px', maxWidth: '200px' }}>
|
|
40
|
+
Panel content positioned at <strong>{label}</strong>
|
|
41
|
+
</div>
|
|
42
|
+
</Panel>
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const Top: Story = {
|
|
48
|
+
render: () => (
|
|
49
|
+
<>
|
|
50
|
+
<PanelStory position={Position.TOP_LEFT} label="top-left" />
|
|
51
|
+
<PanelStory position={Position.TOP} label="top" />
|
|
52
|
+
<PanelStory position={Position.TOP_RIGHT} label="top-right" />
|
|
53
|
+
</>
|
|
54
|
+
),
|
|
55
|
+
...withRTLDiff({ renderAs: 'iframe', hAlign: 'center', vAlign: 'bottom' }),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const Right: Story = {
|
|
59
|
+
render: () => (
|
|
60
|
+
<>
|
|
61
|
+
<PanelStory position={Position.RIGHT_TOP} label="right-top" />
|
|
62
|
+
<PanelStory position={Position.RIGHT} label="right" />
|
|
63
|
+
<PanelStory position={Position.RIGHT_BOTTOM} label="right-bottom" />
|
|
64
|
+
</>
|
|
65
|
+
),
|
|
66
|
+
...withRTLDiff({ renderAs: 'iframe', hAlign: 'left', vAlign: 'center' }),
|
|
67
|
+
};
|
|
68
|
+
export const RightFlipped: Story = {
|
|
69
|
+
render: () => (
|
|
70
|
+
<>
|
|
71
|
+
<PanelStory position={Position.RIGHT_TOP} label="right-top" />
|
|
72
|
+
<PanelStory position={Position.RIGHT} label="right" />
|
|
73
|
+
<PanelStory position={Position.RIGHT_BOTTOM} label="right-bottom" />
|
|
74
|
+
</>
|
|
75
|
+
),
|
|
76
|
+
...withRTLDiff({ renderAs: 'iframe', hAlign: 'right', vAlign: 'center' }),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const Bottom: Story = {
|
|
80
|
+
render: () => (
|
|
81
|
+
<>
|
|
82
|
+
<PanelStory position={Position.BOTTOM_LEFT} label="bottom-left" />
|
|
83
|
+
<PanelStory position={Position.BOTTOM} label="bottom" />
|
|
84
|
+
<PanelStory position={Position.BOTTOM_RIGHT} label="bottom-right" />
|
|
85
|
+
</>
|
|
86
|
+
),
|
|
87
|
+
...withRTLDiff({ renderAs: 'iframe', hAlign: 'center' }),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const Left: Story = {
|
|
91
|
+
render: () => (
|
|
92
|
+
<>
|
|
93
|
+
<PanelStory position={Position.LEFT_TOP} label="left-top" />
|
|
94
|
+
<PanelStory position={Position.LEFT} label="left" />
|
|
95
|
+
<PanelStory position={Position.LEFT_BOTTOM} label="left-bottom" />
|
|
96
|
+
</>
|
|
97
|
+
),
|
|
98
|
+
...withRTLDiff({ renderAs: 'iframe', hAlign: 'right', vAlign: 'center' }),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const LeftFlipped: Story = {
|
|
102
|
+
render: () => (
|
|
103
|
+
<>
|
|
104
|
+
<PanelStory position={Position.LEFT_TOP} label="left-top" />
|
|
105
|
+
<PanelStory position={Position.LEFT} label="left" />
|
|
106
|
+
<PanelStory position={Position.LEFT_BOTTOM} label="left-bottom" />
|
|
107
|
+
</>
|
|
108
|
+
),
|
|
109
|
+
...withRTLDiff({ renderAs: 'iframe', hAlign: 'left', vAlign: 'center' }),
|
|
110
|
+
};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { Placement } from '@floating-ui/react';
|
|
2
|
-
|
|
3
1
|
export enum Position {
|
|
4
2
|
BOTTOM = 'bottom',
|
|
5
3
|
BOTTOM_LEFT = 'bottom-left',
|
|
@@ -21,51 +19,3 @@ export type PositionTop = 'top';
|
|
|
21
19
|
export type PositionRight = 'right';
|
|
22
20
|
export type PositionBottom = 'bottom';
|
|
23
21
|
export type PositionLeft = 'left';
|
|
24
|
-
|
|
25
|
-
export type PositionTopLeft = 'top-left';
|
|
26
|
-
export type PositionTopRight = 'top-right';
|
|
27
|
-
export type PositionBottomLeft = 'bottom-left';
|
|
28
|
-
export type PositionBottomRight = 'bottom-right';
|
|
29
|
-
export type PositionLeftTop = 'left-top';
|
|
30
|
-
export type PositionLeftBottom = 'left-bottom';
|
|
31
|
-
export type PositionRightTop = 'right-top';
|
|
32
|
-
export type PositionRightBottom = 'right-bottom';
|
|
33
|
-
|
|
34
|
-
/** Neptune position values that can be converted to floating-ui Placement */
|
|
35
|
-
export type FloatingPlacement =
|
|
36
|
-
| PositionTop
|
|
37
|
-
| PositionRight
|
|
38
|
-
| PositionBottom
|
|
39
|
-
| PositionLeft
|
|
40
|
-
| PositionTopLeft
|
|
41
|
-
| PositionTopRight
|
|
42
|
-
| PositionRightTop
|
|
43
|
-
| PositionRightBottom
|
|
44
|
-
| PositionBottomLeft
|
|
45
|
-
| PositionBottomRight
|
|
46
|
-
| PositionLeftTop
|
|
47
|
-
| PositionLeftBottom;
|
|
48
|
-
|
|
49
|
-
/** Maps Neptune position values to floating-ui Placement */
|
|
50
|
-
export const positionToPlacement = (position: FloatingPlacement): Placement => {
|
|
51
|
-
switch (position) {
|
|
52
|
-
case 'top-left':
|
|
53
|
-
return 'top-start';
|
|
54
|
-
case 'top-right':
|
|
55
|
-
return 'top-end';
|
|
56
|
-
case 'bottom-left':
|
|
57
|
-
return 'bottom-start';
|
|
58
|
-
case 'bottom-right':
|
|
59
|
-
return 'bottom-end';
|
|
60
|
-
case 'left-top':
|
|
61
|
-
return 'left-start';
|
|
62
|
-
case 'left-bottom':
|
|
63
|
-
return 'left-end';
|
|
64
|
-
case 'right-top':
|
|
65
|
-
return 'right-start';
|
|
66
|
-
case 'right-bottom':
|
|
67
|
-
return 'right-end';
|
|
68
|
-
default:
|
|
69
|
-
return position;
|
|
70
|
-
}
|
|
71
|
-
};
|