@12-apps/ui 6.76.1 → 6.77.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/NATIVE.md +1 -1
  2. package/dist/_internal/components/data-display/Alert/Alert.content.js +20 -0
  3. package/dist/_internal/components/data-display/Alert/Alert.content.js.map +1 -0
  4. package/dist/_internal/components/data-display/Alert/Alert.js +5 -3
  5. package/dist/_internal/components/data-display/Alert/Alert.metrics.js +3 -1
  6. package/dist/_internal/components/data-display/Alert/Alert.placement.js +8 -0
  7. package/dist/_internal/components/data-display/Alert/Alert.placement.js.map +1 -0
  8. package/dist/_internal/components/data-display/Alert/Alert.styles.js +2 -2
  9. package/dist/_internal/components/data-display/Banner/Banner.js +3 -3
  10. package/dist/_internal/components/data-display/Banner/Banner.styles.js +2 -2
  11. package/dist/_internal/components/data-display/CardKit/card-actions-context.js +6 -4
  12. package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +7 -5
  13. package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +7 -5
  14. package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +6 -4
  15. package/dist/_internal/components/data-display/index.js +7 -5
  16. package/dist/_internal/components/feedback/NotificationStatusNotice/NotificationStatusNotice.js +6 -4
  17. package/dist/_internal/components/feedback/index.js +6 -4
  18. package/dist/_internal/components/index.js +8 -6
  19. package/dist/{chunk-3LHZCVGM.js → chunk-5ZY3IIIG.js} +3 -1
  20. package/dist/{chunk-3LHZCVGM.js.map → chunk-5ZY3IIIG.js.map} +1 -1
  21. package/dist/chunk-7HZ36QDH.js +82 -0
  22. package/dist/chunk-7HZ36QDH.js.map +1 -0
  23. package/dist/{chunk-ERI6JC4M.js → chunk-7KZEQJPQ.js} +2 -2
  24. package/dist/{chunk-REEV7CLC.js → chunk-HDXUQKJ5.js} +2 -2
  25. package/dist/{chunk-JYAXTRNH.js → chunk-L5RS6QAT.js} +2 -2
  26. package/dist/{chunk-52CLMR47.js → chunk-MKBIIYJM.js} +2 -2
  27. package/dist/{chunk-DQJFCVXW.js → chunk-NQCTANF3.js} +32 -47
  28. package/dist/chunk-NQCTANF3.js.map +1 -0
  29. package/dist/{chunk-44AXT2SN.js → chunk-QIP37Y2C.js} +2 -2
  30. package/dist/chunk-QXUXO22M.js +9 -0
  31. package/dist/chunk-QXUXO22M.js.map +1 -0
  32. package/dist/{chunk-LY7PBZ3Z.js → chunk-RCTHCXS6.js} +2 -2
  33. package/dist/{chunk-3VLVLZ6W.js → chunk-XGKUXYAD.js} +2 -2
  34. package/dist/{chunk-ZBOGZLX3.js → chunk-Z4ZFJ565.js} +3 -3
  35. package/dist/data-display/Alert.js +5 -3
  36. package/dist/data-display/Banner.js +3 -3
  37. package/dist/data-display/CardKit.js +7 -5
  38. package/dist/data-display/DataViews.js +7 -5
  39. package/dist/feedback/NotificationStatusNotice.js +6 -4
  40. package/dist/native/data-display/Alert.js.map +1 -1
  41. package/dist/types/components/data-display/Alert/Alert.content.d.ts +18 -0
  42. package/dist/types/components/data-display/Alert/Alert.content.d.ts.map +1 -0
  43. package/dist/types/components/data-display/Alert/Alert.d.ts.map +1 -1
  44. package/dist/types/components/data-display/Alert/Alert.metrics.d.ts +5 -0
  45. package/dist/types/components/data-display/Alert/Alert.metrics.d.ts.map +1 -1
  46. package/dist/types/components/data-display/Alert/Alert.placement.d.ts +37 -0
  47. package/dist/types/components/data-display/Alert/Alert.placement.d.ts.map +1 -0
  48. package/dist/types/components/data-display/Alert/Alert.types.d.ts +8 -0
  49. package/dist/types/components/data-display/Alert/Alert.types.d.ts.map +1 -1
  50. package/dist/types/components/data-display/Alert/index.d.ts +1 -1
  51. package/dist/types/components/data-display/Alert/index.d.ts.map +1 -1
  52. package/dist/types-native/components/data-display/Alert/Alert.metrics.d.ts +5 -0
  53. package/dist/types-native/components/data-display/Alert/Alert.metrics.d.ts.map +1 -1
  54. package/package.json +1 -1
  55. package/src/components/data-display/Alert/Alert.content.tsx +90 -0
  56. package/src/components/data-display/Alert/Alert.md +19 -0
  57. package/src/components/data-display/Alert/Alert.metrics.ts +2 -0
  58. package/src/components/data-display/Alert/Alert.placement.ts +45 -0
  59. package/src/components/data-display/Alert/Alert.tsx +41 -61
  60. package/src/components/data-display/Alert/Alert.types.ts +10 -1
  61. package/src/components/data-display/Alert/index.ts +7 -1
  62. package/dist/chunk-DQJFCVXW.js.map +0 -1
  63. /package/dist/{chunk-ERI6JC4M.js.map → chunk-7KZEQJPQ.js.map} +0 -0
  64. /package/dist/{chunk-REEV7CLC.js.map → chunk-HDXUQKJ5.js.map} +0 -0
  65. /package/dist/{chunk-JYAXTRNH.js.map → chunk-L5RS6QAT.js.map} +0 -0
  66. /package/dist/{chunk-52CLMR47.js.map → chunk-MKBIIYJM.js.map} +0 -0
  67. /package/dist/{chunk-44AXT2SN.js.map → chunk-QIP37Y2C.js.map} +0 -0
  68. /package/dist/{chunk-LY7PBZ3Z.js.map → chunk-RCTHCXS6.js.map} +0 -0
  69. /package/dist/{chunk-3VLVLZ6W.js.map → chunk-XGKUXYAD.js.map} +0 -0
  70. /package/dist/{chunk-ZBOGZLX3.js.map → chunk-Z4ZFJ565.js.map} +0 -0
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { AlertProps } from './Alert.types';
3
+ /**
4
+ * The message column: title, description, then whatever the caller put inside.
5
+ *
6
+ * `titleIcon` is the icon when it sits on the title's line
7
+ * (`iconPlacement: 'title'`, see `Alert.placement.ts`). The title then shares
8
+ * a row with it, and the description below is a sibling of that row — so it
9
+ * starts at the column's own edge rather than under the title's first letter.
10
+ */
11
+ export declare const AlertContent: React.FC<{
12
+ title?: AlertProps['title'];
13
+ description?: AlertProps['description'];
14
+ titleIcon?: React.ReactNode;
15
+ dataTestId?: string;
16
+ children?: React.ReactNode;
17
+ }>;
18
+ //# sourceMappingURL=Alert.content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.content.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.content.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAQhD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC;IAClC,KAAK,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5B,WAAW,CAAC,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC;IACxC,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CA2DA,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,MAAM,OAAO,CAAC;AA4R1B,eAAO,MAAM,KAAK;;;;;;;;iDA+EhB,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,qCAAsC,CAAC"}
1
+ {"version":3,"file":"Alert.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,MAAM,OAAO,CAAC;AA4Q1B,eAAO,MAAM,KAAK;;;;;;;;iDA4EhB,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,qCAAsC,CAAC"}
@@ -54,6 +54,11 @@ export declare const DESCRIPTION: {
54
54
  readonly fontSize: 14.8;
55
55
  readonly opacity: 0.9;
56
56
  };
57
+ /** The icon on the title's line (`iconPlacement: 'title'`): 10px from the title, centred on its first line (1.5). */
58
+ export declare const TITLE_ICON: {
59
+ readonly gapUnits: 1.25;
60
+ readonly lineHeight: 1.5;
61
+ };
57
62
  /** The icon slot: MUI's 22px glyph at 0.9, 14px from the words, 2px down (MUI's 7px stays below). */
58
63
  export declare const ICON_SLOT: {
59
64
  readonly size: 22;
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.metrics.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.metrics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Alert.metrics.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { Theme } from '@mui/material/styles/index.js';
2
+ /**
3
+ * WHERE THE ICON SITS — a choice the HOST makes once, in its theme.
4
+ *
5
+ * - `'gutter'` (the default, and what every alert did before this existed):
6
+ * the icon has a column of its own, and the title and the description both
7
+ * start to its right. A two-line description wraps inside that narrower
8
+ * column and leaves an empty strip under the icon.
9
+ * - `'title'`: the icon sits on the TITLE's line, and the description below
10
+ * starts at the alert's own edge and uses the whole line.
11
+ *
12
+ * It is a theme value, like `uiSurfaces`, because it is a house style rather
13
+ * than a per-call decision: a host that wants it wants it on every alert, and a
14
+ * per-call prop would have to be spelled at each of them. The `iconPlacement`
15
+ * prop still exists for the one call site that must differ, and wins.
16
+ *
17
+ * An alert with no title, or no icon, has no title line to put the icon on;
18
+ * `'title'` then renders exactly as `'gutter'` does.
19
+ */
20
+ export type AlertIconPlacement = 'gutter' | 'title';
21
+ /** What a host may state about alerts, on its theme. */
22
+ export interface UiAlertTheme {
23
+ iconPlacement?: AlertIconPlacement;
24
+ }
25
+ declare module '@mui/material/styles' {
26
+ interface Theme {
27
+ /** Alert layout the host states. See `Alert.placement.ts`. */
28
+ uiAlert?: UiAlertTheme;
29
+ }
30
+ interface ThemeOptions {
31
+ /** Alert layout, for every alert under this theme. See `Alert.placement.ts`. */
32
+ uiAlert?: UiAlertTheme;
33
+ }
34
+ }
35
+ /** The placement in force: the call site's, else the theme's, else `'gutter'`. */
36
+ export declare function resolveIconPlacement(theme: Theme, stated: AlertIconPlacement | undefined): AlertIconPlacement;
37
+ //# sourceMappingURL=Alert.placement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Alert.placement.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.placement.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAC;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpD,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACpC;AAED,OAAO,QAAQ,sBAAsB,CAAC;IACpC,UAAU,KAAK;QACb,8DAA8D;QAC9D,OAAO,CAAC,EAAE,YAAY,CAAC;KACxB;IACD,UAAU,YAAY;QACpB,gFAAgF;QAChF,OAAO,CAAC,EAAE,YAAY,CAAC;KACxB;CACF;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,kBAAkB,CAEpB"}
@@ -1,8 +1,16 @@
1
1
  import type { AlertProps as MuiAlertProps } from '@mui/material/Alert/index.js';
2
2
  import type { AlertBaseFields, AlertDismiss } from './Alert.base';
3
+ import type { AlertIconPlacement } from './Alert.placement';
3
4
  export type { AlertBaseFields, AlertBaseProps, AlertDismiss, AlertVariant } from './Alert.base';
5
+ export type { AlertIconPlacement, UiAlertTheme } from './Alert.placement';
4
6
  /** The web `Alert`'s non-dismiss half: the shared fields, plus MUI's `Alert` props where they do not overlap. */
5
7
  export interface AlertBase extends AlertBaseFields, Omit<MuiAlertProps, 'variant' | 'color' | 'role' | keyof AlertBaseFields> {
8
+ /**
9
+ * Where the icon sits: its own column (`'gutter'`) or on the title's line
10
+ * (`'title'`). Unset, the theme's `uiAlert.iconPlacement` decides, else
11
+ * `'gutter'`. Web only. See `Alert.placement.ts`.
12
+ */
13
+ iconPlacement?: AlertIconPlacement;
6
14
  }
7
15
  export type AlertProps = AlertBase & AlertDismiss;
8
16
  //# sourceMappingURL=Alert.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.types.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAElE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEhG,iHAAiH;AACjH,MAAM,WAAW,SACf,SAAQ,eAAe,EACrB,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,eAAe,CAAC;CAAG;AAEhF,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,YAAY,CAAC"}
1
+ {"version":3,"file":"Alert.types.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,IAAI,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAChG,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE1E,iHAAiH;AACjH,MAAM,WAAW,SACf,SAAQ,eAAe,EACrB,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,eAAe,CAAC;IAC3E;;;;OAIG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACpC;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,YAAY,CAAC"}
@@ -1,3 +1,3 @@
1
1
  export { Alert } from './Alert';
2
- export type { AlertBaseProps, AlertProps, AlertVariant } from './Alert.types';
2
+ export type { AlertBaseProps, AlertIconPlacement, AlertProps, AlertVariant, UiAlertTheme, } from './Alert.types';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,YAAY,GACb,MAAM,eAAe,CAAC"}
@@ -54,6 +54,11 @@ export declare const DESCRIPTION: {
54
54
  readonly fontSize: 14.8;
55
55
  readonly opacity: 0.9;
56
56
  };
57
+ /** The icon on the title's line (`iconPlacement: 'title'`): 10px from the title, centred on its first line (1.5). */
58
+ export declare const TITLE_ICON: {
59
+ readonly gapUnits: 1.25;
60
+ readonly lineHeight: 1.5;
61
+ };
57
62
  /** The icon slot: MUI's 22px glyph at 0.9, 14px from the words, 2px down (MUI's 7px stays below). */
58
63
  export declare const ICON_SLOT: {
59
64
  readonly size: 22;
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.metrics.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.metrics.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Alert.metrics.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/Alert/Alert.metrics.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/ui",
3
- "version": "6.76.1",
3
+ "version": "6.77.1",
4
4
  "license": "MIT",
5
5
  "packageManager": "pnpm@10.34.5",
6
6
  "type": "module",
@@ -0,0 +1,90 @@
1
+ import AlertTitle from '@mui/material/AlertTitle/index.js';
2
+ import Box from '@mui/material/Box/index.js';
3
+ import React from 'react';
4
+
5
+ import { sxRem } from '../../../tokens/relative';
6
+
7
+ import { testIdFor } from './Alert.helpers';
8
+ import { DESCRIPTION, ICON_SLOT, TITLE, TITLE_ICON } from './Alert.metrics';
9
+ import type { AlertProps } from './Alert.types';
10
+
11
+ const BREAK_WORDS = {
12
+ wordWrap: 'break-word',
13
+ overflowWrap: 'break-word',
14
+ wordBreak: 'break-word',
15
+ } as const;
16
+
17
+ /**
18
+ * The message column: title, description, then whatever the caller put inside.
19
+ *
20
+ * `titleIcon` is the icon when it sits on the title's line
21
+ * (`iconPlacement: 'title'`, see `Alert.placement.ts`). The title then shares
22
+ * a row with it, and the description below is a sibling of that row — so it
23
+ * starts at the column's own edge rather than under the title's first letter.
24
+ */
25
+ export const AlertContent: React.FC<{
26
+ title?: AlertProps['title'];
27
+ description?: AlertProps['description'];
28
+ titleIcon?: React.ReactNode;
29
+ dataTestId?: string;
30
+ children?: React.ReactNode;
31
+ }> = ({ title, description, titleIcon, dataTestId, children }) => {
32
+ const heading = title ? (
33
+ <AlertTitle
34
+ data-testid={testIdFor(dataTestId, 'title')}
35
+ sx={{
36
+ fontWeight: TITLE.fontWeight,
37
+ fontSize: sxRem(TITLE.fontSize),
38
+ // On the icon's row MUI's 2px lift would push the title off the glyph,
39
+ // and the line height is stated so the glyph can be centred on it.
40
+ ...(titleIcon
41
+ ? { margin: 0, lineHeight: TITLE_ICON.lineHeight }
42
+ : { marginBottom: description ? TITLE.marginBottomUnits : 0 }),
43
+ ...BREAK_WORDS,
44
+ }}
45
+ >
46
+ {title}
47
+ </AlertTitle>
48
+ ) : null;
49
+
50
+ return (
51
+ <>
52
+ {titleIcon && heading ? (
53
+ <Box
54
+ data-testid={testIdFor(dataTestId, 'title-row')}
55
+ sx={{
56
+ display: 'flex',
57
+ // The FIRST line, not the block: a title that wraps to three lines
58
+ // on a phone would otherwise float its icon beside the second.
59
+ alignItems: 'flex-start',
60
+ gap: TITLE_ICON.gapUnits,
61
+ marginBottom: description ? TITLE.marginBottomUnits : 0,
62
+ '& > span': {
63
+ display: 'flex',
64
+ flex: 'none',
65
+ alignItems: 'center',
66
+ fontSize: sxRem(ICON_SLOT.size),
67
+ // One title line tall, so the glyph is centred on that line.
68
+ height: sxRem(TITLE.fontSize * TITLE_ICON.lineHeight),
69
+ },
70
+ }}
71
+ >
72
+ {titleIcon}
73
+ {heading}
74
+ </Box>
75
+ ) : (
76
+ heading
77
+ )}
78
+ {description && (
79
+ <Box
80
+ component="div"
81
+ data-testid={testIdFor(dataTestId, 'message')}
82
+ sx={{ opacity: DESCRIPTION.opacity, fontSize: sxRem(DESCRIPTION.fontSize), ...BREAK_WORDS }}
83
+ >
84
+ {description}
85
+ </Box>
86
+ )}
87
+ {children}
88
+ </>
89
+ );
90
+ };
@@ -34,6 +34,7 @@ A beautiful and versatile Alert component with multiple variants, animations, an
34
34
  | `pulse` | `boolean` | `false` | Add a pulsing animation effect |
35
35
  | `animate` | `boolean` | `true` | Whether to animate the alert on mount |
36
36
  | `announce` | `'polite' \| 'assertive'` | - | Sets `role` and `aria-live` together — see [Accessibility](#accessibility) |
37
+ | `iconPlacement` | `'gutter' \| 'title'` | theme's `uiAlert.iconPlacement`, else `'gutter'` | Icon in its own column, or on the title's line — see [Theming](#theming) |
37
38
  | `className` | `string` | - | Additional CSS classes |
38
39
 
39
40
  ## Usage Examples
@@ -188,6 +189,23 @@ The Alert component integrates with the MUI theme system and responds to:
188
189
  - Border radius settings
189
190
  - Shadow/elevation tokens
190
191
 
192
+ ### Where the icon sits — `uiAlert.iconPlacement`
193
+
194
+ A house style, so the host states it ONCE on its theme rather than at each call:
195
+
196
+ ```ts
197
+ createTheme({ uiAlert: { iconPlacement: 'title' } });
198
+ ```
199
+
200
+ | value | layout |
201
+ |---|---|
202
+ | `'gutter'` (default) | the icon has a column of its own; title and description start to its right |
203
+ | `'title'` | the icon sits on the title's line; the description starts at the alert's edge and uses the whole line |
204
+
205
+ An alert with no title, or with `showIcon={false}`, has no title line for the
206
+ icon, so `'title'` renders as `'gutter'`. A call site that must differ passes
207
+ `iconPlacement`, which wins over the theme. Web only.
208
+
191
209
  ## Testing
192
210
 
193
211
  ### Test IDs
@@ -198,6 +216,7 @@ The Alert component integrates with the MUI theme system and responds to:
198
216
  | `alert-icon` | Icon | Alert severity icon |
199
217
  | `alert-title` | Title | Alert title text |
200
218
  | `alert-message` | Message | Alert message content |
219
+ | `alert-title-row` | Row | Icon and title together (`iconPlacement: 'title'` only) |
201
220
  | `alert-close` | Button | Close button (when closable) |
202
221
 
203
222
  Custom test IDs can be provided via the `dataTestId` prop:
@@ -40,6 +40,8 @@ export const TITLE = { fontSize: 16.8, fontWeight: 600, marginTop: -2, marginBot
40
40
  /** The description: 0.925rem, slightly faded. */
41
41
  export const DESCRIPTION = { fontSize: 14.8, opacity: 0.9 } as const;
42
42
 
43
+ /** The icon on the title's line (`iconPlacement: 'title'`): 10px from the title, centred on its first line (1.5). */
44
+ export const TITLE_ICON = { gapUnits: 1.25, lineHeight: 1.5 } as const;
43
45
  /** The icon slot: MUI's 22px glyph at 0.9, 14px from the words, 2px down (MUI's 7px stays below). */
44
46
  export const ICON_SLOT = { size: 22, opacity: 0.9, marginRightUnits: 1.75, paddingTopUnits: 0.25, paddingBottom: 7 } as const;
45
47
  /** The action slot: MUI's `4px 0 0 16px` with the 16px ours, pushed right, 8px into the padding. */
@@ -0,0 +1,45 @@
1
+ import type { Theme } from '@mui/material/styles/index.js';
2
+
3
+ /**
4
+ * WHERE THE ICON SITS — a choice the HOST makes once, in its theme.
5
+ *
6
+ * - `'gutter'` (the default, and what every alert did before this existed):
7
+ * the icon has a column of its own, and the title and the description both
8
+ * start to its right. A two-line description wraps inside that narrower
9
+ * column and leaves an empty strip under the icon.
10
+ * - `'title'`: the icon sits on the TITLE's line, and the description below
11
+ * starts at the alert's own edge and uses the whole line.
12
+ *
13
+ * It is a theme value, like `uiSurfaces`, because it is a house style rather
14
+ * than a per-call decision: a host that wants it wants it on every alert, and a
15
+ * per-call prop would have to be spelled at each of them. The `iconPlacement`
16
+ * prop still exists for the one call site that must differ, and wins.
17
+ *
18
+ * An alert with no title, or no icon, has no title line to put the icon on;
19
+ * `'title'` then renders exactly as `'gutter'` does.
20
+ */
21
+ export type AlertIconPlacement = 'gutter' | 'title';
22
+
23
+ /** What a host may state about alerts, on its theme. */
24
+ export interface UiAlertTheme {
25
+ iconPlacement?: AlertIconPlacement;
26
+ }
27
+
28
+ declare module '@mui/material/styles' {
29
+ interface Theme {
30
+ /** Alert layout the host states. See `Alert.placement.ts`. */
31
+ uiAlert?: UiAlertTheme;
32
+ }
33
+ interface ThemeOptions {
34
+ /** Alert layout, for every alert under this theme. See `Alert.placement.ts`. */
35
+ uiAlert?: UiAlertTheme;
36
+ }
37
+ }
38
+
39
+ /** The placement in force: the call site's, else the theme's, else `'gutter'`. */
40
+ export function resolveIconPlacement(
41
+ theme: Theme,
42
+ stated: AlertIconPlacement | undefined,
43
+ ): AlertIconPlacement {
44
+ return stated ?? theme.uiAlert?.iconPlacement ?? 'gutter';
45
+ }
@@ -1,16 +1,17 @@
1
1
  import Close from '@mui/icons-material/Close';
2
2
  import MuiAlert from '@mui/material/Alert/index.js';
3
- import AlertTitle from '@mui/material/AlertTitle/index.js';
4
3
  import Box from '@mui/material/Box/index.js';
5
4
  import Collapse from '@mui/material/Collapse/index.js';
6
5
  import IconButton from '@mui/material/IconButton/index.js';
7
- import { alpha, styled } from '@mui/material/styles/index.js';
6
+ import { alpha, styled, useTheme } from '@mui/material/styles/index.js';
8
7
  import type { Theme } from '@mui/material/styles/index.js';
9
8
  import React from 'react';
10
9
 
11
10
  import { absoluteInk, modeInk } from '../../../tokens/ink';
12
11
 
12
+ import { AlertContent } from './Alert.content';
13
13
  import { resolveAlertProps, resolveAnnouncement, testIdFor } from './Alert.helpers';
14
+ import { resolveIconPlacement, type AlertIconPlacement } from './Alert.placement';
14
15
  import type { AlertPalette } from './Alert.metrics';
15
16
  import {
16
17
  ACTIVE,
@@ -21,12 +22,10 @@ import {
21
22
  CLOSE_BUTTON,
22
23
  CLOSE_DELAY_MS,
23
24
  COLLAPSE_MS,
24
- DESCRIPTION,
25
25
  FADE_IN,
26
26
  FOCUS,
27
27
  HOVER,
28
28
  seconds,
29
- TITLE,
30
29
  } from './Alert.metrics';
31
30
  import {
32
31
  alertEmphasisStyles,
@@ -39,7 +38,7 @@ import {
39
38
  import type { AlertColor } from '@mui/material/Alert/index.js';
40
39
  import type { AlertProps } from './Alert.types';
41
40
  import { resolveTestId, withoutTestIdProps } from '../../../platform/test-id';
42
- import { rem, rems, sxRem } from '../../../tokens/relative';
41
+ import { rem, rems } from '../../../tokens/relative';
43
42
 
44
43
  const transition = `all ${seconds(ALERT_TRANSITION_MS)} ${ALERT_EASING}`;
45
44
 
@@ -176,47 +175,6 @@ const MUI_SEVERITY: Record<string, AlertColor> = {
176
175
  const toMuiSeverity = (variant: string): AlertColor =>
177
176
  MUI_SEVERITY[variant] ?? (variant as AlertColor);
178
177
 
179
- const AlertContent: React.FC<{
180
- title?: AlertProps['title'];
181
- description?: AlertProps['description'];
182
- dataTestId?: string;
183
- children?: React.ReactNode;
184
- }> = ({ title, description, dataTestId, children }) => (
185
- <>
186
- {title && (
187
- <AlertTitle
188
- data-testid={testIdFor(dataTestId, 'title')}
189
- sx={{
190
- fontWeight: TITLE.fontWeight,
191
- fontSize: sxRem(TITLE.fontSize),
192
- marginBottom: description ? TITLE.marginBottomUnits : 0,
193
- wordWrap: 'break-word',
194
- overflowWrap: 'break-word',
195
- wordBreak: 'break-word',
196
- }}
197
- >
198
- {title}
199
- </AlertTitle>
200
- )}
201
- {description && (
202
- <Box
203
- component="div"
204
- data-testid={testIdFor(dataTestId, 'message')}
205
- sx={{
206
- opacity: DESCRIPTION.opacity,
207
- fontSize: sxRem(DESCRIPTION.fontSize),
208
- wordWrap: 'break-word',
209
- overflowWrap: 'break-word',
210
- wordBreak: 'break-word',
211
- }}
212
- >
213
- {description}
214
- </Box>
215
- )}
216
- {children}
217
- </>
218
- );
219
-
220
178
  const AlertCloseButton: React.FC<{ dataTestId?: string; label: string; onClose: () => void }> = ({
221
179
  dataTestId,
222
180
  label,
@@ -290,6 +248,31 @@ function dismissButton(
290
248
  return <AlertCloseButton dataTestId={dataTestId} label={props.closeLabel} onClose={onClose} />;
291
249
  }
292
250
 
251
+ /**
252
+ * Where the icon goes: MUI's own slot (`slot`), or the title's row
253
+ * (`titleIcon`) — never both. On the title's row only when there IS a title
254
+ * and an icon to put on it (`Alert.placement.ts`).
255
+ */
256
+ function placeIcon({
257
+ icon,
258
+ dataTestId,
259
+ hasTitle,
260
+ placement,
261
+ }: {
262
+ icon: React.ReactNode;
263
+ dataTestId: string | undefined;
264
+ hasTitle: boolean;
265
+ placement: AlertIconPlacement;
266
+ }): { slot: React.ReactNode; titleIcon?: React.ReactNode } {
267
+ if (icon === null) return { slot: false };
268
+ const glyph = (
269
+ <Box component="span" data-testid={testIdFor(dataTestId, 'icon')}>
270
+ {icon}
271
+ </Box>
272
+ );
273
+ return placement === 'title' && hasTitle ? { slot: false, titleIcon: glyph } : { slot: glyph };
274
+ }
275
+
293
276
  export const Alert = React.forwardRef<HTMLDivElement, AlertProps>((alertProps, ref) => {
294
277
  const {
295
278
  variant,
@@ -303,6 +286,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>((alertProps, r
303
286
  description,
304
287
  children,
305
288
  animate,
289
+ iconPlacement,
306
290
  role: _role, announce: _announce, // resolved from `alertProps` — see `resolveAnnouncement`
307
291
  'aria-atomic': ariaAtomic,
308
292
  ...others
@@ -319,6 +303,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>((alertProps, r
319
303
 
320
304
  // Depends on `variant`, so it cannot live in the static defaults above.
321
305
  const announcement = resolveAnnouncement(alertProps, variant);
306
+ const theme = useTheme();
322
307
  const [open, setOpen] = React.useState(true);
323
308
  const [isClosing, setIsClosing] = React.useState(false);
324
309
 
@@ -332,19 +317,12 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>((alertProps, r
332
317
 
333
318
  const severity = toMuiSeverity(variant);
334
319
 
335
- const displayIcon = showIcon ? (
336
- <Box component="span" data-testid={testIdFor(dataTestId, 'icon')}>
337
- {icon || getVariantIcon(variant)}
338
- </Box>
339
- ) : (
340
- false
341
- );
342
-
343
- const content = (
344
- <AlertContent title={title} description={description} dataTestId={dataTestId}>
345
- {children}
346
- </AlertContent>
347
- );
320
+ const { slot, titleIcon } = placeIcon({
321
+ icon: showIcon ? (icon ?? getVariantIcon(variant)) : null,
322
+ dataTestId,
323
+ hasTitle: Boolean(title),
324
+ placement: resolveIconPlacement(theme, iconPlacement),
325
+ });
348
326
 
349
327
  return (
350
328
  <Collapse in={open && !isClosing} timeout={COLLAPSE_MS}>
@@ -357,7 +335,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>((alertProps, r
357
335
  glow={glow}
358
336
  pulse={pulse}
359
337
  animate={animate}
360
- icon={displayIcon}
338
+ icon={slot}
361
339
  role={announcement.role}
362
340
  aria-live={announcement['aria-live']}
363
341
  aria-atomic={ariaAtomic}
@@ -365,7 +343,9 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>((alertProps, r
365
343
  action={dismissButton(alertProps, dataTestId, handleClose)}
366
344
  {...props}
367
345
  >
368
- {content}
346
+ <AlertContent title={title} description={description} titleIcon={titleIcon} dataTestId={dataTestId}>
347
+ {children}
348
+ </AlertContent>
369
349
  </StyledAlert>
370
350
  </Collapse>
371
351
  );
@@ -1,12 +1,21 @@
1
1
  import type { AlertProps as MuiAlertProps } from '@mui/material/Alert/index.js';
2
2
 
3
3
  import type { AlertBaseFields, AlertDismiss } from './Alert.base';
4
+ import type { AlertIconPlacement } from './Alert.placement';
4
5
 
5
6
  export type { AlertBaseFields, AlertBaseProps, AlertDismiss, AlertVariant } from './Alert.base';
7
+ export type { AlertIconPlacement, UiAlertTheme } from './Alert.placement';
6
8
 
7
9
  /** The web `Alert`'s non-dismiss half: the shared fields, plus MUI's `Alert` props where they do not overlap. */
8
10
  export interface AlertBase
9
11
  extends AlertBaseFields,
10
- Omit<MuiAlertProps, 'variant' | 'color' | 'role' | keyof AlertBaseFields> {}
12
+ Omit<MuiAlertProps, 'variant' | 'color' | 'role' | keyof AlertBaseFields> {
13
+ /**
14
+ * Where the icon sits: its own column (`'gutter'`) or on the title's line
15
+ * (`'title'`). Unset, the theme's `uiAlert.iconPlacement` decides, else
16
+ * `'gutter'`. Web only. See `Alert.placement.ts`.
17
+ */
18
+ iconPlacement?: AlertIconPlacement;
19
+ }
11
20
 
12
21
  export type AlertProps = AlertBase & AlertDismiss;
@@ -1,2 +1,8 @@
1
1
  export { Alert } from './Alert';
2
- export type { AlertBaseProps, AlertProps, AlertVariant } from './Alert.types';
2
+ export type {
3
+ AlertBaseProps,
4
+ AlertIconPlacement,
5
+ AlertProps,
6
+ AlertVariant,
7
+ UiAlertTheme,
8
+ } from './Alert.types';
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/data-display/Alert/Alert.tsx"],"sourcesContent":["import Close from '@mui/icons-material/Close';\nimport MuiAlert from '@mui/material/Alert/index.js';\nimport AlertTitle from '@mui/material/AlertTitle/index.js';\nimport Box from '@mui/material/Box/index.js';\nimport Collapse from '@mui/material/Collapse/index.js';\nimport IconButton from '@mui/material/IconButton/index.js';\nimport { alpha, styled } from '@mui/material/styles/index.js';\nimport type { Theme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { absoluteInk, modeInk } from '../../../tokens/ink';\n\nimport { resolveAlertProps, resolveAnnouncement, testIdFor } from './Alert.helpers';\nimport type { AlertPalette } from './Alert.metrics';\nimport {\n ACTIVE,\n GLOW,\n ALERT_EASING,\n ALERT_RADIUS_UNITS,\n ALERT_TRANSITION_MS,\n CLOSE_BUTTON,\n CLOSE_DELAY_MS,\n COLLAPSE_MS,\n DESCRIPTION,\n FADE_IN,\n FOCUS,\n HOVER,\n seconds,\n TITLE,\n} from './Alert.metrics';\nimport {\n alertEmphasisStyles,\n alertLayoutStyles,\n alertVariantStyles,\n fadeInScale,\n getColorFromTheme,\n getVariantIcon,\n} from './Alert.styles';\nimport type { AlertColor } from '@mui/material/Alert/index.js';\nimport type { AlertProps } from './Alert.types';\nimport { resolveTestId, withoutTestIdProps } from '../../../platform/test-id';\nimport { rem, rems, sxRem } from '../../../tokens/relative';\n\nconst transition = `all ${seconds(ALERT_TRANSITION_MS)} ${ALERT_EASING}`;\n\n// Define animations\n/**\n * The pointer states' shadow: a flat tint, plus the glow when the alert has one.\n *\n * The tint is an inset spread at 100vmax, which always exceeds the element, so\n * it covers the surface edge to edge. It paints ABOVE the background and BELOW\n * the content, which is what keeps the ink's own contrast out of these states\n * entirely — see `ACTIVE` in `Alert.metrics.ts` for why a layer replaced the\n * opacity dip and the brightness multiplier that each preceded it.\n *\n * The glow is COMPOSED rather than replaced, so hovering a glowing alert keeps\n * its glow; `glowStyles` gave up its `!important` for this.\n */\nfunction pointerShadow(\n theme: Theme,\n colorPalette: AlertPalette,\n glow: boolean,\n variant: string | undefined,\n): (alpha_: number) => string {\n // The tint moves the surface AWAY from its own ink, which is why this reads\n // the variant and not only the mode.\n //\n // `gradient` is the one variant where the two disagree: it paints a\n // mid-lightness sweep (L* 42–61) under WHITE ink in BOTH modes, so the\n // mode-keyed answer tinted it white in dark mode — toward the text. Measured:\n // white-on-gradient fell to 2.90:1 on hover and 2.41:1 pressed, under the 3:1\n // floor and below its own idle. Every other variant's surface tracks the mode,\n // so for them the two rules agree.\n const ink = variant === 'gradient' ? absoluteInk(theme).black : modeInk(theme);\n const glowShadow = glow\n ? `0 0 ${rems(theme, GLOW.blur, GLOW.spread)} ${alpha(colorPalette.main, GLOW.alpha)}`\n : null;\n return (alpha_) =>\n [`inset 0 0 0 100vmax ${alpha(ink, alpha_)}`, glowShadow].filter(Boolean).join(', ');\n}\n\nconst StyledAlert = styled(MuiAlert, {\n shouldForwardProp: (prop) =>\n !['customVariant', 'customColor', 'glow', 'pulse', 'animate'].includes(prop as string),\n})<{\n customVariant?: string;\n customColor?: string;\n glow?: boolean;\n pulse?: boolean;\n animate?: boolean;\n}>(({ theme, customVariant, customColor, glow, pulse, animate }) => {\n const colorPalette = getColorFromTheme(theme, customColor || customVariant || 'info');\n\n const withTint = pointerShadow(theme, colorPalette, Boolean(glow), customVariant);\n\n return {\n borderRadius: theme.spacing(ALERT_RADIUS_UNITS),\n transition: `${transition}, box-shadow ${seconds(ACTIVE.ms)} ${ALERT_EASING}`,\n position: 'relative',\n overflow: 'hidden',\n animation: animate ? `${fadeInScale(theme)} ${seconds(FADE_IN.ms)} ease-out` : 'none',\n willChange: 'transform, opacity',\n\n ...alertLayoutStyles(theme, colorPalette, animate),\n\n // Hover: one tint step, and nothing that moves. An alert is a surface\n // rather than a control, so the pointer only needs telling it is here. The\n // icon keeps its own place for the same reason — it is the variant's\n // meaning, not an affordance to animate.\n '&:hover': {\n boxShadow: withTint(HOVER.tintAlpha),\n\n '&::before': {\n opacity: 1,\n },\n },\n\n // Press: a 20% tint, landing at once and easing back. Still no geometry.\n //\n // `transition: none` here is what makes the dip INSTANT: a click's\n // `:active` lasts about as long as the finger is down, so easing into it\n // would be cut off in its first tenth and read as a flicker. The ease is on\n // the way back, governed by the root.\n //\n // Neither `:hover` nor `:focus-visible` may declare `transition`, and that\n // is not a style preference. `:active` under a mouse ALWAYS implies\n // `:hover`, so a bare shorthand in the hover block outranks the root's\n // (0,2,0 beats 0,1,0) and silently resets the timing this state rests on.\n //\n // The `:not(:has(…))` names CONTROLS, not any descendant. `:active`\n // matches an ancestor of whatever is pressed, so dismissing an alert\n // dimmed the alert being dismissed and pressing a call to action dimmed\n // the message explaining it. Excluding `:has(:active)` wholesale does not\n // work and was the first attempt: pressing the TEXT makes that text\n // `:active` too, so the guard matched on every press and the state never\n // fired at all.\n '&:active:not(:has(button:active, a:active, [role=\"button\"]:active))': {\n boxShadow: withTint(ACTIVE.tintAlpha),\n transition: 'none',\n },\n\n // Focus: `:focus-visible`, not `:focus-within`.\n //\n // The alert carries `tabIndex={0}`, so a MOUSE CLICK focused it and painted\n // the ring — a keyboard affordance answering a pointer, which is what made\n // clicking one look like an error state. `:focus-visible` is the browser's\n // own answer to \"did this focus come from the keyboard\": the ring stays for\n // whoever tabs here and never fires on a click.\n //\n // One ring, not three. It was a 3px outline at 3px offset UNDER a 6px halo,\n // and stacking two rings on one edge is what read as unfinished rather than\n // as emphasis.\n // It declares no `transition`: `outline-style` is discrete, so the ring\n // arrives at full width with only its COLOUR easing — from `currentcolor`,\n // the near-black alert ink, to the blue. That is a visible dark flash on\n // every keyboard focus, and it would also outrank the root's opacity\n // transition the way the hover block did.\n '&:focus-visible': {\n outline: `${rem(theme, FOCUS.ringWidth)} solid ${alpha(colorPalette.main, FOCUS.ringAlpha)}`,\n outlineOffset: rem(theme, FOCUS.offset),\n },\n\n ...alertVariantStyles(theme, customVariant, colorPalette),\n ...alertEmphasisStyles(theme, colorPalette, Boolean(glow), Boolean(pulse)),\n };\n});\n\n// glass and gradient are our own looks with no MUI severity of their own; both\n// borrow info's. danger is MUI's error.\nconst MUI_SEVERITY: Record<string, AlertColor> = {\n danger: 'error',\n glass: 'info',\n gradient: 'info',\n};\n\nconst toMuiSeverity = (variant: string): AlertColor =>\n MUI_SEVERITY[variant] ?? (variant as AlertColor);\n\nconst AlertContent: React.FC<{\n title?: AlertProps['title'];\n description?: AlertProps['description'];\n dataTestId?: string;\n children?: React.ReactNode;\n}> = ({ title, description, dataTestId, children }) => (\n <>\n {title && (\n <AlertTitle\n data-testid={testIdFor(dataTestId, 'title')}\n sx={{\n fontWeight: TITLE.fontWeight,\n fontSize: sxRem(TITLE.fontSize),\n marginBottom: description ? TITLE.marginBottomUnits : 0,\n wordWrap: 'break-word',\n overflowWrap: 'break-word',\n wordBreak: 'break-word',\n }}\n >\n {title}\n </AlertTitle>\n )}\n {description && (\n <Box\n component=\"div\"\n data-testid={testIdFor(dataTestId, 'message')}\n sx={{\n opacity: DESCRIPTION.opacity,\n fontSize: sxRem(DESCRIPTION.fontSize),\n wordWrap: 'break-word',\n overflowWrap: 'break-word',\n wordBreak: 'break-word',\n }}\n >\n {description}\n </Box>\n )}\n {children}\n </>\n);\n\nconst AlertCloseButton: React.FC<{ dataTestId?: string; label: string; onClose: () => void }> = ({\n dataTestId,\n label,\n onClose,\n}) => (\n <IconButton\n data-testid={testIdFor(dataTestId, 'close')}\n aria-label={label}\n color=\"inherit\"\n size=\"small\"\n onClick={onClose}\n sx={(theme) => ({\n transition,\n opacity: CLOSE_BUTTON.opacity,\n // Opacity and a wash only: the button used to spin 90deg and grow 1.1,\n // which drew the eye to the dismiss rather than to the message.\n '&:hover': {\n opacity: 1,\n backgroundColor: alpha(theme.palette.action.hover, CLOSE_BUTTON.washAlpha),\n },\n // A REAL ring, on the keyboard-only selector.\n //\n // This used to be `&:focus` setting `outline: 'none'` over a\n // `rgba(0,0,0,0.1)` wash — 1.25:1 against the info surface, and MUI's\n // ButtonBase already sets `outline: 0`, so there was no UA fallback\n // either. It went unnoticed while the ROOT still matched `:focus-within`\n // and drew a ring around the whole alert; moving the root to\n // `:focus-visible` took that away and left this button with no visible\n // focus at all. `Banner`'s dismiss already draws its own outline, so\n // this is also what keeps the two surfaces telling a keyboard user the\n // same thing.\n '&:focus-visible': {\n opacity: 1,\n // `currentColor` is the alert's own ink, inherited through\n // `color=\"inherit\"`, so the ring is drawn in whatever colour was\n // already chosen to be READ on this surface. Measured 11.2:1 on the\n // semantic variants and 5.7:1 on `gradient`.\n //\n // That is a reasoned choice, not a test-backed one, and the difference\n // matters: `alert-contrast.test.tsx` covers the ink of the four\n // semantic variants only — its `glass` case asserts the pane's alpha\n // rather than its ink, and `gradient` it does not render at all. A\n // translucent surface's contrast depends on what sits behind it, which\n // is why that test stops where it does and why this comment does not\n // borrow a guarantee it never made.\n outline: `${rem(theme, FOCUS.ringWidth)} solid currentColor`,\n outlineOffset: rem(theme, FOCUS.offset),\n backgroundColor: alpha(theme.palette.action.focus, CLOSE_BUTTON.washAlpha),\n },\n })}\n >\n <Close fontSize=\"inherit\" />\n </IconButton>\n);\n\n/**\n * The dismiss button, or nothing.\n *\n * Reads `closeLabel` straight off the props rather than through the\n * `definedProps` merge, which would widen it to `string | undefined` and hand\n * the button an empty name. The UNION in `AlertProps` guarantees the label\n * wherever `closable` is true — that narrowing is gone inside the component,\n * which is why the fallback below exists and why it is unreachable.\n */\nfunction dismissButton(\n props: AlertProps,\n dataTestId: string | undefined,\n onClose: () => void,\n): React.JSX.Element | null {\n if (!props.closable) return null;\n return <AlertCloseButton dataTestId={dataTestId} label={props.closeLabel} onClose={onClose} />;\n}\n\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>((alertProps, ref) => {\n const {\n variant,\n color,\n glow,\n pulse,\n icon,\n showIcon,\n onClose,\n title,\n description,\n children,\n animate,\n role: _role, announce: _announce, // resolved from `alertProps` — see `resolveAnnouncement`\n 'aria-atomic': ariaAtomic,\n ...others\n } = resolveAlertProps(alertProps);\n\n // Every spelling of the test id the shared contract allows, mapped to the\n // one the DOM reads and defaulted to `alert`; the native Alert does the same.\n const dataTestId = resolveTestId(others, 'alert');\n // Then the dismiss union, which `dismissButton` reads off `alertProps` rather\n // than through this destructure — so without this strip it rides `...props`\n // onto the DOM. `ALERT_DEFAULTS` supplies `closable: false`, so that happened\n // on EVERY alert, not only the closable ones.\n const props = withoutAlertOnlyProps(withoutTestIdProps(others));\n\n // Depends on `variant`, so it cannot live in the static defaults above.\n const announcement = resolveAnnouncement(alertProps, variant);\n const [open, setOpen] = React.useState(true);\n const [isClosing, setIsClosing] = React.useState(false);\n\n const handleClose = () => {\n setIsClosing(true);\n window.setTimeout(() => {\n setOpen(false);\n onClose?.();\n }, CLOSE_DELAY_MS);\n };\n\n const severity = toMuiSeverity(variant);\n\n const displayIcon = showIcon ? (\n <Box component=\"span\" data-testid={testIdFor(dataTestId, 'icon')}>\n {icon || getVariantIcon(variant)}\n </Box>\n ) : (\n false\n );\n\n const content = (\n <AlertContent title={title} description={description} dataTestId={dataTestId}>\n {children}\n </AlertContent>\n );\n\n return (\n <Collapse in={open && !isClosing} timeout={COLLAPSE_MS}>\n <StyledAlert\n ref={ref}\n data-testid={dataTestId}\n severity={severity}\n customVariant={variant}\n customColor={color}\n glow={glow}\n pulse={pulse}\n animate={animate}\n icon={displayIcon}\n role={announcement.role}\n aria-live={announcement['aria-live']}\n aria-atomic={ariaAtomic}\n tabIndex={0}\n action={dismissButton(alertProps, dataTestId, handleClose)}\n {...props}\n >\n {content}\n </StyledAlert>\n </Collapse>\n );\n});\n\n/**\n * The props `Alert` OWNS, which must never reach the DOM.\n *\n * `AlertProps` extends MUI's, whose rest-spread is how a host's real HTML\n * attributes get onto the root — so the same spread carries the alert's own\n * configuration unless it is named here. Everything else the component consumes\n * leaves through the destructure above; these two do not, because\n * `dismissButton` reads them off the original props to keep the dismiss union's\n * narrowing intact.\n *\n * Exported so a test can render an alert carrying every name on it and assert\n * none reaches the DOM — the check that would have caught `closable`.\n */\nexport const ALERT_ONLY_PROPS = ['closable', 'closeLabel'] as const;\n\nfunction withoutAlertOnlyProps<P extends object>(rest: P): P {\n const html = { ...rest } as Record<string, unknown>;\n for (const key of ALERT_ONLY_PROPS) delete html[key];\n return html as P;\n}\n\nAlert.displayName = 'Alert';\n"],"mappings":"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","names":[]}