@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.
Files changed (36) hide show
  1. package/build/FlowNavigation/FlowNavigation.js +14 -5
  2. package/build/FlowNavigation/FlowNavigation.js.map +1 -1
  3. package/build/FlowNavigation/FlowNavigation.mjs +14 -5
  4. package/build/FlowNavigation/FlowNavigation.mjs.map +1 -1
  5. package/build/Sticky/Sticky.js +1 -0
  6. package/build/Sticky/Sticky.js.map +1 -1
  7. package/build/Sticky/Sticky.mjs +1 -0
  8. package/build/Sticky/Sticky.mjs.map +1 -1
  9. package/build/_deprecated/OverlayHeader/OverlayHeader.js +41 -6
  10. package/build/_deprecated/OverlayHeader/OverlayHeader.js.map +1 -1
  11. package/build/_deprecated/OverlayHeader/OverlayHeader.mjs +41 -6
  12. package/build/_deprecated/OverlayHeader/OverlayHeader.mjs.map +1 -1
  13. package/build/main.css +39 -8
  14. package/build/styles/FlowNavigation/FlowNavigation.css +13 -7
  15. package/build/styles/Sticky/Sticky.css +4 -0
  16. package/build/styles/_deprecated/OverlayHeader/OverlayHeader.css +15 -0
  17. package/build/styles/main.css +39 -8
  18. package/build/types/FlowNavigation/FlowNavigation.d.ts +9 -2
  19. package/build/types/FlowNavigation/FlowNavigation.d.ts.map +1 -1
  20. package/build/types/Sticky/Sticky.d.ts.map +1 -1
  21. package/build/types/_deprecated/OverlayHeader/OverlayHeader.d.ts.map +1 -1
  22. package/package.json +1 -1
  23. package/src/FlowNavigation/FlowNavigation.css +13 -7
  24. package/src/FlowNavigation/FlowNavigation.less +15 -6
  25. package/src/FlowNavigation/FlowNavigation.tsx +27 -6
  26. package/src/FlowNavigation/_stories/FlowNavigation.story.tsx +86 -0
  27. package/src/Sticky/Sticky.css +4 -0
  28. package/src/Sticky/Sticky.less +6 -0
  29. package/src/Sticky/Sticky.test.tsx +1 -0
  30. package/src/Sticky/Sticky.tsx +1 -0
  31. package/src/Sticky/_stories/Sticky.test.story.tsx +177 -0
  32. package/src/_deprecated/OverlayHeader/OverlayHeader.css +15 -0
  33. package/src/_deprecated/OverlayHeader/OverlayHeader.less +24 -0
  34. package/src/_deprecated/OverlayHeader/OverlayHeader.tsx +21 -2
  35. package/src/main.css +39 -8
  36. 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,CAiB1C,CAAC;AAEF,eAAe,MAAM,CAAC"}
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":"AAGA;;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,+BAEhG"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "0.0.0-experimental-bd48fa9",
3
+ "version": "0.0.0-experimental-6e333ba",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -4,12 +4,7 @@
4
4
  justify-content: center;
5
5
  width: 100%;
6
6
  box-sizing: border-box;
7
- min-height: 80px;
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: none;
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: 80px;
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: none;
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?: (() => void) | React.MouseEventHandler<HTMLButtonElement>;
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
- <Container
115
- size="fluid"
116
- className={clsx('np-flow-navigation', {
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
- </Container>
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}
@@ -0,0 +1,4 @@
1
+ .wds-sticky.sliding-panel--open-bottom {
2
+ padding-bottom: env(safe-area-inset-bottom, 0px);
3
+ padding-bottom: var(--wds-sticky-safe-area-inset-bottom, env(safe-area-inset-bottom, 0px));
4
+ }
@@ -0,0 +1,6 @@
1
+ .wds-sticky.sliding-panel--open-bottom {
2
+ padding-bottom: var(
3
+ --wds-sticky-safe-area-inset-bottom,
4
+ env(safe-area-inset-bottom, 0px)
5
+ );
6
+ }
@@ -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
  });
@@ -46,6 +46,7 @@ const Sticky: FunctionComponent<StickyProps> = ({
46
46
  }: StickyProps) => {
47
47
  return (
48
48
  <SlidingPanel
49
+ className="wds-sticky"
49
50
  testId={testId}
50
51
  open={open}
51
52
  position={position}
@@ -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
- return <FlowNavigation {...{ avatar, onClose, logo }} />;
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: 80px;
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: none;
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";