@transferwise/components 46.128.3 → 46.130.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 (121) hide show
  1. package/build/alert/Alert.js +1 -1
  2. package/build/alert/Alert.mjs +1 -1
  3. package/build/card/Card.js.map +1 -1
  4. package/build/card/Card.mjs.map +1 -1
  5. package/build/common/{card/Card.js → baseCard/BaseCard.js} +4 -4
  6. package/build/common/baseCard/BaseCard.js.map +1 -0
  7. package/build/common/{card/Card.mjs → baseCard/BaseCard.mjs} +4 -4
  8. package/build/common/baseCard/BaseCard.mjs.map +1 -0
  9. package/build/common/liveRegion/LiveRegion.js +42 -0
  10. package/build/common/liveRegion/LiveRegion.js.map +1 -0
  11. package/build/common/liveRegion/LiveRegion.mjs +40 -0
  12. package/build/common/liveRegion/LiveRegion.mjs.map +1 -0
  13. package/build/criticalBanner/CriticalCommsBanner.js +68 -3
  14. package/build/criticalBanner/CriticalCommsBanner.js.map +1 -1
  15. package/build/criticalBanner/CriticalCommsBanner.mjs +69 -4
  16. package/build/criticalBanner/CriticalCommsBanner.mjs.map +1 -1
  17. package/build/flowNavigation/FlowNavigation.js +1 -1
  18. package/build/flowNavigation/FlowNavigation.mjs +1 -1
  19. package/build/index.js +4 -4
  20. package/build/index.mjs +1 -1
  21. package/build/inputs/SelectInput.js +1 -1
  22. package/build/inputs/SelectInput.js.map +1 -1
  23. package/build/inputs/SelectInput.mjs +1 -1
  24. package/build/inputs/SelectInput.mjs.map +1 -1
  25. package/build/inputs/_ButtonInput.js +2 -2
  26. package/build/inputs/_ButtonInput.js.map +1 -1
  27. package/build/inputs/_ButtonInput.mjs +2 -2
  28. package/build/inputs/_ButtonInput.mjs.map +1 -1
  29. package/build/main.css +191 -165
  30. package/build/overlayHeader/OverlayHeader.js +1 -1
  31. package/build/overlayHeader/OverlayHeader.mjs +1 -1
  32. package/build/promoCard/PromoCard.js +2 -2
  33. package/build/promoCard/PromoCard.js.map +1 -1
  34. package/build/promoCard/PromoCard.mjs +2 -2
  35. package/build/promoCard/PromoCard.mjs.map +1 -1
  36. package/build/prompt/InfoPrompt/InfoPrompt.js +35 -29
  37. package/build/prompt/InfoPrompt/InfoPrompt.js.map +1 -1
  38. package/build/prompt/InfoPrompt/InfoPrompt.mjs +35 -29
  39. package/build/prompt/InfoPrompt/InfoPrompt.mjs.map +1 -1
  40. package/build/sentimentSurface/SentimentSurface.js +5 -1
  41. package/build/sentimentSurface/SentimentSurface.js.map +1 -1
  42. package/build/sentimentSurface/SentimentSurface.mjs +5 -1
  43. package/build/sentimentSurface/SentimentSurface.mjs.map +1 -1
  44. package/build/styles/criticalBanner/CriticalCommsBanner.css +33 -15
  45. package/build/styles/inputs/SelectInput.css +8 -0
  46. package/build/styles/listItem/ListItem.css +1 -1
  47. package/build/styles/main.css +191 -165
  48. package/build/styles/sentimentSurface/SentimentSurface.css +100 -100
  49. package/build/types/card/Card.d.ts +1 -1
  50. package/build/types/common/{card/Card.d.ts → baseCard/BaseCard.d.ts} +8 -8
  51. package/build/types/common/baseCard/BaseCard.d.ts.map +1 -0
  52. package/build/types/common/baseCard/index.d.ts +3 -0
  53. package/build/types/common/baseCard/index.d.ts.map +1 -0
  54. package/build/types/common/index.d.ts +2 -0
  55. package/build/types/common/index.d.ts.map +1 -1
  56. package/build/types/common/liveRegion/LiveRegion.d.ts +23 -0
  57. package/build/types/common/liveRegion/LiveRegion.d.ts.map +1 -0
  58. package/build/types/common/liveRegion/index.d.ts +3 -0
  59. package/build/types/common/liveRegion/index.d.ts.map +1 -0
  60. package/build/types/criticalBanner/CriticalCommsBanner.d.ts +4 -1
  61. package/build/types/criticalBanner/CriticalCommsBanner.d.ts.map +1 -1
  62. package/build/types/criticalBanner/index.d.ts +1 -0
  63. package/build/types/criticalBanner/index.d.ts.map +1 -1
  64. package/build/types/index.d.ts +2 -1
  65. package/build/types/index.d.ts.map +1 -1
  66. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  67. package/build/types/promoCard/PromoCard.d.ts +3 -3
  68. package/build/types/promoCard/PromoCard.d.ts.map +1 -1
  69. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts +11 -2
  70. package/build/types/prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -1
  71. package/build/types/sentimentSurface/SentimentSurface.d.ts.map +1 -1
  72. package/package.json +2 -2
  73. package/src/card/Card.story.tsx +3 -2
  74. package/src/card/Card.tsx +1 -1
  75. package/src/common/{card/Card.less → baseCard/BaseCard.less} +1 -1
  76. package/src/common/{card/Card.story.tsx → baseCard/BaseCard.story.tsx} +5 -5
  77. package/src/common/{card/Card.test.tsx → baseCard/BaseCard.test.tsx} +11 -12
  78. package/src/common/{card/Card.tsx → baseCard/BaseCard.tsx} +9 -9
  79. package/src/common/baseCard/index.ts +2 -0
  80. package/src/common/index.ts +2 -0
  81. package/src/common/liveRegion/LiveRegion.test.tsx +56 -0
  82. package/src/common/liveRegion/LiveRegion.tsx +49 -0
  83. package/src/common/liveRegion/index.ts +2 -0
  84. package/src/criticalBanner/CriticalCommsBanner.css +33 -15
  85. package/src/criticalBanner/CriticalCommsBanner.less +46 -36
  86. package/src/criticalBanner/CriticalCommsBanner.story.tsx +9 -15
  87. package/src/criticalBanner/CriticalCommsBanner.test.story.tsx +70 -0
  88. package/src/criticalBanner/CriticalCommsBanner.test.tsx +66 -0
  89. package/src/criticalBanner/CriticalCommsBanner.tsx +54 -5
  90. package/src/criticalBanner/index.ts +1 -0
  91. package/src/index.ts +2 -1
  92. package/src/inputs/SelectInput.css +8 -0
  93. package/src/inputs/SelectInput.story.tsx +2 -2
  94. package/src/inputs/SelectInput.test.story.tsx +57 -1
  95. package/src/inputs/SelectInput.test.tsx +33 -1
  96. package/src/inputs/SelectInput.tsx +2 -1
  97. package/src/inputs/_ButtonInput.less +8 -0
  98. package/src/inputs/_ButtonInput.tsx +1 -1
  99. package/src/listItem/ListItem.css +1 -1
  100. package/src/listItem/ListItem.less +4 -2
  101. package/src/listItem/_stories/Breakpoints/ListItem.noMedia.test.story.tsx +62 -0
  102. package/src/listItem/_stories/Breakpoints/ListItem.sideMedia.test.story.tsx +62 -0
  103. package/src/listItem/_stories/Breakpoints/ListItem.stackedMedia.test.story.tsx +62 -0
  104. package/src/listItem/_stories/ListItem.story.tsx +3 -2
  105. package/src/main.css +191 -165
  106. package/src/main.less +2 -2
  107. package/src/promoCard/PromoCard.tsx +6 -5
  108. package/src/prompt/InfoPrompt/InfoPrompt.test.story.tsx +119 -0
  109. package/src/prompt/InfoPrompt/InfoPrompt.tsx +47 -34
  110. package/src/sentimentSurface/SentimentSurface.css +100 -100
  111. package/src/sentimentSurface/SentimentSurface.less +50 -50
  112. package/src/sentimentSurface/SentimentSurface.test.story.tsx +19 -0
  113. package/src/sentimentSurface/SentimentSurface.tsx +3 -0
  114. package/build/common/card/Card.js.map +0 -1
  115. package/build/common/card/Card.mjs.map +0 -1
  116. package/build/types/common/card/Card.d.ts.map +0 -1
  117. package/build/types/common/card/index.d.ts +0 -3
  118. package/build/types/common/card/index.d.ts.map +0 -1
  119. package/src/common/card/index.ts +0 -2
  120. /package/build/styles/common/{card/Card.css → baseCard/BaseCard.css} +0 -0
  121. /package/src/common/{card/Card.css → baseCard/BaseCard.css} +0 -0
@@ -1,4 +1,5 @@
1
1
  import { HTMLAttributes, ReactNode } from 'react';
2
+ import type { AriaLive } from '../../common';
2
3
  import type { Sentiment as SurfaceSentiment } from '../../sentimentSurface';
3
4
  import { LinkProps } from '../../link';
4
5
  import { PrimitivePromptProps } from '../PrimitivePrompt';
@@ -15,7 +16,7 @@ export type InfoPromptMedia = {
15
16
  */
16
17
  asset: ReactNode;
17
18
  };
18
- export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & Pick<PrimitivePromptProps, 'data-testid'> & {
19
+ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'aria-live' | 'role'> & Pick<PrimitivePromptProps, 'data-testid'> & {
19
20
  /**
20
21
  * The sentiment determines the colour scheme
21
22
  * @default 'neutral'
@@ -44,6 +45,14 @@ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & Pi
44
45
  */
45
46
  description: string;
46
47
  className?: string;
48
+ /**
49
+ * Sets the ARIA live region politeness level.
50
+ * - `'polite'` — announced after the current speech (default)
51
+ * - `'assertive'` — interrupts the current speech immediately
52
+ * - `'off'` — disables the live region entirely
53
+ * @default 'polite'
54
+ */
55
+ 'aria-live'?: AriaLive;
47
56
  };
48
57
  /**
49
58
  * InfoPrompt displays important contextual messages to users within a screen.
@@ -54,5 +63,5 @@ export type InfoPromptProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & Pi
54
63
  *
55
64
  * Guidance can be found in the [design system](https://wise.design/components/info-prompt).
56
65
  */
57
- export declare const InfoPrompt: ({ sentiment, onDismiss, media, action, title, description, className, "data-testid": dataTestId, ...restProps }: InfoPromptProps) => import("react").JSX.Element;
66
+ export declare const InfoPrompt: ({ sentiment, onDismiss, media, action, title, description, className, "aria-live": ariaLive, "data-testid": dataTestId, ...restProps }: InfoPromptProps) => import("react").JSX.Element;
58
67
  //# sourceMappingURL=InfoPrompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InfoPrompt.d.ts","sourceRoot":"","sources":["../../../../src/prompt/InfoPrompt/InfoPrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAY,MAAM,OAAO,CAAC;AAG5D,OAAO,KAAK,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI5E,OAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAmB,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE3E,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG;IAC9E;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC,GACzE,IAAI,CAAC,oBAAoB,EAAE,aAAa,CAAC,GAAG;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GAAI,iHAUxB,eAAe,gCAyEjB,CAAC"}
1
+ {"version":3,"file":"InfoPrompt.d.ts","sourceRoot":"","sources":["../../../../src/prompt/InfoPrompt/InfoPrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAY,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI5E,OAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAmB,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE3E,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG;IAC9E;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;;OAGG;IACH,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC,GAChG,IAAI,CAAC,oBAAoB,EAAE,aAAa,CAAC,GAAG;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;OAGG;IACH,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;OAEG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;CACxB,CAAC;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GAAI,wIAWxB,eAAe,gCA4EjB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"SentimentSurface.d.ts","sourceRoot":"","sources":["../../../src/sentimentSurface/SentimentSurface.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,QAAA,MAAM,gBAAgB,EAAE,yBAsCtB,CAAC;AAIH,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"SentimentSurface.d.ts","sourceRoot":"","sources":["../../../src/sentimentSurface/SentimentSurface.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAGlC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,QAAA,MAAM,gBAAgB,EAAE,yBAwCtB,CAAC;AAIH,eAAe,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "46.128.3",
3
+ "version": "46.130.0",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -103,7 +103,7 @@
103
103
  },
104
104
  "dependencies": {
105
105
  "@babel/runtime": "^7.28.6",
106
- "@floating-ui/react": "^0.27.17",
106
+ "@floating-ui/react": "^0.27.18",
107
107
  "@headlessui/react": "^2.2.9",
108
108
  "@popperjs/core": "^2.11.8",
109
109
  "@react-aria/focus": "^3.21.4",
@@ -8,13 +8,14 @@ import Card from '.';
8
8
  type Story = StoryObj<typeof Card>;
9
9
 
10
10
  /**
11
- * @deprecated use Navigation pattern (via `NavigationOption` component)
11
+ * ⚠️ This component is **deprecated** and superseded by the [new ListItem component](?path=/docs/content-listitem--docs).
12
12
  *
13
13
  * @see https://transferwise.atlassian.net/wiki/spaces/DS/pages/2387314550/Instructions+for+killing+expanding+cards+on+web+design+pattern
14
14
  */
15
15
  const meta: Meta<typeof Card> = {
16
16
  component: Card,
17
- title: 'Layouts/Card (Deprecated)',
17
+ title: 'Layouts/Card',
18
+ tags: ['deprecated'],
18
19
  args: {
19
20
  title: 'A card',
20
21
  details: 'Some details about this card',
package/src/card/Card.tsx CHANGED
@@ -21,7 +21,7 @@ export interface CardProps {
21
21
  }
22
22
 
23
23
  /**
24
- * @deprecated use Navigation pattern (via `NavigationOption` component)
24
+ * @deprecated Use [new ListItem component](?path=/docs/content-listitem--docs) instead.
25
25
  * @see https://transferwise.atlassian.net/wiki/spaces/DS/pages/2387314550/Instructions+for+killing+expanding+cards+on+web+design+pattern
26
26
  */
27
27
  const Card = forwardRef(function Card(
@@ -50,4 +50,4 @@
50
50
  --Card-closeButton-position: var(--size-8);
51
51
  --Card-padding: var(--size-16);
52
52
  }
53
- }
53
+ }
@@ -4,15 +4,15 @@ import { lorem10 } from '../../test-utils';
4
4
  import Title from '../../title';
5
5
  import { Typography } from '../propsValues/typography';
6
6
 
7
- import Card from '.';
7
+ import BaseCard from '.';
8
8
 
9
- const meta: Meta<typeof Card> = {
10
- component: Card,
11
- title: 'Internal/Card',
9
+ const meta: Meta<typeof BaseCard> = {
10
+ component: BaseCard,
11
+ title: 'Internal/BaseCard',
12
12
  };
13
13
 
14
14
  export default meta;
15
- type Story = StoryObj<typeof Card>;
15
+ type Story = StoryObj<typeof BaseCard>;
16
16
 
17
17
  export const Default: Story = {
18
18
  args: {
@@ -1,10 +1,9 @@
1
1
  import { render, fireEvent, screen } from '@testing-library/react';
2
- import React from 'react';
3
2
  import { IntlProvider } from 'react-intl';
4
3
 
5
- import Card from './Card';
4
+ import BaseCard from './BaseCard';
6
5
 
7
- describe('Card', () => {
6
+ describe('BaseCard', () => {
8
7
  const defaultProps = {
9
8
  children: 'Test Content',
10
9
  testId: 'test-card',
@@ -15,13 +14,13 @@ describe('Card', () => {
15
14
  ...defaultProps,
16
15
  children: 'Test text',
17
16
  };
18
- const { rerender } = render(<Card {...defaultProps}>Test Content</Card>);
17
+ const { rerender } = render(<BaseCard {...defaultProps}>Test Content</BaseCard>);
19
18
 
20
19
  expect(screen.getByTestId('test-card')).toBeInTheDocument();
21
20
  expect(screen.getByTestId('test-card')).toHaveClass('np-Card');
22
21
 
23
22
  // Change props
24
- rerender(<Card {...props} />);
23
+ rerender(<BaseCard {...props} />);
25
24
 
26
25
  expect(screen.getByText('Test text')).toBeInTheDocument();
27
26
  });
@@ -31,12 +30,12 @@ describe('Card', () => {
31
30
  ...defaultProps,
32
31
  className: 'test-custom',
33
32
  };
34
- const { rerender } = render(<Card {...defaultProps} />);
33
+ const { rerender } = render(<BaseCard {...defaultProps} />);
35
34
 
36
35
  expect(screen.getByTestId('test-card')).not.toHaveClass('test-custom');
37
36
 
38
37
  // Change props
39
- rerender(<Card {...props} />);
38
+ rerender(<BaseCard {...props} />);
40
39
 
41
40
  expect(screen.getByTestId('test-card')).toHaveClass('test-custom');
42
41
  });
@@ -46,12 +45,12 @@ describe('Card', () => {
46
45
  ...defaultProps,
47
46
  isDisabled: true,
48
47
  };
49
- const { rerender } = render(<Card {...defaultProps} />);
48
+ const { rerender } = render(<BaseCard {...defaultProps} />);
50
49
 
51
50
  expect(screen.getByTestId('test-card')).not.toHaveClass('is-disabled');
52
51
 
53
52
  // Change props
54
- rerender(<Card {...props} />);
53
+ rerender(<BaseCard {...props} />);
55
54
 
56
55
  expect(screen.getByTestId('test-card')).toHaveClass('is-disabled');
57
56
  });
@@ -61,12 +60,12 @@ describe('Card', () => {
61
60
  ...defaultProps,
62
61
  isSmall: true,
63
62
  };
64
- const { rerender } = render(<Card {...defaultProps} />);
63
+ const { rerender } = render(<BaseCard {...defaultProps} />);
65
64
 
66
65
  expect(screen.getByTestId('test-card')).not.toHaveClass('np-Card--small');
67
66
 
68
67
  // Change props
69
- rerender(<Card {...props} />);
68
+ rerender(<BaseCard {...props} />);
70
69
 
71
70
  expect(screen.getByTestId('test-card')).toHaveClass('np-Card--small');
72
71
  });
@@ -79,7 +78,7 @@ describe('Card', () => {
79
78
  };
80
79
  render(
81
80
  <IntlProvider locale="en">
82
- <Card {...props} />
81
+ <BaseCard {...props} />
83
82
  </IntlProvider>,
84
83
  );
85
84
 
@@ -1,10 +1,10 @@
1
1
  import { clsx } from 'clsx';
2
- import { MouseEvent, type ReactNode, forwardRef, useRef } from 'react';
2
+ import { type ReactNode, forwardRef, useRef } from 'react';
3
3
 
4
4
  import { CloseButton } from '../closeButton';
5
5
  import { stopPropagation } from '../domHelpers';
6
6
 
7
- export interface CardProps {
7
+ export interface BaseCardProps {
8
8
  /** Content to display inside Card. */
9
9
  children: ReactNode;
10
10
 
@@ -28,9 +28,9 @@ export interface CardProps {
28
28
  }
29
29
 
30
30
  /**
31
- * Card component.
31
+ * BaseCard component.
32
32
  *
33
- * A card is a container for content that is used to group related information.
33
+ * A BaseCard is a container for content that is used to group related information.
34
34
  * It can be used to display information in a structured way, and can be
35
35
  * customized with various props to suit different use cases.
36
36
  *
@@ -44,11 +44,11 @@ export interface CardProps {
44
44
  * @param {string} testId - Optional ID to add to the card container for testing purposes.
45
45
  * @returns {React.JSX.Element} - The card component.
46
46
  * @example
47
- * <Card>
47
+ * <BaseCard>
48
48
  * <p>Hello World!</p>
49
- * </Card>
49
+ * </BaseCard>
50
50
  */
51
- const Card = forwardRef<HTMLDivElement, CardProps>(
51
+ const BaseCard = forwardRef<HTMLDivElement, BaseCardProps>(
52
52
  (
53
53
  {
54
54
  className,
@@ -98,6 +98,6 @@ const Card = forwardRef<HTMLDivElement, CardProps>(
98
98
  },
99
99
  );
100
100
 
101
- Card.displayName = 'Card';
101
+ BaseCard.displayName = 'Card';
102
102
 
103
- export default Card;
103
+ export default BaseCard;
@@ -0,0 +1,2 @@
1
+ export { default } from './BaseCard';
2
+ export * from './BaseCard';
@@ -27,3 +27,5 @@ export * from './initials';
27
27
  export * from './colors';
28
28
  export * from './constants';
29
29
  export { CloseButton } from './closeButton';
30
+ export { LiveRegion } from './liveRegion';
31
+ export type { AriaLive, LiveRegionProps } from './liveRegion';
@@ -0,0 +1,56 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { LiveRegion, LiveRegionProps } from './LiveRegion';
3
+
4
+ describe('LiveRegion', () => {
5
+ const renderLiveRegion = (props: Partial<LiveRegionProps> & Pick<LiveRegionProps, 'aria-live'>) =>
6
+ render(<LiveRegion {...props}>{props.children ?? 'Live content'}</LiveRegion>);
7
+
8
+ describe('when aria-live is "polite"', () => {
9
+ it('renders with role="status"', () => {
10
+ renderLiveRegion({ 'aria-live': 'polite' });
11
+ expect(screen.getByRole('status')).toBeInTheDocument();
12
+ });
13
+
14
+ it('sets aria-live="polite"', () => {
15
+ renderLiveRegion({ 'aria-live': 'polite' });
16
+ expect(screen.getByRole('status')).toHaveAttribute('aria-live', 'polite');
17
+ });
18
+
19
+ it('sets aria-atomic="true"', () => {
20
+ renderLiveRegion({ 'aria-live': 'polite' });
21
+ expect(screen.getByRole('status')).toHaveAttribute('aria-atomic', 'true');
22
+ });
23
+ });
24
+
25
+ describe('when aria-live is "assertive"', () => {
26
+ it('renders with role="alert"', () => {
27
+ renderLiveRegion({ 'aria-live': 'assertive' });
28
+ expect(screen.getByRole('alert')).toBeInTheDocument();
29
+ });
30
+
31
+ it('sets aria-live="assertive"', () => {
32
+ renderLiveRegion({ 'aria-live': 'assertive' });
33
+ expect(screen.getByRole('alert')).toHaveAttribute('aria-live', 'assertive');
34
+ });
35
+
36
+ it('sets aria-atomic="true"', () => {
37
+ renderLiveRegion({ 'aria-live': 'assertive' });
38
+ expect(screen.getByRole('alert')).toHaveAttribute('aria-atomic', 'true');
39
+ });
40
+ });
41
+
42
+ it('renders children', () => {
43
+ renderLiveRegion({ 'aria-live': 'polite', children: 'Transfer sent' });
44
+ expect(screen.getByText('Transfer sent')).toBeInTheDocument();
45
+ });
46
+
47
+ it('passes additional HTML attributes to the wrapper div', () => {
48
+ renderLiveRegion({ 'aria-live': 'polite', className: 'custom' });
49
+ expect(screen.getByRole('status')).toHaveClass('custom');
50
+ });
51
+
52
+ it('supports data-testid prop', () => {
53
+ renderLiveRegion({ 'aria-live': 'polite', 'data-testid': 'live-region' });
54
+ expect(screen.getByTestId('live-region')).toBeInTheDocument();
55
+ });
56
+ });
@@ -0,0 +1,49 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+
3
+ const ARIA_LIVE_ROLE_MAP = {
4
+ assertive: 'alert',
5
+ polite: 'status',
6
+ } as const;
7
+
8
+ export type AriaLive = 'off' | 'polite' | 'assertive';
9
+
10
+ export interface LiveRegionProps extends Omit<
11
+ HTMLAttributes<HTMLDivElement>,
12
+ 'role' | 'aria-live' | 'aria-atomic'
13
+ > {
14
+ /**
15
+ * Determines urgency: 'assertive' interrupts, 'polite' waits for idle, 'off' disables live region.
16
+ */
17
+ 'aria-live': AriaLive;
18
+ /** Test ID for testing tools */
19
+ 'data-testid'?: string;
20
+ children?: ReactNode;
21
+ }
22
+
23
+ /**
24
+ * Renders an ARIA live region with the correct implicit role.
25
+ *
26
+ * - `aria-live="polite"` → `role="status"`
27
+ * - `aria-live="assertive"` → `role="alert"`
28
+ * - `aria-live="off"` → no live region
29
+ *
30
+ * The `role` prop is intentionally excluded from the public API
31
+ * to prevent mismatches between `aria-live` and `role`.
32
+ */
33
+ export const LiveRegion = ({ 'aria-live': ariaLive, children, ...props }: LiveRegionProps) => {
34
+ if (ariaLive === 'off') {
35
+ return <>{children}</>;
36
+ }
37
+
38
+ return (
39
+ <div
40
+ role={ARIA_LIVE_ROLE_MAP[ariaLive]}
41
+ aria-live={ariaLive}
42
+ aria-atomic="true"
43
+ style={{ display: 'contents' }}
44
+ {...props}
45
+ >
46
+ {children}
47
+ </div>
48
+ );
49
+ };
@@ -0,0 +1,2 @@
1
+ export { LiveRegion } from './LiveRegion';
2
+ export type { AriaLive, LiveRegionProps } from './LiveRegion';
@@ -1,34 +1,52 @@
1
- .np-theme-personal .critical-comms .alert-warning {
2
- color: var(--color-contrast-overlay);
3
- background-color: var(--color-sentiment-negative);
1
+ .critical-comms {
2
+ border-radius: 16px;
3
+ border-radius: var(--radius-medium);
4
+ overflow: hidden;
4
5
  }
5
- .np-theme-personal .critical-comms .alert-warning .np-text-title-body {
6
- color: var(--color-contrast-overlay);
6
+ .critical-comms .alert {
7
+ background-color: var(--color-sentiment-background-surface);
8
+ color: var(--color-sentiment-content-primary);
9
+ margin-bottom: 0;
7
10
  }
8
- .np-theme-personal .critical-comms .status-circle.warning {
11
+ .critical-comms .alert .np-text-title-body {
12
+ color: var(--color-sentiment-content-primary);
13
+ }
14
+ .critical-comms .status-circle.negative {
9
15
  background-color: #ffffff;
10
16
  background-color: var(--color-background-screen);
11
17
  }
12
- .np-theme-personal .critical-comms .status-circle.warning .status-icon {
13
- color: var(--color-contrast-theme);
18
+ .critical-comms .status-circle.negative .status-icon {
19
+ color: #37517e;
20
+ color: var(--color-content-primary);
14
21
  }
15
- .np-theme-personal .critical-comms .alert__message .alert__action {
22
+ .critical-comms .alert__message .alert__action {
16
23
  margin-top: 16px;
17
24
  margin-top: var(--size-16);
18
25
  }
19
- .np-theme-personal .critical-comms .wds-Button {
20
- --Button-background: var(--color-contrast-overlay);
21
- --Button-background-hover: var(--color-sentiment-negative-secondary-hover);
22
- --Button-background-active: var(--color-sentiment-negative-secondary-active);
26
+ .critical-comms .wds-Button {
27
+ --Button-color: var(--color-content-primary);
28
+ --Button-color-hover: var(--color-content-primary);
29
+ --Button-color-active: var(--color-content-primary);
30
+ --Button-background: var(--color-background-screen);
31
+ --Button-background-hover: var(--color-sentiment-interactive-primary-hover);
32
+ --Button-background-active: var(--color-sentiment-interactive-primary-active);
33
+ }
34
+ .critical-comms .alert-warning .wds-Button {
35
+ --Button-background-hover: var(--color-sentiment-interactive-secondary-neutral-hover);
36
+ --Button-background-active: var(--color-sentiment-interactive-secondary-neutral-active);
23
37
  }
24
38
  @media (min-width: 768px) {
25
- .np-theme-personal .critical-comms .alert-warning .alert__message {
39
+ .critical-comms .alert-warning .alert__message,
40
+ .critical-comms .alert-negative .alert__message,
41
+ .critical-comms .alert-neutral .alert__message {
26
42
  flex-direction: row;
27
43
  justify-content: space-between;
28
44
  align-items: center;
29
45
  width: 100%;
30
46
  }
31
- .np-theme-personal .critical-comms .alert-warning .alert__message .alert__action {
47
+ .critical-comms .alert-warning .alert__message .alert__action,
48
+ .critical-comms .alert-negative .alert__message .alert__action,
49
+ .critical-comms .alert-neutral .alert__message .alert__action {
32
50
  margin-top: 0;
33
51
  margin-left: 16px;
34
52
  margin-left: var(--padding-small);
@@ -1,51 +1,61 @@
1
- .np-theme-personal {
2
- .critical-comms {
3
- .alert-warning {
4
- color: var(--color-contrast-overlay);
5
- background-color: var(--color-sentiment-negative);
6
-
7
- .np-text-title-body {
8
- color: var(--color-contrast-overlay);
9
- }
10
- }
1
+ .critical-comms {
2
+ border-radius: var(--radius-medium);
3
+ overflow: hidden;
11
4
 
12
- .status-circle.warning {
13
- background-color: var(--color-background-screen);
5
+ .alert {
6
+ background-color: var(--color-sentiment-background-surface);
7
+ color: var(--color-sentiment-content-primary);
8
+ margin-bottom: 0;
14
9
 
15
- .status-icon {
16
- color: var(--color-contrast-theme);
17
- }
10
+ .np-text-title-body {
11
+ color: var(--color-sentiment-content-primary);
18
12
  }
13
+ }
19
14
 
20
- .alert__message {
21
- .alert__action {
22
- margin-top: var(--size-16);
23
- }
15
+ .status-circle.negative {
16
+ background-color: var(--color-background-screen);
17
+
18
+ .status-icon {
19
+ color: var(--color-content-primary);
24
20
  }
21
+ }
25
22
 
26
- .wds-Button {
27
- --Button-background: var(--color-contrast-overlay);
28
- --Button-background-hover: var(--color-sentiment-negative-secondary-hover);
29
- --Button-background-active: var(--color-sentiment-negative-secondary-active);
23
+ .alert__message {
24
+ .alert__action {
25
+ margin-top: var(--size-16);
30
26
  }
31
27
  }
32
28
 
29
+ .wds-Button {
30
+ --Button-color: var(--color-content-primary);
31
+ --Button-color-hover: var(--color-content-primary);
32
+ --Button-color-active: var(--color-content-primary);
33
+
34
+ --Button-background: var(--color-background-screen);
35
+ --Button-background-hover: var(--color-sentiment-interactive-primary-hover);
36
+ --Button-background-active: var(--color-sentiment-interactive-primary-active);
37
+ }
38
+
39
+ .alert-warning .wds-Button {
40
+ --Button-background-hover: var(--color-sentiment-interactive-secondary-neutral-hover);
41
+ --Button-background-active: var(--color-sentiment-interactive-secondary-neutral-active);
42
+ }
33
43
 
34
44
  @media (--screen-md) {
35
- .critical-comms {
36
- .alert-warning {
37
- .alert__message {
38
- flex-direction: row;
39
- justify-content: space-between;
40
- align-items: center;
41
- width: 100%;
42
-
43
- .alert__action {
44
- margin-top: 0;
45
- margin-left: var(--padding-small);
46
- }
45
+ .alert-warning,
46
+ .alert-negative,
47
+ .alert-neutral {
48
+ .alert__message {
49
+ flex-direction: row;
50
+ justify-content: space-between;
51
+ align-items: center;
52
+ width: 100%;
53
+
54
+ .alert__action {
55
+ margin-top: 0;
56
+ margin-left: var(--padding-small);
47
57
  }
48
58
  }
49
59
  }
50
60
  }
51
- }
61
+ }
@@ -1,7 +1,5 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-webpack5';
2
-
3
2
  import CriticalCommsBanner from '.';
4
- import { withVariantConfig } from '../../.storybook/helpers';
5
3
 
6
4
  export default {
7
5
  component: CriticalCommsBanner,
@@ -18,21 +16,17 @@ export const Basic = {
18
16
  },
19
17
  };
20
18
 
21
- export const Variants: Story = {
19
+ export const Sentiments: Story = {
20
+ render: (args) => (
21
+ <>
22
+ <CriticalCommsBanner {...args} sentiment="negative" title="Your account is overdrawn" />
23
+ <CriticalCommsBanner {...args} sentiment="warning" title="Your account needs attention" />
24
+ <CriticalCommsBanner {...args} sentiment="neutral" title="Your transfer is being processed" />
25
+ </>
26
+ ),
22
27
  args: {
23
- title: 'Your account is overdrawn',
24
- subtitle: 'Add money within the next 30 days',
25
- action: { label: 'Take action', href: 'https://wise.com' },
26
- },
27
- ...withVariantConfig(['default', 'dark', 'rtl']),
28
- };
29
-
30
- export const Mobile: Story = {
31
- tags: ['!autodocs'],
32
- args: {
33
- title: 'Your account is overdrawn',
34
28
  subtitle: 'Add money within the next 30 days',
35
29
  action: { label: 'Take action', href: 'https://wise.com' },
30
+ className: 'm-b-1',
36
31
  },
37
- ...withVariantConfig(['default', 'dark', 'rtl', 'mobile']),
38
32
  };
@@ -0,0 +1,70 @@
1
+ import { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import CriticalCommsBanner from '.';
4
+ import { withVariantConfig } from '../../.storybook/helpers';
5
+
6
+ export default {
7
+ component: CriticalCommsBanner,
8
+ title: 'Prompts/CriticalCommsBanner/tests',
9
+ tags: ['!autodocs'],
10
+ } satisfies Meta<typeof CriticalCommsBanner>;
11
+
12
+ type Story = StoryObj<typeof CriticalCommsBanner>;
13
+
14
+ export const Sentiments: Story = {
15
+ render: (args) => (
16
+ <>
17
+ <CriticalCommsBanner {...args} sentiment="negative" />
18
+ <CriticalCommsBanner {...args} sentiment="warning" />
19
+ <CriticalCommsBanner {...args} sentiment="neutral" />
20
+ </>
21
+ ),
22
+ args: {
23
+ subtitle: 'Add money within the next 30 days',
24
+ action: { label: 'Take action', href: 'https://wise.com' },
25
+ className: 'm-b-1',
26
+ },
27
+ ...withVariantConfig([
28
+ 'default',
29
+ 'dark',
30
+ 'bright-green',
31
+ 'forest-green',
32
+ 'business',
33
+ 'business--forest-green',
34
+ 'business--bright-green',
35
+ ]),
36
+ };
37
+
38
+ export const RTL: Story = {
39
+ render: (args) => (
40
+ <>
41
+ <CriticalCommsBanner {...args} sentiment="negative" />
42
+ <CriticalCommsBanner {...args} sentiment="warning" />
43
+ <CriticalCommsBanner {...args} sentiment="neutral" />
44
+ </>
45
+ ),
46
+ args: {
47
+ subtitle: 'Add money within the next 30 days',
48
+ action: { label: 'Take action', href: 'https://wise.com' },
49
+ className: 'm-b-1',
50
+ },
51
+ ...withVariantConfig(['rtl']),
52
+ };
53
+
54
+ export const Mobile: Story = {
55
+ args: {
56
+ subtitle: 'Add money within the next 30 days',
57
+ action: { label: 'Take action', href: 'https://wise.com' },
58
+ className: 'm-b-1',
59
+ },
60
+ ...withVariantConfig(['mobile']),
61
+ };
62
+
63
+ export const Zoom: Story = {
64
+ args: {
65
+ subtitle: 'Add money within the next 30 days',
66
+ action: { label: 'Take action', href: 'https://wise.com' },
67
+ className: 'm-b-1',
68
+ },
69
+ ...withVariantConfig(['400%']),
70
+ };