@transferwise/components 0.0.0-experimental-bd48fa9 → 0.0.0-experimental-6e333ba
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/FlowNavigation/FlowNavigation.js +14 -5
- package/build/FlowNavigation/FlowNavigation.js.map +1 -1
- package/build/FlowNavigation/FlowNavigation.mjs +14 -5
- package/build/FlowNavigation/FlowNavigation.mjs.map +1 -1
- package/build/Sticky/Sticky.js +1 -0
- package/build/Sticky/Sticky.js.map +1 -1
- package/build/Sticky/Sticky.mjs +1 -0
- package/build/Sticky/Sticky.mjs.map +1 -1
- package/build/_deprecated/OverlayHeader/OverlayHeader.js +41 -6
- package/build/_deprecated/OverlayHeader/OverlayHeader.js.map +1 -1
- package/build/_deprecated/OverlayHeader/OverlayHeader.mjs +41 -6
- package/build/_deprecated/OverlayHeader/OverlayHeader.mjs.map +1 -1
- package/build/main.css +39 -8
- package/build/styles/FlowNavigation/FlowNavigation.css +13 -7
- package/build/styles/Sticky/Sticky.css +4 -0
- package/build/styles/_deprecated/OverlayHeader/OverlayHeader.css +15 -0
- package/build/styles/main.css +39 -8
- package/build/types/FlowNavigation/FlowNavigation.d.ts +9 -2
- package/build/types/FlowNavigation/FlowNavigation.d.ts.map +1 -1
- package/build/types/Sticky/Sticky.d.ts.map +1 -1
- package/build/types/_deprecated/OverlayHeader/OverlayHeader.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/FlowNavigation/FlowNavigation.css +13 -7
- package/src/FlowNavigation/FlowNavigation.less +15 -6
- package/src/FlowNavigation/FlowNavigation.tsx +27 -6
- package/src/FlowNavigation/_stories/FlowNavigation.story.tsx +86 -0
- package/src/Sticky/Sticky.css +4 -0
- package/src/Sticky/Sticky.less +6 -0
- package/src/Sticky/Sticky.test.tsx +1 -0
- package/src/Sticky/Sticky.tsx +1 -0
- package/src/Sticky/_stories/Sticky.test.story.tsx +177 -0
- package/src/_deprecated/OverlayHeader/OverlayHeader.css +15 -0
- package/src/_deprecated/OverlayHeader/OverlayHeader.less +24 -0
- package/src/_deprecated/OverlayHeader/OverlayHeader.tsx +21 -2
- package/src/main.css +39 -8
- package/src/main.less +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sticky.d.ts","sourceRoot":"","sources":["../../../src/Sticky/Sticky.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE1C,MAAM,WAAW,WAAW;IAC1B,wFAAwF;IACxF,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B,yGAAyG;IACzG,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,yHAAyH;IACzH,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;IAEpD,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,MAAM,EAAE,iBAAiB,CAAC,WAAW,
|
|
1
|
+
{"version":3,"file":"Sticky.d.ts","sourceRoot":"","sources":["../../../src/Sticky/Sticky.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAE1C,MAAM,WAAW,WAAW;IAC1B,wFAAwF;IACxF,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAE3B,yGAAyG;IACzG,IAAI,CAAC,EAAE,OAAO,CAAC;IAEf,yHAAyH;IACzH,QAAQ,CAAC,EAAE,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;IAEpD,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,MAAM,EAAE,iBAAiB,CAAC,WAAW,CAkB1C,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverlayHeader.d.ts","sourceRoot":"","sources":["../../../../src/_deprecated/OverlayHeader/OverlayHeader.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"OverlayHeader.d.ts","sourceRoot":"","sources":["../../../../src/_deprecated/OverlayHeader/OverlayHeader.tsx"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,gBAAgB;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,gDAAgD;IAChD,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;CACtD;AAID;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAkB,EAAE,EAAE,kBAAkB,+BAiBhG"}
|
package/package.json
CHANGED
|
@@ -4,12 +4,7 @@
|
|
|
4
4
|
justify-content: center;
|
|
5
5
|
width: 100%;
|
|
6
6
|
box-sizing: border-box;
|
|
7
|
-
min-height:
|
|
8
|
-
}
|
|
9
|
-
@media (min-width: 320.02px) {
|
|
10
|
-
.np-flow-navigation {
|
|
11
|
-
min-height: 128px;
|
|
12
|
-
}
|
|
7
|
+
min-height: 96px;
|
|
13
8
|
}
|
|
14
9
|
.np-flow-navigation--border-bottom {
|
|
15
10
|
border-bottom: 1px solid rgba(0,0,0,0.10196);
|
|
@@ -18,7 +13,7 @@
|
|
|
18
13
|
.np-flow-navigation__content {
|
|
19
14
|
width: 100%;
|
|
20
15
|
min-width: 320px;
|
|
21
|
-
max-width:
|
|
16
|
+
max-width: calc(1160px - 36px);
|
|
22
17
|
}
|
|
23
18
|
.np-flow-navigation__stepper {
|
|
24
19
|
padding-bottom: 0 !important;
|
|
@@ -77,6 +72,17 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
77
72
|
.np-flow-navigation--hidden {
|
|
78
73
|
visibility: hidden;
|
|
79
74
|
}
|
|
75
|
+
.np-flow-navigation--composable {
|
|
76
|
+
min-height: 80px;
|
|
77
|
+
}
|
|
78
|
+
@media (min-width: 320.02px) {
|
|
79
|
+
.np-flow-navigation--composable {
|
|
80
|
+
min-height: 128px;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
.np-flow-navigation--composable .np-flow-navigation__content {
|
|
84
|
+
max-width: none;
|
|
85
|
+
}
|
|
80
86
|
.np-theme-personal--forest-green .np-flow-navigation .np-flow-header__left path,
|
|
81
87
|
.np-theme-personal--bright-green .np-flow-navigation .np-flow-header__left path,
|
|
82
88
|
.np-theme-personal--dark .np-flow-navigation .np-flow-header__left path {
|
|
@@ -9,11 +9,7 @@
|
|
|
9
9
|
box-sizing: border-box;
|
|
10
10
|
|
|
11
11
|
// This is the natural height of FlowNavigation when the Avatar is displayed.
|
|
12
|
-
min-height:
|
|
13
|
-
|
|
14
|
-
@media (--screen-xs) {
|
|
15
|
-
min-height: 128px;
|
|
16
|
-
}
|
|
12
|
+
min-height: 96px;
|
|
17
13
|
|
|
18
14
|
&--border-bottom {
|
|
19
15
|
border-bottom: 1px solid var(--color-border-neutral);
|
|
@@ -22,7 +18,7 @@
|
|
|
22
18
|
&__content {
|
|
23
19
|
width: 100%;
|
|
24
20
|
min-width: 320px;
|
|
25
|
-
max-width:
|
|
21
|
+
max-width: calc(@screen-lg-min - 36px);
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
&__stepper {
|
|
@@ -73,6 +69,19 @@
|
|
|
73
69
|
visibility: hidden;
|
|
74
70
|
}
|
|
75
71
|
|
|
72
|
+
&--composable {
|
|
73
|
+
min-height: 80px;
|
|
74
|
+
|
|
75
|
+
@media (--screen-xs) {
|
|
76
|
+
min-height: 128px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.np-flow-navigation__content {
|
|
80
|
+
// Remove max-width constraint for fluid layout
|
|
81
|
+
max-width: none;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
76
85
|
.np-theme-personal--forest-green &,
|
|
77
86
|
.np-theme-personal--bright-green &,
|
|
78
87
|
.np-theme-personal--dark & {
|
|
@@ -26,13 +26,20 @@ export interface FlowNavigationProps {
|
|
|
26
26
|
/** @default false */
|
|
27
27
|
done?: boolean;
|
|
28
28
|
/** Called when the close button is clicked. If not provided the close button won't show */
|
|
29
|
-
onClose?: (
|
|
29
|
+
onClose?: () => void;
|
|
30
30
|
/** Called when the back button is clicked. If not provided the back button won't show. The back button only shows on small screens */
|
|
31
31
|
onGoBack?: () => void;
|
|
32
32
|
/** Steps to be displayed in stepper. If not provided, the stepper won't show. */
|
|
33
33
|
steps?: readonly Step[];
|
|
34
|
+
/**
|
|
35
|
+
* When true, renders in composable mode using a fluid Container.
|
|
36
|
+
* This allows the FlowNavigation to be used within custom layout containers.
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
composable?: boolean;
|
|
34
40
|
/**
|
|
35
41
|
* Controls whether the bottom border is displayed.
|
|
42
|
+
* Only applies to the composable variant. Non-composable variant always uses the done state.
|
|
36
43
|
* @default true
|
|
37
44
|
*/
|
|
38
45
|
showBottomBorder?: boolean;
|
|
@@ -46,6 +53,7 @@ const FlowNavigation = ({
|
|
|
46
53
|
onClose,
|
|
47
54
|
onGoBack,
|
|
48
55
|
steps,
|
|
56
|
+
composable = false,
|
|
49
57
|
showBottomBorder = true,
|
|
50
58
|
}: FlowNavigationProps) => {
|
|
51
59
|
const intl = useIntl();
|
|
@@ -61,6 +69,7 @@ const FlowNavigation = ({
|
|
|
61
69
|
<FlowHeader
|
|
62
70
|
className={clsx(
|
|
63
71
|
'np-flow-navigation__content',
|
|
72
|
+
!composable && 'p-x-3',
|
|
64
73
|
screenSm == null
|
|
65
74
|
? 'np-flow-navigation--hidden'
|
|
66
75
|
: {
|
|
@@ -110,15 +119,27 @@ const FlowNavigation = ({
|
|
|
110
119
|
/>
|
|
111
120
|
);
|
|
112
121
|
|
|
122
|
+
if (composable) {
|
|
123
|
+
return (
|
|
124
|
+
<Container
|
|
125
|
+
size="fluid"
|
|
126
|
+
className={clsx('np-flow-navigation np-flow-navigation--composable', {
|
|
127
|
+
'np-flow-navigation--border-bottom': showBottomBorder,
|
|
128
|
+
})}
|
|
129
|
+
>
|
|
130
|
+
{flowHeaderContent}
|
|
131
|
+
</Container>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
113
135
|
return (
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
'np-flow-navigation--border-bottom': showBottomBorder,
|
|
136
|
+
<div
|
|
137
|
+
className={clsx('np-flow-navigation p-y-3', {
|
|
138
|
+
'np-flow-navigation--border-bottom': !done,
|
|
118
139
|
})}
|
|
119
140
|
>
|
|
120
141
|
{flowHeaderContent}
|
|
121
|
-
</
|
|
142
|
+
</div>
|
|
122
143
|
);
|
|
123
144
|
};
|
|
124
145
|
|
|
@@ -384,6 +384,89 @@ export const WithOverlayHeaderComparison: Story = {
|
|
|
384
384
|
},
|
|
385
385
|
};
|
|
386
386
|
|
|
387
|
+
export const ComposableVariant: Story = {
|
|
388
|
+
args: {
|
|
389
|
+
profileType: ProfileType.PERSONAL,
|
|
390
|
+
showCloseButton: true,
|
|
391
|
+
showMobileBackButton: true,
|
|
392
|
+
done: false,
|
|
393
|
+
avatarURL: '../tapestry-01.png',
|
|
394
|
+
},
|
|
395
|
+
parameters: {
|
|
396
|
+
padding: '0',
|
|
397
|
+
chromatic: {
|
|
398
|
+
viewports,
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
render: (args) => {
|
|
402
|
+
const [activeStep, setActiveStep] = useState(2);
|
|
403
|
+
const steps = [
|
|
404
|
+
{
|
|
405
|
+
label: 'Recipient',
|
|
406
|
+
onClick: () => setActiveStep(0),
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
label: 'Amount',
|
|
410
|
+
hoverLabel: (
|
|
411
|
+
<>
|
|
412
|
+
You send 100 GBP <br />
|
|
413
|
+
You get 99.39 GBP{' '}
|
|
414
|
+
</>
|
|
415
|
+
),
|
|
416
|
+
...(activeStep > 1 && { onClick: () => setActiveStep(1) }),
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
label: 'Review',
|
|
420
|
+
...(activeStep > 2 && { onClick: () => setActiveStep(2) }),
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
label: 'Pay',
|
|
424
|
+
...(activeStep > 3 && { onClick: () => setActiveStep(3) }),
|
|
425
|
+
},
|
|
426
|
+
];
|
|
427
|
+
return (
|
|
428
|
+
<>
|
|
429
|
+
<FlowNavigation
|
|
430
|
+
composable
|
|
431
|
+
avatar={<AvatarView profileType={args.profileType} />}
|
|
432
|
+
logo={<Logo />}
|
|
433
|
+
activeStep={activeStep}
|
|
434
|
+
steps={steps}
|
|
435
|
+
onClose={args.showCloseButton ? () => alert('close') : undefined}
|
|
436
|
+
onGoBack={
|
|
437
|
+
args.showMobileBackButton
|
|
438
|
+
? () => setActiveStep(activeStep > 0 ? activeStep - 1 : 0)
|
|
439
|
+
: undefined
|
|
440
|
+
}
|
|
441
|
+
/>
|
|
442
|
+
<Container size="narrow">
|
|
443
|
+
<Body className="m-a-3">
|
|
444
|
+
<Display type={Typography.DISPLAY_SMALL}>{steps[activeStep].label} Step</Display>
|
|
445
|
+
<br />
|
|
446
|
+
{lorem10}
|
|
447
|
+
</Body>
|
|
448
|
+
</Container>
|
|
449
|
+
|
|
450
|
+
<Sticky>
|
|
451
|
+
<Container
|
|
452
|
+
size="narrow"
|
|
453
|
+
className="d-flex justify-content-center align-items-center p-y-3"
|
|
454
|
+
>
|
|
455
|
+
<Button
|
|
456
|
+
v2
|
|
457
|
+
disabled={activeStep === 3}
|
|
458
|
+
block
|
|
459
|
+
onClick={() => setActiveStep(activeStep + 1)}
|
|
460
|
+
>
|
|
461
|
+
Continue
|
|
462
|
+
</Button>
|
|
463
|
+
</Container>
|
|
464
|
+
</Sticky>
|
|
465
|
+
</>
|
|
466
|
+
);
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
|
|
387
470
|
export const WithoutStepper: Story = {
|
|
388
471
|
args: {
|
|
389
472
|
profileType: ProfileType.PERSONAL,
|
|
@@ -492,6 +575,7 @@ export const BorderControl: Story = {
|
|
|
492
575
|
|
|
493
576
|
{/* Composable: showBottomBorder=true, done=false - SHOWS BORDER */}
|
|
494
577
|
<FlowNavigation
|
|
578
|
+
composable
|
|
495
579
|
avatar={<AvatarView profileType={args.profileType} />}
|
|
496
580
|
logo={<Logo />}
|
|
497
581
|
activeStep={activeStep}
|
|
@@ -507,6 +591,7 @@ export const BorderControl: Story = {
|
|
|
507
591
|
|
|
508
592
|
{/* Composable: showBottomBorder=true, done=true - SHOWS BORDER */}
|
|
509
593
|
<FlowNavigation
|
|
594
|
+
composable
|
|
510
595
|
avatar={<AvatarView profileType={args.profileType} />}
|
|
511
596
|
logo={<Logo />}
|
|
512
597
|
activeStep={activeStep}
|
|
@@ -522,6 +607,7 @@ export const BorderControl: Story = {
|
|
|
522
607
|
|
|
523
608
|
{/* Composable: showBottomBorder=false, done=false - HIDES BORDER */}
|
|
524
609
|
<FlowNavigation
|
|
610
|
+
composable
|
|
525
611
|
avatar={<AvatarView profileType={args.profileType} />}
|
|
526
612
|
logo={<Logo />}
|
|
527
613
|
activeStep={activeStep}
|
|
@@ -21,6 +21,7 @@ describe('Sticky', () => {
|
|
|
21
21
|
|
|
22
22
|
test('renders with default props', () => {
|
|
23
23
|
expect(screen.getByTestId('test-sticky-panel')).toBeVisible();
|
|
24
|
+
expect(screen.getByTestId('test-sticky-panel')).toHaveClass('wds-sticky');
|
|
24
25
|
expect(screen.getByTestId('test-sticky-panel')).toHaveClass('sliding-panel--open-bottom');
|
|
25
26
|
expect(screen.getByText('Test Content')).toBeInTheDocument();
|
|
26
27
|
});
|
package/src/Sticky/Sticky.tsx
CHANGED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import { Card, Home, Payments, Recipients } from '@transferwise/icons';
|
|
3
|
+
import type { CSSProperties } from 'react';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { expect, userEvent, within } from 'storybook/test';
|
|
6
|
+
|
|
7
|
+
import { withVariantConfig } from '../../../.storybook/helpers';
|
|
8
|
+
import Button from '../../Button';
|
|
9
|
+
import { wait } from '../../test-utils/wait';
|
|
10
|
+
import Sticky from '../Sticky';
|
|
11
|
+
|
|
12
|
+
const SAFE_AREA_INSET = 64;
|
|
13
|
+
const DEMO_HEIGHT = 400;
|
|
14
|
+
const DEMO_WIDTH = 390;
|
|
15
|
+
const SAFE_AREA_VIEWPORT = 'safe-area-demo';
|
|
16
|
+
|
|
17
|
+
type SafeAreaDemoStyle = CSSProperties & {
|
|
18
|
+
'--wds-sticky-safe-area-inset-bottom': string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const safeAreaDemoStyle: SafeAreaDemoStyle = {
|
|
22
|
+
'--wds-sticky-safe-area-inset-bottom': `${SAFE_AREA_INSET}px`,
|
|
23
|
+
background: 'var(--color-background-screen)',
|
|
24
|
+
height: DEMO_HEIGHT,
|
|
25
|
+
overflow: 'hidden',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const navigationItems = [
|
|
29
|
+
{ Icon: Home, label: 'Home' },
|
|
30
|
+
{ Icon: Card, label: 'Card' },
|
|
31
|
+
{ Icon: Recipients, label: 'Recipients' },
|
|
32
|
+
{ Icon: Payments, label: 'Payments' },
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
function SafeAreaBottomBar() {
|
|
36
|
+
return (
|
|
37
|
+
<nav
|
|
38
|
+
aria-label="Safe area bottom bar"
|
|
39
|
+
data-testid="safe-area-bottom-bar"
|
|
40
|
+
style={{
|
|
41
|
+
alignItems: 'center',
|
|
42
|
+
background: 'var(--color-background-elevated)',
|
|
43
|
+
borderTop: '1px solid var(--color-border-neutral)',
|
|
44
|
+
bottom: 0,
|
|
45
|
+
display: 'flex',
|
|
46
|
+
height: SAFE_AREA_INSET,
|
|
47
|
+
justifyContent: 'space-around',
|
|
48
|
+
left: 0,
|
|
49
|
+
padding: '0 8px',
|
|
50
|
+
position: 'fixed',
|
|
51
|
+
right: 0,
|
|
52
|
+
zIndex: 1031,
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
{navigationItems.map(({ Icon, label }) => (
|
|
56
|
+
<span
|
|
57
|
+
key={label}
|
|
58
|
+
style={{
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
color: 'var(--color-content-primary)',
|
|
61
|
+
display: 'flex',
|
|
62
|
+
flexDirection: 'column',
|
|
63
|
+
fontSize: 11,
|
|
64
|
+
gap: 2,
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<Icon size={24} />
|
|
68
|
+
{label}
|
|
69
|
+
</span>
|
|
70
|
+
))}
|
|
71
|
+
</nav>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function SafeAreaDemo() {
|
|
76
|
+
const [open, setOpen] = useState(false);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<main style={safeAreaDemoStyle}>
|
|
80
|
+
<section style={{ maxWidth: 520, padding: 32 }}>
|
|
81
|
+
<h1 style={{ fontSize: 32, lineHeight: 1.15, margin: '0 0 16px' }}>
|
|
82
|
+
Sticky respects safe area at bottom of screen
|
|
83
|
+
</h1>
|
|
84
|
+
<Button v2 onClick={() => setOpen((currentOpen) => !currentOpen)}>
|
|
85
|
+
{open ? 'Hide Sticky' : 'Show Sticky'}
|
|
86
|
+
</Button>
|
|
87
|
+
</section>
|
|
88
|
+
|
|
89
|
+
<SafeAreaBottomBar />
|
|
90
|
+
|
|
91
|
+
<Sticky open={open} position="bottom" testId="safe-area-sticky">
|
|
92
|
+
<div className="p-a-2" data-testid="sticky-content">
|
|
93
|
+
Sticky
|
|
94
|
+
</div>
|
|
95
|
+
</Sticky>
|
|
96
|
+
</main>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const meta = {
|
|
101
|
+
component: Sticky,
|
|
102
|
+
parameters: {
|
|
103
|
+
controls: { disable: true },
|
|
104
|
+
layout: 'fullscreen',
|
|
105
|
+
},
|
|
106
|
+
tags: ['!autodocs', '!manifest'],
|
|
107
|
+
title: 'Dialogs/Sticky/Tests',
|
|
108
|
+
} satisfies Meta<typeof Sticky>;
|
|
109
|
+
|
|
110
|
+
export default meta;
|
|
111
|
+
|
|
112
|
+
type Story = StoryObj<typeof Sticky>;
|
|
113
|
+
|
|
114
|
+
const SafeAreaRegression: Story = {
|
|
115
|
+
parameters: {
|
|
116
|
+
chromatic: { delay: 500 },
|
|
117
|
+
viewport: {
|
|
118
|
+
options: {
|
|
119
|
+
[SAFE_AREA_VIEWPORT]: {
|
|
120
|
+
name: 'Safe area demo',
|
|
121
|
+
styles: { height: `${DEMO_HEIGHT}px`, width: `${DEMO_WIDTH}px` },
|
|
122
|
+
type: 'mobile',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
globals: {
|
|
128
|
+
viewport: SAFE_AREA_VIEWPORT,
|
|
129
|
+
},
|
|
130
|
+
play: async ({ canvasElement, step }) => {
|
|
131
|
+
const canvas = within(canvasElement);
|
|
132
|
+
|
|
133
|
+
const expectStickyAboveSafeArea = async () => {
|
|
134
|
+
await wait(400);
|
|
135
|
+
|
|
136
|
+
const sticky = canvas.getByTestId('safe-area-sticky');
|
|
137
|
+
const stickyContent = canvas.getByTestId('sticky-content');
|
|
138
|
+
const safeArea = canvas.getByTestId('safe-area-bottom-bar');
|
|
139
|
+
|
|
140
|
+
await expect(sticky).toBeVisible();
|
|
141
|
+
await expect(window.getComputedStyle(sticky).paddingBottom).toBe(`${SAFE_AREA_INSET}px`);
|
|
142
|
+
await expect(Math.round(stickyContent.getBoundingClientRect().bottom)).toBe(
|
|
143
|
+
Math.round(safeArea.getBoundingClientRect().top),
|
|
144
|
+
);
|
|
145
|
+
await expect(stickyContent.getBoundingClientRect().bottom).toBeLessThanOrEqual(
|
|
146
|
+
safeArea.getBoundingClientRect().top,
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
await step('Open Sticky from the top button', async () => {
|
|
151
|
+
await userEvent.click(canvas.getByRole('button', { name: /^show sticky$/i }));
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
await step('Keep Sticky above the bottom safe area', expectStickyAboveSafeArea);
|
|
155
|
+
|
|
156
|
+
await step('Toggle Sticky closed and open again', async () => {
|
|
157
|
+
const hideStickyButton = canvas.getByRole('button', { name: /^hide sticky$/i });
|
|
158
|
+
|
|
159
|
+
await userEvent.click(hideStickyButton);
|
|
160
|
+
await wait(400);
|
|
161
|
+
await expect(canvas.queryByTestId('safe-area-sticky')).not.toBeInTheDocument();
|
|
162
|
+
|
|
163
|
+
await userEvent.click(canvas.getByRole('button', { name: /^show sticky$/i }));
|
|
164
|
+
await expectStickyAboveSafeArea();
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
render: () => <SafeAreaDemo />,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Reproduces the MobileNav replacement from DS-7193 with a fixed bottom safe area.
|
|
172
|
+
* It verifies that Sticky clears the inset.
|
|
173
|
+
*/
|
|
174
|
+
export const MobileNavSafeArea: Story = {
|
|
175
|
+
...SafeAreaRegression,
|
|
176
|
+
...withVariantConfig(['mobile'], SafeAreaRegression),
|
|
177
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.np-overlay-header .np-overlay-header__content {
|
|
2
|
+
min-height: 97px;
|
|
3
|
+
width: 100%;
|
|
4
|
+
min-width: 320px;
|
|
5
|
+
max-width: calc(1160px - 36px);
|
|
6
|
+
}
|
|
7
|
+
.np-theme-personal .np-overlay-header {
|
|
8
|
+
border-bottom: 1px solid rgba(0,0,0,0.10196);
|
|
9
|
+
border-bottom: 1px solid var(--color-border-neutral);
|
|
10
|
+
}
|
|
11
|
+
.np-theme-personal--forest-green .np-overlay-header .np-flow-header__left path,
|
|
12
|
+
.np-theme-personal--bright-green .np-overlay-header .np-flow-header__left path,
|
|
13
|
+
.np-theme-personal--dark .np-overlay-header .np-flow-header__left path {
|
|
14
|
+
fill: var(--color-interactive-primary);
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
@import (reference) "@transferwise/neptune-tokens/breakpoints.less";
|
|
2
|
+
@import (reference) "./../../styles/less/addons/_spacing-utilities.less";
|
|
3
|
+
|
|
4
|
+
.np-overlay-header {
|
|
5
|
+
.np-overlay-header__content {
|
|
6
|
+
// Same height and width as in FlowNavigation
|
|
7
|
+
min-height: 97px;
|
|
8
|
+
width: 100%;
|
|
9
|
+
min-width: @screen-xs-min;
|
|
10
|
+
max-width: calc(@screen-lg-min - 36px);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.np-theme-personal & {
|
|
14
|
+
border-bottom: 1px solid var(--color-border-neutral);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.np-theme-personal--forest-green &,
|
|
18
|
+
.np-theme-personal--bright-green &,
|
|
19
|
+
.np-theme-personal--dark & {
|
|
20
|
+
.np-flow-header__left path {
|
|
21
|
+
fill: var(--color-interactive-primary);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
|
|
3
|
+
import { Size } from '../../common';
|
|
4
|
+
import { CloseButton } from '../../common/CloseButton';
|
|
5
|
+
import FlowHeader from '../../common/FlowHeader';
|
|
1
6
|
import Logo from '../../Logo';
|
|
2
|
-
import FlowNavigation from '../../FlowNavigation';
|
|
3
7
|
|
|
4
8
|
/**
|
|
5
9
|
* @deprecated Use `FlowNavigation` component instead, with `steps={[]}`
|
|
@@ -18,5 +22,20 @@ const defaultLogo = <Logo />;
|
|
|
18
22
|
* @deprecated Use `FlowNavigation` component instead
|
|
19
23
|
*/
|
|
20
24
|
export default function OverlayHeader({ avatar, onClose, logo = defaultLogo }: OverlayHeaderProps) {
|
|
21
|
-
|
|
25
|
+
const closeButton = onClose && <CloseButton size={Size.LARGE} onClick={onClose} />;
|
|
26
|
+
return (
|
|
27
|
+
<div className="np-overlay-header d-flex justify-content-center">
|
|
28
|
+
<FlowHeader
|
|
29
|
+
className="np-overlay-header__content p-a-3"
|
|
30
|
+
leftContent={logo}
|
|
31
|
+
rightContent={
|
|
32
|
+
<div className={clsx('d-flex', 'align-items-center', 'order-2')}>
|
|
33
|
+
{avatar}
|
|
34
|
+
{avatar && closeButton && <span className={clsx('m-x-1')} />}
|
|
35
|
+
{closeButton}
|
|
36
|
+
</div>
|
|
37
|
+
}
|
|
38
|
+
/>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
22
41
|
}
|
package/src/main.css
CHANGED
|
@@ -28933,13 +28933,7 @@ button.np-option {
|
|
|
28933
28933
|
justify-content: center;
|
|
28934
28934
|
width: 100%;
|
|
28935
28935
|
box-sizing: border-box;
|
|
28936
|
-
min-height:
|
|
28937
|
-
}
|
|
28938
|
-
|
|
28939
|
-
@media (min-width: 320.02px) {
|
|
28940
|
-
.np-flow-navigation {
|
|
28941
|
-
min-height: 128px;
|
|
28942
|
-
}
|
|
28936
|
+
min-height: 96px;
|
|
28943
28937
|
}
|
|
28944
28938
|
|
|
28945
28939
|
.np-flow-navigation--border-bottom {
|
|
@@ -28950,7 +28944,7 @@ button.np-option {
|
|
|
28950
28944
|
.np-flow-navigation__content {
|
|
28951
28945
|
width: 100%;
|
|
28952
28946
|
min-width: 320px;
|
|
28953
|
-
max-width:
|
|
28947
|
+
max-width: calc(1160px - 36px);
|
|
28954
28948
|
}
|
|
28955
28949
|
|
|
28956
28950
|
.np-flow-navigation__stepper {
|
|
@@ -29027,6 +29021,20 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
29027
29021
|
visibility: hidden;
|
|
29028
29022
|
}
|
|
29029
29023
|
|
|
29024
|
+
.np-flow-navigation--composable {
|
|
29025
|
+
min-height: 80px;
|
|
29026
|
+
}
|
|
29027
|
+
|
|
29028
|
+
@media (min-width: 320.02px) {
|
|
29029
|
+
.np-flow-navigation--composable {
|
|
29030
|
+
min-height: 128px;
|
|
29031
|
+
}
|
|
29032
|
+
}
|
|
29033
|
+
|
|
29034
|
+
.np-flow-navigation--composable .np-flow-navigation__content {
|
|
29035
|
+
max-width: none;
|
|
29036
|
+
}
|
|
29037
|
+
|
|
29030
29038
|
.np-theme-personal--forest-green .np-flow-navigation .np-flow-header__left path,
|
|
29031
29039
|
.np-theme-personal--bright-green .np-flow-navigation .np-flow-header__left path,
|
|
29032
29040
|
.np-theme-personal--dark .np-flow-navigation .np-flow-header__left path {
|
|
@@ -31527,6 +31535,24 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
31527
31535
|
text-decoration: var(--nudge-link-text-decoration);
|
|
31528
31536
|
}
|
|
31529
31537
|
|
|
31538
|
+
.np-overlay-header .np-overlay-header__content {
|
|
31539
|
+
min-height: 97px;
|
|
31540
|
+
width: 100%;
|
|
31541
|
+
min-width: 320px;
|
|
31542
|
+
max-width: calc(1160px - 36px);
|
|
31543
|
+
}
|
|
31544
|
+
|
|
31545
|
+
.np-theme-personal .np-overlay-header {
|
|
31546
|
+
border-bottom: 1px solid rgba(0,0,0,0.10196);
|
|
31547
|
+
border-bottom: 1px solid var(--color-border-neutral);
|
|
31548
|
+
}
|
|
31549
|
+
|
|
31550
|
+
.np-theme-personal--forest-green .np-overlay-header .np-flow-header__left path,
|
|
31551
|
+
.np-theme-personal--bright-green .np-overlay-header .np-flow-header__left path,
|
|
31552
|
+
.np-theme-personal--dark .np-overlay-header .np-flow-header__left path {
|
|
31553
|
+
fill: var(--color-interactive-primary);
|
|
31554
|
+
}
|
|
31555
|
+
|
|
31530
31556
|
.tw-telephone {
|
|
31531
31557
|
display: flex;
|
|
31532
31558
|
}
|
|
@@ -32686,6 +32712,11 @@ html:not([dir="rtl"]) .np-navigation-option {
|
|
|
32686
32712
|
will-change: width;
|
|
32687
32713
|
}
|
|
32688
32714
|
|
|
32715
|
+
.wds-sticky.sliding-panel--open-bottom {
|
|
32716
|
+
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
32717
|
+
padding-bottom: var(--wds-sticky-safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
|
|
32718
|
+
}
|
|
32719
|
+
|
|
32689
32720
|
.np-select .np-dropdown-toggle {
|
|
32690
32721
|
background-color: #ffffff;
|
|
32691
32722
|
background-color: var(--color-background-screen);
|
package/src/main.less
CHANGED
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
@import "./_deprecated/NavigationOption/NavigationOption.less";
|
|
63
63
|
@import "./_deprecated/NavigationOptionsList/NavigationOptionsList.less";
|
|
64
64
|
@import "./Nudge/Nudge.less";
|
|
65
|
+
@import "./_deprecated/OverlayHeader/OverlayHeader.less";
|
|
65
66
|
@import "./PhoneNumberInput/PhoneNumberInput.less";
|
|
66
67
|
@import "./Popover/Popover.less";
|
|
67
68
|
@import "./PromoCard/PromoCard.less";
|
|
@@ -77,6 +78,7 @@
|
|
|
77
78
|
@import "./Snackbar/Snackbar.less";
|
|
78
79
|
@import "./StatusIcon/StatusIcon.less";
|
|
79
80
|
@import "./Stepper/Stepper.less";
|
|
81
|
+
@import "./Sticky/Sticky.less";
|
|
80
82
|
@import "./_deprecated/Select/Select.less";
|
|
81
83
|
@import "./SegmentedControl/SegmentedControl.less";
|
|
82
84
|
@import "./_deprecated/Summary/Summary.less";
|