@transferwise/components 46.148.1 → 46.149.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.
Files changed (77) hide show
  1. package/build/flowNavigation/FlowNavigation.js +56 -42
  2. package/build/flowNavigation/FlowNavigation.js.map +1 -1
  3. package/build/flowNavigation/FlowNavigation.mjs +56 -42
  4. package/build/flowNavigation/FlowNavigation.mjs.map +1 -1
  5. package/build/main.css +31 -14
  6. package/build/overlayHeader/OverlayHeader.js +3 -0
  7. package/build/overlayHeader/OverlayHeader.js.map +1 -1
  8. package/build/overlayHeader/OverlayHeader.mjs +3 -0
  9. package/build/overlayHeader/OverlayHeader.mjs.map +1 -1
  10. package/build/prompt/CriticalBanner/CriticalBanner.js +1 -1
  11. package/build/prompt/CriticalBanner/CriticalBanner.js.map +1 -1
  12. package/build/prompt/CriticalBanner/CriticalBanner.mjs +1 -1
  13. package/build/prompt/CriticalBanner/CriticalBanner.mjs.map +1 -1
  14. package/build/styles/css/neptune.css +11 -11
  15. package/build/styles/flowNavigation/FlowNavigation.css +16 -2
  16. package/build/styles/less/legacy-variables.less +1 -1
  17. package/build/styles/less/neptune-tokens.less +2 -2
  18. package/build/styles/main.css +31 -14
  19. package/build/styles/props/custom-media.css +1 -1
  20. package/build/styles/props/neptune-tokens.css +1 -1
  21. package/build/styles/sentimentSurface/SentimentSurface.css +1 -1
  22. package/build/styles/styles/less/neptune.css +11 -11
  23. package/build/types/flowNavigation/FlowNavigation.d.ts +15 -3
  24. package/build/types/flowNavigation/FlowNavigation.d.ts.map +1 -1
  25. package/build/types/overlayHeader/OverlayHeader.d.ts +6 -0
  26. package/build/types/overlayHeader/OverlayHeader.d.ts.map +1 -1
  27. package/package.json +15 -15
  28. package/src/body/Body.story.tsx +1 -5
  29. package/src/calendar/Calendar.story.tsx +1 -4
  30. package/src/checkbox/Checkbox.story.tsx +1 -4
  31. package/src/chevron/Chevron.story.tsx +1 -1
  32. package/src/container/Container.story.tsx +1 -4
  33. package/src/dateInput/DateInput.story.tsx +1 -4
  34. package/src/dateLookup/DateLookup.story.tsx +1 -4
  35. package/src/decision/Decision.story.tsx +1 -4
  36. package/src/definitionList/DefinitionList.story.tsx +1 -4
  37. package/src/dimmer/Dimmer.story.tsx +1 -5
  38. package/src/display/Display.story.tsx +1 -4
  39. package/src/emphasis/Emphasis.story.tsx +1 -5
  40. package/src/expressiveMoneyInput/ExpressiveMoneyInput.story.tsx +1 -4
  41. package/src/field/Field.story.tsx +1 -4
  42. package/src/flowNavigation/FlowNavigation.css +16 -2
  43. package/src/flowNavigation/FlowNavigation.less +20 -4
  44. package/src/flowNavigation/FlowNavigation.story.tsx +249 -0
  45. package/src/flowNavigation/FlowNavigation.tsx +91 -58
  46. package/src/header/Header.story.tsx +1 -4
  47. package/src/inputWithDisplayFormat/InputWithDisplayFormat.story.tsx +1 -4
  48. package/src/inputs/InputGroup.story.tsx +1 -4
  49. package/src/inputs/SearchInput.story.tsx +1 -4
  50. package/src/inputs/TextArea.story.tsx +1 -4
  51. package/src/instructionsList/InstructionsList.story.tsx +1 -4
  52. package/src/label/Label.story.tsx +1 -4
  53. package/src/link/Link.story.tsx +1 -4
  54. package/src/loader/Loader.story.tsx +1 -1
  55. package/src/main.css +31 -14
  56. package/src/markdown/Markdown.story.tsx +1 -5
  57. package/src/money/Money.story.tsx +0 -1
  58. package/src/moneyInput/MoneyInput.story.tsx +1 -4
  59. package/src/overlayHeader/OverlayHeader.story.tsx +4 -0
  60. package/src/overlayHeader/OverlayHeader.tsx +6 -0
  61. package/src/phoneNumberInput/PhoneNumberInput.story.tsx +1 -4
  62. package/src/promoCard/PromoCard.story.tsx +1 -4
  63. package/src/promoCard/PromoCardGroup.story.tsx +1 -4
  64. package/src/prompt/CriticalBanner/CriticalBanner.story.tsx +1 -1
  65. package/src/prompt/CriticalBanner/CriticalBanner.tsx +1 -1
  66. package/src/radio/Radio.story.tsx +1 -4
  67. package/src/radioGroup/RadioGroup.story.tsx +1 -4
  68. package/src/segmentedControl/SegmentedControl.story.tsx +1 -4
  69. package/src/sentimentSurface/SentimentSurface.css +1 -1
  70. package/src/statusIcon/StatusIcon.story.tsx +1 -1
  71. package/src/styles/less/neptune.css +11 -11
  72. package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +1 -4
  73. package/src/title/Title.story.tsx +1 -5
  74. package/src/typeahead/Typeahead.story.tsx +1 -4
  75. package/src/upload/Upload.story.tsx +1 -4
  76. package/src/uploadInput/UploadInput.story.tsx +1 -4
  77. package/src/flowNavigation/FlowNavigation.test.js +0 -190
@@ -1,10 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { fn } from 'storybook/test';
3
3
 
4
- import {
5
- createSandboxStory,
6
- globalScope,
7
- } from '../../.storybook/components/sandbox/SandboxEditor';
4
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
8
5
  import { Field } from '../field/Field';
9
6
 
10
7
  import Checkbox from './Checkbox';
@@ -12,7 +12,7 @@ type Story = StoryObj<typeof Chevron>;
12
12
 
13
13
  export const Sandbox = createSandboxStory({
14
14
  code: `<Chevron orientation="bottom" size="md" />`,
15
- scope: {Chevron},
15
+ scope: { Chevron },
16
16
  });
17
17
 
18
18
  export const Basic: Story = {
@@ -4,10 +4,7 @@ import Body from '../body';
4
4
  import Title from '../title';
5
5
 
6
6
  import Container from './Container';
7
- import {
8
- createSandboxStory,
9
- globalScope,
10
- } from '../../.storybook/components/sandbox/SandboxEditor';
7
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
11
8
 
12
9
  export default {
13
10
  component: Container,
@@ -1,10 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { fn } from 'storybook/test';
3
3
 
4
- import {
5
- createSandboxStory,
6
- globalScope,
7
- } from '../../.storybook/components/sandbox/SandboxEditor';
4
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
8
5
  import { DateInput } from '..';
9
6
 
10
7
  const meta: Meta<typeof DateInput> = {
@@ -1,9 +1,6 @@
1
1
  import { useState } from 'react';
2
2
  import { StoryObj } from '@storybook/react-webpack5';
3
- import {
4
- createSandboxStory,
5
- globalScope,
6
- } from '../../.storybook/components/sandbox/SandboxEditor';
3
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
7
4
  import { Field } from '..';
8
5
  import { Size } from '../common';
9
6
  import DateLookup, { type DateLookupProps } from './DateLookup';
@@ -3,10 +3,7 @@ import { fn } from 'storybook/test';
3
3
  import { Size } from '../common';
4
4
  import AvatarView from '../avatarView';
5
5
  import Decision, { DecisionPresentation, DecisionType } from '.';
6
- import {
7
- createSandboxStory,
8
- globalScope,
9
- } from '../../.storybook/components/sandbox/SandboxEditor';
6
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
10
7
 
11
8
  export default {
12
9
  component: Decision,
@@ -6,10 +6,7 @@ import DefinitionList, { type DefinitionListLayout } from './DefinitionList';
6
6
  import { lorem10 } from '../test-utils';
7
7
  import Section from '../section';
8
8
  import Header from '../header';
9
- import {
10
- createSandboxStory,
11
- globalScope,
12
- } from '../../.storybook/components/sandbox/SandboxEditor';
9
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
13
10
 
14
11
  export default {
15
12
  component: DefinitionList,
@@ -3,10 +3,7 @@ import { Meta, StoryObj } from '@storybook/react-webpack5';
3
3
  import { useState } from 'react';
4
4
 
5
5
  import { Button, Dimmer } from '..';
6
- import {
7
- createSandboxStory,
8
- globalScope,
9
- } from '../../.storybook/components/sandbox/SandboxEditor';
6
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
10
7
 
11
8
  export default {
12
9
  component: Dimmer,
@@ -60,4 +57,3 @@ export const Basic: Story = {
60
57
  );
61
58
  },
62
59
  };
63
-
@@ -3,10 +3,7 @@ import Money from '../money/Money';
3
3
  import Title from '../title/Title';
4
4
 
5
5
  import Display from './Display';
6
- import {
7
- createSandboxStory,
8
- globalScope,
9
- } from '../../.storybook/components/sandbox/SandboxEditor';
6
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
10
7
 
11
8
  export default {
12
9
  title: 'Typography/Display',
@@ -1,10 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
 
3
3
  import Emphasis from './Emphasis';
4
- import {
5
- createSandboxStory,
6
- globalScope,
7
- } from '../../.storybook/components/sandbox/SandboxEditor';
4
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
8
5
 
9
6
  export default {
10
7
  component: Emphasis,
@@ -28,4 +25,3 @@ export const Basic: Story = {
28
25
  '<script>alert("nice try!")</script>',
29
26
  },
30
27
  };
31
-
@@ -3,10 +3,7 @@ import { Meta, StoryObj, Decorator } from '@storybook/react-webpack5';
3
3
  import { fn } from 'storybook/test';
4
4
  import { Flag } from '@wise/art';
5
5
  import { Rewards, Tags } from '@transferwise/icons';
6
- import {
7
- createSandboxStory,
8
- globalScope,
9
- } from '../../.storybook/components/sandbox/SandboxEditor';
6
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
10
7
  import { storySourceWithoutNoise } from '../../.storybook/helpers';
11
8
  import { lorem10, lorem5 } from '../test-utils';
12
9
  import { Sentiment } from '../common';
@@ -1,10 +1,7 @@
1
1
  import { useState } from 'react';
2
2
  import { action } from 'storybook/actions';
3
3
 
4
- import {
5
- createSandboxStory,
6
- globalScope,
7
- } from '../../.storybook/components/sandbox/SandboxEditor';
4
+ import { createSandboxStory, globalScope } from '../../.storybook/components/sandbox/SandboxEditor';
8
5
  import { Input } from '../inputs/Input';
9
6
  import { Field, FieldProps } from './Field';
10
7
  import { Sentiment } from '../common';
@@ -1,9 +1,12 @@
1
1
  .np-flow-navigation {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
2
5
  width: 100%;
3
- min-height: 97px;
6
+ box-sizing: border-box;
7
+ min-height: 96px;
4
8
  }
5
9
  .np-flow-navigation--border-bottom {
6
- min-height: 96px;
7
10
  border-bottom: 1px solid rgba(0,0,0,0.10196);
8
11
  border-bottom: 1px solid var(--color-border-neutral);
9
12
  }
@@ -69,6 +72,17 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
69
72
  .np-flow-navigation--hidden {
70
73
  visibility: hidden;
71
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
+ }
72
86
  .np-theme-personal--forest-green .np-flow-navigation .np-flow-header__left path,
73
87
  .np-theme-personal--bright-green .np-flow-navigation .np-flow-header__left path,
74
88
  .np-theme-personal--dark .np-flow-navigation .np-flow-header__left path {
@@ -2,13 +2,16 @@
2
2
  @import (reference) "./../styles/less/addons/_spacing-utilities.less";
3
3
 
4
4
  .np-flow-navigation {
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: center;
5
8
  width: 100%;
6
- // This prevents jumping when border disappears.
7
- min-height: 97px;
9
+ box-sizing: border-box;
10
+
11
+ // This is the natural height of FlowNavigation when the Avatar is displayed.
12
+ min-height: 96px;
8
13
 
9
14
  &--border-bottom {
10
- // This is the natural height of FlowNavigation when the Avatar is displayed.
11
- min-height: 96px;
12
15
  border-bottom: 1px solid var(--color-border-neutral);
13
16
  }
14
17
 
@@ -66,6 +69,19 @@
66
69
  visibility: hidden;
67
70
  }
68
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
+
69
85
  .np-theme-personal--forest-green &,
70
86
  .np-theme-personal--bright-green &,
71
87
  .np-theme-personal--dark & {
@@ -25,6 +25,13 @@ export type StoryArgs = FlowNavigationProps & CustomControls;
25
25
 
26
26
  type Story = StoryObj<StoryArgs>;
27
27
 
28
+ /**
29
+ * FlowNavigation component for multi-step flows with progress tracking.
30
+ *
31
+ * **Border Behavior:**
32
+ * - Non-composable (default): Border respects `done` prop when `showBottomBorder=true`.
33
+ * - Composable: Border controlled only by `showBottomBorder`, ignoring the `done` property.
34
+ **/
28
35
  const meta: Meta<StoryArgs> = {
29
36
  component: FlowNavigation,
30
37
  title: 'Navigation/FlowNavigation',
@@ -32,6 +39,7 @@ const meta: Meta<StoryArgs> = {
32
39
  showCloseButton: { control: 'boolean', defaultValue: true },
33
40
  showMobileBackButton: { control: 'boolean', defaultValue: true },
34
41
  done: { control: 'boolean', defaultValue: false },
42
+ showBottomBorder: { control: 'boolean', defaultValue: true },
35
43
  avatarURL: { control: 'text', defaultValue: '../tapestry-01.png' },
36
44
  profileType: {
37
45
  control: 'radio',
@@ -372,3 +380,244 @@ export const WithOverlayHeaderComparison: Story = {
372
380
  );
373
381
  },
374
382
  };
383
+
384
+ export const ComposableVariant: Story = {
385
+ args: {
386
+ profileType: ProfileType.PERSONAL,
387
+ showCloseButton: true,
388
+ showMobileBackButton: true,
389
+ done: false,
390
+ avatarURL: '../tapestry-01.png',
391
+ },
392
+ parameters: {
393
+ padding: '0',
394
+ chromatic: {
395
+ viewports,
396
+ },
397
+ },
398
+ render: (args) => {
399
+ const [activeStep, setActiveStep] = useState(2);
400
+ const steps = [
401
+ {
402
+ label: 'Recipient',
403
+ onClick: () => setActiveStep(0),
404
+ },
405
+ {
406
+ label: 'Amount',
407
+ hoverLabel: (
408
+ <>
409
+ You send 100 GBP <br />
410
+ You get 99.39 GBP{' '}
411
+ </>
412
+ ),
413
+ ...(activeStep > 1 && { onClick: () => setActiveStep(1) }),
414
+ },
415
+ {
416
+ label: 'Review',
417
+ ...(activeStep > 2 && { onClick: () => setActiveStep(2) }),
418
+ },
419
+ {
420
+ label: 'Pay',
421
+ ...(activeStep > 3 && { onClick: () => setActiveStep(3) }),
422
+ },
423
+ ];
424
+ return (
425
+ <>
426
+ <FlowNavigation
427
+ composable
428
+ avatar={<AvatarView profileType={args.profileType} />}
429
+ logo={<Logo />}
430
+ activeStep={activeStep}
431
+ steps={steps}
432
+ onClose={args.showCloseButton ? () => alert('close') : undefined}
433
+ onGoBack={
434
+ args.showMobileBackButton
435
+ ? () => setActiveStep(activeStep > 0 ? activeStep - 1 : 0)
436
+ : undefined
437
+ }
438
+ />
439
+ <Container size="narrow">
440
+ <Body className="m-a-3">
441
+ <Display type={Typography.DISPLAY_SMALL}>{steps[activeStep].label} Step</Display>
442
+ <br />
443
+ {lorem10}
444
+ </Body>
445
+ </Container>
446
+
447
+ <Sticky>
448
+ <Container
449
+ size="narrow"
450
+ className="d-flex justify-content-center align-items-center p-y-3"
451
+ >
452
+ <Button
453
+ v2
454
+ disabled={activeStep === 3}
455
+ block
456
+ onClick={() => setActiveStep(activeStep + 1)}
457
+ >
458
+ Continue
459
+ </Button>
460
+ </Container>
461
+ </Sticky>
462
+ </>
463
+ );
464
+ },
465
+ };
466
+
467
+ export const WithoutStepper: Story = {
468
+ args: {
469
+ profileType: ProfileType.PERSONAL,
470
+ showCloseButton: true,
471
+ avatarURL: '../tapestry-01.png',
472
+ },
473
+ parameters: {
474
+ chromatic: {
475
+ viewports,
476
+ },
477
+ },
478
+ render: (args) => {
479
+ const [closed, setClosed] = useState(false);
480
+
481
+ if (closed) return <Body className="text-xs-center m-a-3">FlowNavigation closed</Body>;
482
+
483
+ return (
484
+ <FlowNavigation
485
+ avatar={<AvatarView profileType={args.profileType} />}
486
+ logo={<Logo />}
487
+ onClose={args.showCloseButton ? () => setClosed(true) : undefined}
488
+ />
489
+ );
490
+ },
491
+ };
492
+
493
+ export const BorderControl: Story = {
494
+ args: {
495
+ profileType: ProfileType.PERSONAL,
496
+ avatarURL: '../tapestry-01.png',
497
+ },
498
+ parameters: {
499
+ chromatic: {
500
+ viewports,
501
+ },
502
+ },
503
+ render: (args) => {
504
+ const [activeStep, setActiveStep] = useState(2);
505
+
506
+ const steps = [
507
+ {
508
+ label: 'Recipient',
509
+ onClick: () => setActiveStep(0),
510
+ },
511
+ {
512
+ label: 'Amount',
513
+ hoverLabel: (
514
+ <>
515
+ You send 100 GBP <br />
516
+ You get 99.39 GBP{' '}
517
+ </>
518
+ ),
519
+ ...(activeStep > 1 && { onClick: () => setActiveStep(1) }),
520
+ },
521
+ {
522
+ label: 'Review',
523
+ ...(activeStep > 2 && { onClick: () => setActiveStep(2) }),
524
+ },
525
+ {
526
+ label: 'Pay',
527
+ ...(activeStep > 3 && { onClick: () => setActiveStep(3) }),
528
+ },
529
+ ];
530
+
531
+ return (
532
+ <>
533
+ <Body className="text-xs-center m-b-3">
534
+ <Display type={Typography.DISPLAY_SMALL}>Non-composable Variant</Display>
535
+ <br />
536
+ Border controlled by <code>done</code> property only
537
+ </Body>
538
+
539
+ {/* Non-composable: done=false - SHOWS BORDER */}
540
+ <FlowNavigation
541
+ avatar={<AvatarView profileType={args.profileType} />}
542
+ logo={<Logo />}
543
+ activeStep={activeStep}
544
+ done={false}
545
+ steps={steps}
546
+ onClose={() => {}}
547
+ onGoBack={() => setActiveStep(activeStep > 0 ? activeStep - 1 : 0)}
548
+ />
549
+ <Body className="text-xs-center m-b-3">
550
+ <code>done=false</code> → ✅ Border shown
551
+ </Body>
552
+
553
+ {/* Non-composable: done=true - HIDES BORDER */}
554
+ <FlowNavigation
555
+ avatar={<AvatarView profileType={args.profileType} />}
556
+ logo={<Logo />}
557
+ activeStep={activeStep}
558
+ done
559
+ steps={steps}
560
+ onClose={() => {}}
561
+ onGoBack={() => setActiveStep(activeStep > 0 ? activeStep - 1 : 0)}
562
+ />
563
+ <Body className="text-xs-center m-b-5">
564
+ <code>done=true</code> → ❌ Border hidden
565
+ </Body>
566
+
567
+ <Body className="text-xs-center m-b-3 m-t-5">
568
+ <Display type={Typography.DISPLAY_SMALL}>Composable Variant</Display>
569
+ <br />
570
+ Border ignores <code>done</code>, follows <code>showBottomBorder</code> only
571
+ </Body>
572
+
573
+ {/* Composable: showBottomBorder=true, done=false - SHOWS BORDER */}
574
+ <FlowNavigation
575
+ composable
576
+ avatar={<AvatarView profileType={args.profileType} />}
577
+ logo={<Logo />}
578
+ activeStep={activeStep}
579
+ done={false}
580
+ steps={steps}
581
+ showBottomBorder
582
+ onClose={() => {}}
583
+ onGoBack={() => setActiveStep(activeStep > 0 ? activeStep - 1 : 0)}
584
+ />
585
+ <Body className="text-xs-center m-b-3">
586
+ <code>showBottomBorder=true, done=false</code> → ✅ Border shown
587
+ </Body>
588
+
589
+ {/* Composable: showBottomBorder=true, done=true - SHOWS BORDER */}
590
+ <FlowNavigation
591
+ composable
592
+ avatar={<AvatarView profileType={args.profileType} />}
593
+ logo={<Logo />}
594
+ activeStep={activeStep}
595
+ done
596
+ steps={steps}
597
+ showBottomBorder
598
+ onClose={() => {}}
599
+ onGoBack={() => setActiveStep(activeStep > 0 ? activeStep - 1 : 0)}
600
+ />
601
+ <Body className="text-xs-center m-b-3">
602
+ <code>showBottomBorder=true, done=true</code> → ✅ Border shown (ignores done)
603
+ </Body>
604
+
605
+ {/* Composable: showBottomBorder=false, done=false - HIDES BORDER */}
606
+ <FlowNavigation
607
+ composable
608
+ avatar={<AvatarView profileType={args.profileType} />}
609
+ logo={<Logo />}
610
+ activeStep={activeStep}
611
+ done={false}
612
+ steps={steps}
613
+ showBottomBorder={false}
614
+ onClose={() => {}}
615
+ onGoBack={() => setActiveStep(activeStep > 0 ? activeStep - 1 : 0)}
616
+ />
617
+ <Body className="text-xs-center m-b-3">
618
+ <code>showBottomBorder=false, done=false</code> → ❌ Border hidden
619
+ </Body>
620
+ </>
621
+ );
622
+ },
623
+ };