asma-ui-core 3.104.0 → 3.106.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 (70) hide show
  1. package/dist/asma-ui-core.css +1 -1
  2. package/dist/components/custom/module/header-layout/DynamicToolbar.js +99 -97
  3. package/dist/components/custom/module/header-layout/ToolbarActionGroup.js +62 -43
  4. package/dist/components/custom/module/header-layout/ToolbarRows.js +51 -47
  5. package/dist/components/custom/module/header-layout/useTranslations.js +12 -10
  6. package/dist/components/custom/page/page-header/PageHeader.js +41 -40
  7. package/dist/components/custom/widget/widget/StyledWidget.js +21 -20
  8. package/dist/components/data-display/chip/StyledChip.js +70 -52
  9. package/dist/components/data-display/tooltip/StyledTooltip.js +110 -65
  10. package/dist/components/feedback/dialog/DialogBusyContext.js +32 -0
  11. package/dist/components/feedback/dialog/StyledDialog.js +104 -80
  12. package/dist/components/feedback/dialog/dialog-footer/StyledDialogFooter.js +76 -71
  13. package/dist/components/feedback/minimizable-dialog/MinimizableDialog.js +133 -137
  14. package/dist/components/feedback/minimizable-dialog/components/CloseBtn.js +11 -8
  15. package/dist/components/feedback/minimizable-dialog/components/FullscreenBtn.js +6 -6
  16. package/dist/components/feedback/minimizable-dialog/components/MinimizeBtn.js +1 -1
  17. package/dist/components/feedback/minimizable-dialog/v2/MinimizableDialogV2.js +94 -85
  18. package/dist/components/feedback/minimizable-dialog/v2/useTranslations.js +6 -4
  19. package/dist/components/inputs/button/StyledButton.js +83 -33
  20. package/dist/components/inputs/button/StyledButton.module.js +8 -8
  21. package/dist/components/inputs/dynamic-select/components/DynamicInteractiveChipGroup.js +32 -32
  22. package/dist/components/inputs/dynamic-select/components/DynamicSelectAutocomplete.js +91 -82
  23. package/dist/components/inputs/select/StyledSelect.js +8 -8
  24. package/dist/components/inputs/select-autocomplete/StyledSelectAutocomplete.js +152 -152
  25. package/dist/components/navigation/link/StyledLink.js +53 -26
  26. package/dist/components/navigation/link/StyledLink.module.js +6 -6
  27. package/dist/components/navigation/menu/StyledMenuItem.js +35 -30
  28. package/dist/components/navigation/menu/StyledMenuList.js +47 -38
  29. package/dist/components/navigation/tabs/StyledTab.js +25 -18
  30. package/dist/components/navigation/tabs/StyledTabs.js +33 -33
  31. package/dist/components/utils/bottom-sheet/StyledBottomSheet.js +6 -6
  32. package/dist/components/utils/copy-wrapper/CopyButton.js +4 -4
  33. package/dist/components/utils/copy-wrapper/CopyWrapper.js +4 -4
  34. package/dist/datetime/shared-components/button/StyledButton.module.js +11 -11
  35. package/dist/helpers/focusable.js +4 -3
  36. package/dist/helpers/warnUnnamedDialog.js +6 -0
  37. package/dist/index.js +589 -585
  38. package/dist/src/components/custom/module/header-layout/ToolbarActionGroup.d.ts +13 -2
  39. package/dist/src/components/custom/module/header-layout/ToolbarRows.d.ts +2 -1
  40. package/dist/src/components/custom/module/header-layout/planToolbarActions.d.ts +18 -1
  41. package/dist/src/components/custom/module/header-layout/useTranslations.d.ts +2 -0
  42. package/dist/src/components/custom/widget/widget/StyledWidget.d.ts +5 -0
  43. package/dist/src/components/data-display/chip/StyledChip.d.ts +6 -0
  44. package/dist/src/components/data-display/interactive-chip/StyledInteractiveChip.d.ts +7 -1
  45. package/dist/src/components/data-display/tooltip/StyledTooltip.d.ts +16 -2
  46. package/dist/src/components/feedback/dialog/DialogBusy.test.d.ts +1 -0
  47. package/dist/src/components/feedback/dialog/DialogBusyContext.d.ts +28 -0
  48. package/dist/src/components/feedback/dialog/StyledDialog.d.ts +15 -2
  49. package/dist/src/components/feedback/dialog/dialog-footer/StyledDialogFooter.d.ts +10 -7
  50. package/dist/src/components/feedback/dialog/index.d.ts +1 -0
  51. package/dist/src/components/feedback/minimizable-dialog/components/CloseBtn.d.ts +2 -0
  52. package/dist/src/components/feedback/minimizable-dialog/types.d.ts +11 -0
  53. package/dist/src/components/feedback/minimizable-dialog/v2/types.d.ts +2 -0
  54. package/dist/src/components/feedback/minimizable-dialog/v2/useTranslations.d.ts +2 -0
  55. package/dist/src/components/inputs/button/StyledButton.d.ts +25 -1
  56. package/dist/src/components/inputs/button/StyledButtonBusy.test.d.ts +1 -0
  57. package/dist/src/components/navigation/link/StyledLink.d.ts +6 -0
  58. package/dist/src/components/navigation/menu/StyledMenuItem.d.ts +8 -1
  59. package/dist/src/components/navigation/menu/StyledMenuList.d.ts +2 -1
  60. package/dist/src/components/navigation/tabs/StyledTab.d.ts +17 -1
  61. package/dist/src/forms/submitChanged.d.ts +3 -0
  62. package/dist/src/helpers/focusable.d.ts +9 -0
  63. package/dist/src/helpers/index.d.ts +1 -0
  64. package/dist/src/helpers/uiCoreLocale.d.ts +1 -0
  65. package/dist/src/helpers/warnUnnamedDialog.d.ts +5 -0
  66. package/dist/src/table/shared-components/StyledMenuItem.d.ts +5 -1
  67. package/dist/table/components/columns/action-column/components/RowActionMenu.js +57 -50
  68. package/dist/table/shared-components/StyledMenuItem.js +28 -16
  69. package/dist/table/shared-components/button/StyledButton.module.js +11 -11
  70. package/package.json +1 -1
@@ -1,19 +1,30 @@
1
+ import { ReactNode } from 'react';
1
2
  import { DynamicToolbarAction, PlannedToolbarActions } from './planToolbarActions';
3
+ /** More-menu item state: a running action is disabled with the busy reason (disabled-states DIS-8). */
4
+ export declare function overflowItemState(action: DynamicToolbarAction, inProgressLabel?: string): {
5
+ disabled?: boolean;
6
+ disabledReason?: ReactNode;
7
+ 'aria-busy'?: boolean;
8
+ };
2
9
  /** True when the plan renders anything (inline buttons or the More menu). */
3
10
  export declare function hasPlannedActions(plan?: PlannedToolbarActions): boolean;
4
- export declare function ToolbarActionButton({ action, showLabel, selectionTone, }: {
11
+ export declare function ToolbarActionButton({ action, showLabel, selectionTone, inProgressLabel, }: {
5
12
  action: DynamicToolbarAction;
6
13
  showLabel: boolean;
7
14
  selectionTone?: boolean;
15
+ /** Busy announcement of a `loading` action, in the toolbar locale. */
16
+ inProgressLabel?: string;
8
17
  }): JSX.Element;
9
18
  export declare function MoreTriggerButton({ overflowMenuLabel, onOpen, }: {
10
19
  overflowMenuLabel: string;
11
20
  onOpen?: (anchor: HTMLElement) => void;
12
21
  }): JSX.Element;
13
22
  /** Inline action buttons plus the "More" overflow menu for a planned strip. */
14
- export declare function ToolbarActionGroup({ plan, overflowMenuLabel, className, selectionTone, registerActionWidth, }: {
23
+ export declare function ToolbarActionGroup({ plan, overflowMenuLabel, className, selectionTone, registerActionWidth, inProgressLabel, }: {
15
24
  plan: PlannedToolbarActions;
16
25
  overflowMenuLabel: string;
26
+ /** Reason and busy announcement of a `loading` action, in the toolbar locale. */
27
+ inProgressLabel?: string;
17
28
  className?: string;
18
29
  selectionTone?: boolean;
19
30
  /**
@@ -51,7 +51,7 @@ export declare function TitleBlock({ title, helperText, titleMeasureRef, }: {
51
51
  * Bulk actions render only in the `inline-selection` variant; the other
52
52
  * variants pair with a separate {@link SelectionRow} when selection is active.
53
53
  */
54
- export declare function UtilityCluster({ variant, beforeFilterActionsPlan, filter, search, afterSearchActionsPlan, bulkActionsPlan, showPageActions, overflowMenuLabel, searchMeasureRef, align, }: {
54
+ export declare function UtilityCluster({ variant, beforeFilterActionsPlan, filter, search, afterSearchActionsPlan, bulkActionsPlan, showPageActions, overflowMenuLabel, inProgressLabel, searchMeasureRef, align, }: {
55
55
  variant: UtilityRowVariant;
56
56
  beforeFilterActionsPlan: PlannedToolbarActions;
57
57
  filter?: ReactNode;
@@ -60,6 +60,7 @@ export declare function UtilityCluster({ variant, beforeFilterActionsPlan, filte
60
60
  bulkActionsPlan?: PlannedToolbarActions;
61
61
  showPageActions: boolean;
62
62
  overflowMenuLabel: string;
63
+ inProgressLabel?: string;
63
64
  searchMeasureRef?: MeasureRef;
64
65
  align?: 'start' | 'end';
65
66
  }): JSX.Element | null;
@@ -1,4 +1,4 @@
1
- import { ReactElement } from 'react';
1
+ import { ReactElement, ReactNode } from 'react';
2
2
  export interface DynamicToolbarActionRenderOptions {
3
3
  showLabel: boolean;
4
4
  }
@@ -8,6 +8,23 @@ export interface DynamicToolbarAction {
8
8
  onClick: () => void;
9
9
  icon?: ReactElement;
10
10
  disabled?: boolean;
11
+ /**
12
+ * Why the action is unavailable. With `disabled` the button or More-menu item stays focusable
13
+ * and shows the reason on hover, focus and tap (disabled-states DIS-1…DIS-3).
14
+ */
15
+ disabledReason?: ReactNode;
16
+ /**
17
+ * Busy: the button shows a spinner, sets `aria-busy` and ignores activation (disabled-states DIS-8).
18
+ * In the More menu the item is disabled with the reason "In progress" in the toolbar locale. Pass a
19
+ * boolean from the first render so the busy announcement region exists before it turns on.
20
+ */
21
+ loading?: boolean;
22
+ /**
23
+ * Whether a `loading` action keeps the dialog around it open (no Esc, backdrop, X or Cancel), inline
24
+ * and in the More menu; defaults to `true`, the rule for a running save (submit-buttons SUB-8). Pass
25
+ * `false` for an action that is not a save, such as a preview or a lookup.
26
+ */
27
+ holdsDialog?: boolean;
11
28
  hidden?: boolean;
12
29
  variant?: 'contained' | 'outlined' | 'text' | 'textGray' | 'textWhite';
13
30
  /** Visual tone. `danger` renders the action in the destructive/red palette. */
@@ -7,6 +7,7 @@ declare const translations: {
7
7
  back: string;
8
8
  menu: string;
9
9
  close: string;
10
+ inProgress: string;
10
11
  };
11
12
  no: {
12
13
  more: string;
@@ -15,6 +16,7 @@ declare const translations: {
15
16
  back: string;
16
17
  menu: string;
17
18
  close: string;
19
+ inProgress: string;
18
20
  };
19
21
  };
20
22
  export type ToolbarTranslations = (typeof translations)['en'];
@@ -25,6 +25,11 @@ export interface StyledWidgetProps {
25
25
  onClick?: () => void;
26
26
  hide?: boolean;
27
27
  disabled?: boolean;
28
+ /**
29
+ * @figmaProp none — behavioral. Why "view more" is unavailable. With `disabled` the button stays
30
+ * focusable and shows the reason on hover, focus and tap (disabled-states DIS-1…DIS-4).
31
+ */
32
+ disabledReason?: ReactNode;
28
33
  };
29
34
  /**
30
35
  * @param {string} persistKey - Unique key to persist expanded state
@@ -27,6 +27,12 @@ export interface StyledChipProps {
27
27
  size?: 'small' | 'medium';
28
28
  /** @figmaProp State = true→"Disabled" */
29
29
  disabled?: boolean;
30
+ /**
31
+ * @figmaProp none — behavioral. Why a clickable chip is unavailable. With `disabled` the chip stays
32
+ * focusable (`aria-disabled`), ignores activation and shows the reason on hover, focus and tap
33
+ * (disabled-states DIS-1…DIS-3).
34
+ */
35
+ disabledReason?: ReactNode;
30
36
  /** @figmaProp State = true→"Read only" */
31
37
  readOnly?: boolean;
32
38
  /** @figmaProp State = drives interactive Hovered/Focused/Pressed via native events */
@@ -1,4 +1,4 @@
1
- import { ComponentProps } from 'react';
1
+ import { ComponentProps, ReactNode } from 'react';
2
2
  import { StyledChip } from '../chip';
3
3
  /**
4
4
  * @figmaNode wXrXt5uKNNzV2DnQCgyYZH#13248-31089
@@ -19,6 +19,12 @@ export interface StyledInteractiveChipProps extends ComponentProps<typeof Styled
19
19
  size?: 'small' | 'medium';
20
20
  /** @figmaProp none — accessible name for the control */
21
21
  ariaLabel?: string;
22
+ /**
23
+ * @figmaProp none — behavioral. Why the chip can't be selected. With `disabled` the chip stays
24
+ * focusable (`aria-disabled`), ignores activation and shows the reason on hover, focus and tap
25
+ * (disabled-states DIS-1…DIS-4). Without `disabled` it has no effect.
26
+ */
27
+ disabledReason?: ReactNode;
22
28
  }
23
29
  /**
24
30
  * A `StyledChip` whose avatar is a native checkbox or radio (base-ui controls now render their own
@@ -30,6 +30,19 @@ export interface TooltipProps {
30
30
  disableHoverListener?: boolean;
31
31
  disableFocusListener?: boolean;
32
32
  disableTouchListener?: boolean;
33
+ /**
34
+ * A tap on a touch screen toggles the tooltip; it stays until the next tap, a tap outside,
35
+ * scroll or Escape — never a timer (disabled-states DIS-2, §5). Hover stays mouse-only so the
36
+ * emulated mouse events after a tap cannot toggle it a second time.
37
+ */
38
+ openOnTap?: boolean;
39
+ /**
40
+ * The text stays in the DOM and the trigger references it through `aria-describedby` while the
41
+ * tooltip is closed, so a screen reader announces it on focus (disabled-states DIS-2, 4.1.2).
42
+ */
43
+ persistentDescription?: boolean;
44
+ /** Share the persistent description with every focusable item in a composite control. */
45
+ persistentDescriptionId?: string;
33
46
  offsetDistance?: number;
34
47
  className?: string;
35
48
  slotProps?: TooltipSlotProps;
@@ -37,8 +50,9 @@ export interface TooltipProps {
37
50
  /**
38
51
  * Tooltip built on `@floating-ui/react` (replaces MUI `Tooltip`) — hover(+`enterDelay`)/focus open,
39
52
  * dismiss on blur/esc, `flip`/`shift` collision handling, optional arrow, portalled. Empty `title`
40
- * renders the child alone (MUI parity). Preserves the `#363E4A` design and the `title`/`placement`/
53
+ * renders the child alone (MUI parity); a Fragment child keeps its `display: contents` wrapper so
54
+ * it does not remount when a title appears. Preserves the `#363E4A` design and the `title`/`placement`/
41
55
  * `arrow`/`enterDelay`/`open`/`slotProps` surface (DEC-003). TASK-301.
42
56
  */
43
- export declare const StyledTooltip: (props: TooltipProps) => JSX.Element;
57
+ export declare const StyledTooltip: ({ children, ...props }: TooltipProps) => JSX.Element;
44
58
  export {};
@@ -0,0 +1,28 @@
1
+ import { ReactNode } from 'react';
2
+ import { UiCoreLocale } from '../../../helpers/uiCoreLocale';
3
+ export declare const WAIT_UNTIL_SAVED: {
4
+ readonly en: "Wait until saved";
5
+ readonly no: "Vent til lagringen er ferdig";
6
+ };
7
+ interface DialogBusyContextValue {
8
+ busy: boolean;
9
+ busyReason?: ReactNode;
10
+ /** Marks the dialog busy; the returned function ends that mark. */
11
+ registerBusy: () => () => void;
12
+ }
13
+ export declare const DialogBusyContext: import('react').Context<DialogBusyContextValue | null>;
14
+ /**
15
+ * A dialog (or popover) is busy while one of its buttons runs an action (`StyledButton loading`, unless
16
+ * the button passes `holdsDialog={false}`).
17
+ * Closing it then would hide a request that is still running, so every dismissal (Esc, backdrop click,
18
+ * the X button) is ignored until the action ends (submit-buttons SUB-4/SUB-8: Cancel waits for the result).
19
+ * Provide `contextValue` with `DialogBusyContext.Provider` around the dialog content.
20
+ */
21
+ export declare const useDialogBusyBoundary: (busyReason?: ReactNode, locale?: UiCoreLocale) => {
22
+ busy: boolean;
23
+ busyReason: ReactNode;
24
+ contextValue: DialogBusyContextValue;
25
+ };
26
+ /** Reports `busy` to the closest dialog, if any. Safe outside a dialog. */
27
+ export declare const useReportDialogBusy: (busy: boolean) => void;
28
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { CSSProperties, ReactNode, SyntheticEvent } from 'react';
2
+ import { UiCoreLocale } from '../../../helpers/uiCoreLocale';
2
3
  export type DialogCloseReason = 'escapeKeyDown' | 'backdropClick';
3
4
  export type DialogMaxWidth = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
4
5
  interface DialogPaperProps {
@@ -48,6 +49,10 @@ export interface IStyledDialogProps {
48
49
  onExited?: () => void;
49
50
  };
50
51
  };
52
+ /** Why close and Cancel wait while an action runs; defaults to "Wait until saved" in `locale`. */
53
+ busyReason?: ReactNode;
54
+ /** Language of the default busy reason; defaults to English. */
55
+ locale?: UiCoreLocale;
51
56
  onCloseText?: ReactNode;
52
57
  /** @figmaProp none — behavioral */
53
58
  showCloseIcon?: boolean;
@@ -55,9 +60,17 @@ export interface IStyledDialogProps {
55
60
  dialogLabel?: ReactNode;
56
61
  /** @figmaProp header Title (Page title 24 SemiBold, delta-800) */
57
62
  dialogTitle?: ReactNode;
58
- /** Accessible name when the dialog has no visible title. */
59
- ariaLabel?: string;
60
63
  dialogHeaderNode?: ReactNode;
64
+ /**
65
+ * @figmaProp none — accessibility
66
+ * The dialog's accessible name when it shows no visible title: neither `dialogTitle`, a
67
+ * `<StyledDialogTitle>` child nor `dialogLabel`. A visible title or label always wins: the dialog
68
+ * is `aria-labelledby` it. Without any of these, the dialog is named by the first heading
69
+ * (`h1`–`h6` or `role="heading"`) in its content; with no heading either it stays unnamed and a
70
+ * development-only warning names its `dataTest`. `dataTest` is never used as a name — it is a
71
+ * test hook, not text.
72
+ */
73
+ ariaLabel?: string;
61
74
  }
62
75
  /**
63
76
  * Modal dialog built on the native `<dialog>` element (replaces MUI `Dialog`) — backdrop, focus
@@ -6,6 +6,7 @@ import { ToolbarLocale } from '../../../custom/module/header-layout/useTranslati
6
6
  export interface StyledDialogFooterButton {
7
7
  label: string;
8
8
  onClick?: () => void;
9
+ /** Rendered as `aria-disabled`, not native `disabled`: the button stays focusable, so keyboard users reach its `tooltip`. A click does nothing. */
9
10
  disabled?: boolean;
10
11
  /** `submit` lets the footer drive the surrounding `<form>` without a click handler. */
11
12
  type?: 'button' | 'submit' | 'reset';
@@ -17,16 +18,18 @@ export interface StyledDialogFooterButton {
17
18
  /** Trailing icon. `loading` supplies its own and wins. */
18
19
  endIcon?: ReactNode;
19
20
  /**
20
- * Shows a spinner after the label and disables the button. Consumers previously swapped
21
- * the label *for* a spinner, which changes the button's width mid-submit — hence the
22
- * hard-coded `w-[98px]` workarounds in the footers this replaces. Keeping the label
23
- * makes the width stable on its own.
21
+ * Marks the button busy: a spinner sits centred over the label, which stays in the layout
22
+ * (invisible, still the accessible name), so the button keeps its width. It keeps focus and
23
+ * ignores activation. Consumers previously swapped the label *for* a spinner, which changed
24
+ * the button's width mid-submit — hence the hard-coded `w-[98px]` workarounds in the footers
25
+ * this replaces.
24
26
  */
25
27
  loading?: boolean;
26
28
  /**
27
- * Wraps the button in a `StyledTooltip`. Falsy renders no tooltip, so the common
28
- * `tooltip={disabled && 'Locked for editing'}` reads naturally. A disabled button does
29
- * not emit pointer events, so the tooltip is anchored on a wrapper around it.
29
+ * Pass a constant `tooltip` (e.g. 'Locked for editing') and toggle `disabled` separately.
30
+ * With `disabled` it is the reason the action is unavailable (`disabledReason`):
31
+ * the button stays focusable and shows it on hover, focus and tap. Otherwise it is a plain
32
+ * hint anchored on a wrapper.
30
33
  */
31
34
  tooltip?: ReactNode;
32
35
  dataTest?: string;
@@ -3,3 +3,4 @@ export * from './StyledDialogActions';
3
3
  export * from './StyledDialogContent';
4
4
  export * from './StyledDialogTitle';
5
5
  export * from './dialog-footer';
6
+ export { DialogBusyContext, useDialogBusyBoundary, useReportDialogBusy } from './DialogBusyContext';
@@ -4,4 +4,6 @@ export declare const CloseBtn: React.FC<{
4
4
  onClick: () => void;
5
5
  tooltipTitle: string;
6
6
  buttonRef?: React.Ref<HTMLButtonElement>;
7
+ /** While set, the button is soft-disabled with this reason (e.g. a save is running). */
8
+ disabledReason?: React.ReactNode;
7
9
  }>;
@@ -19,7 +19,18 @@ export interface IMinimizableDialogProps {
19
19
  children?: React.ReactNode | ((props: IFloatingWindowProps) => ReactNode);
20
20
  className?: string;
21
21
  primaryButtonText?: string;
22
+ /**
23
+ * @figmaProp none — behavioral. Busy: the primary button shows a spinner and ignores activation;
24
+ * the secondary button, Esc and the close button wait with the reason "Wait until saved" (DIS-8).
25
+ */
22
26
  primaryButtonLoading?: boolean;
27
+ /** @figmaProp none — behavioral. Disables the primary button; pair it with `primaryButtonDisabledReason`. */
28
+ primaryButtonDisabled?: boolean;
29
+ /**
30
+ * @figmaProp none — behavioral. Why the primary action is unavailable. With `primaryButtonDisabled`
31
+ * the button stays focusable and shows the reason on hover, focus and tap (disabled-states DIS-1…DIS-4).
32
+ */
33
+ primaryButtonDisabledReason?: ReactNode;
23
34
  secondaryButtonText?: string;
24
35
  onPrimaryButtonClick?: () => void;
25
36
  onSecondaryButtonClick?: () => void;
@@ -30,6 +30,8 @@ export interface IMinimizableDialogV2Props {
30
30
  fullscreen?: string;
31
31
  };
32
32
  locale?: ILocale;
33
+ /** Why Close and Cancel wait while an action inside runs; defaults to "Wait until saved" in `locale`. */
34
+ busyReason?: ReactNode;
33
35
  dataTest: string;
34
36
  style?: React.CSSProperties;
35
37
  /**
@@ -2,6 +2,7 @@ import { ILocale } from './types';
2
2
  declare const translations: {
3
3
  en: {
4
4
  close: string;
5
+ waitUntilSaved: string;
5
6
  expand: string;
6
7
  minimize: string;
7
8
  fullscreen: string;
@@ -9,6 +10,7 @@ declare const translations: {
9
10
  };
10
11
  no: {
11
12
  close: string;
13
+ waitUntilSaved: string;
12
14
  expand: string;
13
15
  minimize: string;
14
16
  fullscreen: string;
@@ -1,4 +1,5 @@
1
1
  import { default as React, ReactNode } from 'react';
2
+ import { UiCoreLocale } from '../../../helpers/uiCoreLocale';
2
3
  export type StyledButtonType = 'contained' | 'outlined' | 'text' | 'textGray';
3
4
  interface commonProps {
4
5
  /** @figmaProp none — ref */
@@ -11,6 +12,29 @@ interface commonProps {
11
12
  endIcon?: ReactNode;
12
13
  /** @figmaProp none — test hook */
13
14
  dataTest: string;
15
+ /**
16
+ * @figmaProp none — behavioral. Why the action is unavailable. Together with `disabled` the
17
+ * button stays focusable (`aria-disabled` instead of the native attribute), ignores activation
18
+ * and shows the reason on hover, focus and tap (disabled-states DIS-1…DIS-4). Without
19
+ * `disabled` it has no effect.
20
+ */
21
+ disabledReason?: ReactNode;
22
+ /**
23
+ * @figmaProp none — behavioral. Busy: spinner centred over the label (the label stays invisible in the layout, so the width is kept), `aria-busy`, focus kept,
24
+ * activation ignored (disabled-states DIS-8, submit-buttons SUB-4). The start is announced politely.
25
+ * Pass a boolean from the first render: the announcement region exists only while `loading` is defined.
26
+ */
27
+ loading?: boolean;
28
+ /**
29
+ * @figmaProp none — behavioral. Whether a `loading` button keeps the dialog around it open (no Esc,
30
+ * backdrop, X or Cancel) until it ends; defaults to `true`, the rule for a running save (submit-buttons
31
+ * SUB-8). Pass `false` for an action that is not a save, such as a search or a lookup.
32
+ */
33
+ holdsDialog?: boolean;
34
+ /** Language of the default busy announcement; defaults to English. */
35
+ locale?: UiCoreLocale;
36
+ /** @figmaProp none — behavioral. Announced when `loading` starts; defaults to "In progress" in `locale`. */
37
+ loadingAnnouncement?: string;
14
38
  }
15
39
  interface variantTextGrayProps {
16
40
  /** @figmaProp Type = textGray→"Quaternary" (transparent bg, text-icon/body #49525f) */
@@ -50,5 +74,5 @@ export type StyledButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
50
74
  * @param refLink - ref to component
51
75
  * @param dataTest - data-test tag
52
76
  */
53
- export declare const StyledButton: ({ variant, className, size, children, refLink, startIcon, endIcon, dataTest, error, style: styleProp, ...otherProps }: StyledButtonProps) => JSX.Element;
77
+ export declare const StyledButton: ({ variant, className, size, children, refLink, startIcon, endIcon, dataTest, error, style: styleProp, disabled, disabledReason, loading, holdsDialog, loadingAnnouncement, locale, onClick, ...otherProps }: StyledButtonProps) => JSX.Element;
54
78
  export {};
@@ -10,6 +10,12 @@ export type StyledLinkProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'onC
10
10
  onClick?: (event: MouseEvent<HTMLAnchorElement | HTMLButtonElement>) => void;
11
11
  /** @figmaProp State = true→"Disabled" (renders a non-interactive <span>, delta-300) */
12
12
  disabled?: boolean;
13
+ /**
14
+ * @figmaProp none — behavioral. Why the link is unavailable. With `disabled` the link stays
15
+ * focusable (`aria-disabled`), does not navigate and shows the reason on hover, focus and tap
16
+ * (disabled-states DIS-1…DIS-3). Without it a disabled link stays a plain `<span>`.
17
+ */
18
+ disabledReason?: ReactNode;
13
19
  /** @figmaProp Size = small→"Base" (14/20) | large→"Medium" (16/24); xs (12/20) = none (no DS size) */
14
20
  size?: 'small' | 'large' | 'xs';
15
21
  /** @figmaProp none — ref */
@@ -12,6 +12,13 @@ export interface StyledMenuItemProps {
12
12
  onMouseUp?: (event: MouseEvent<HTMLLIElement>) => void;
13
13
  /** @figmaProp State = true→"Disabled" */
14
14
  disabled?: boolean;
15
+ /**
16
+ * @figmaProp none — behavioral. Why the item is unavailable. With `disabled` the item stays in
17
+ * arrow-key navigation and shows the reason on hover, focus and tap (disabled-states DIS-1…DIS-3).
18
+ */
19
+ disabledReason?: ReactNode;
20
+ /** @figmaProp none — behavioral. The item's action is running; pair it with `disabled` and a reason. */
21
+ 'aria-busy'?: boolean;
15
22
  /** @figmaProp State = true→"Selected" */
16
23
  selected?: boolean;
17
24
  /** Compact vertical padding (MUI `MenuItem` `dense` parity, DEC-003). */
@@ -36,4 +43,4 @@ export interface StyledMenuItemProps {
36
43
  * Native `role="menuitem"` (replaces MUI `MenuItem`). Enter/Space activate; a disabled item stays
37
44
  * focusable but non-interactive (MUI parity). Public props preserved (DEC-003). TASK-303.
38
45
  */
39
- export declare const StyledMenuItem: ({ children, onClick, onMouseDown, onMouseUp, disabled, selected, dense, tooltip, className, classes, sx, "data-test": dataTest, "data-testid": dataTestId, value: _value, }: StyledMenuItemProps) => JSX.Element;
46
+ export declare const StyledMenuItem: ({ children, onClick, onMouseDown, onMouseUp, disabled, disabledReason, "aria-busy": ariaBusy, selected, dense, tooltip, className, classes, sx, "data-test": dataTest, "data-testid": dataTestId, value: _value, }: StyledMenuItemProps) => JSX.Element;
@@ -13,6 +13,7 @@ export interface StyledMenuListProps {
13
13
  }
14
14
  /**
15
15
  * Keyboard-navigable `role="menu"` list (replaces MUI `MenuList`). Arrow-key / Home / End roving
16
- * focus over enabled `[role="menuitem"]` children. TASK-303.
16
+ * focus over every `[role="menuitem"]` child, disabled ones included; opening focuses the first
17
+ * enabled item. TASK-303.
17
18
  */
18
19
  export declare const StyledMenuList: ({ children, id, role, className, autoFocus, disablePadding, onClick, onKeyDown, }: StyledMenuListProps) => JSX.Element;
@@ -12,8 +12,24 @@ export interface StyledTabProps {
12
12
  value?: TabValue;
13
13
  /** @figmaProp Label (tab content; may include a leading icon) */
14
14
  label?: ReactNode;
15
- /** @figmaProp State = true→"Disabled" */
15
+ /**
16
+ * @figmaProp State = true→"Disabled". Rendered as `aria-disabled`, not native `disabled`: the tab
17
+ * stays in the arrow-key order so keyboard users can find it (disabled-states DIS-3); a click or
18
+ * Enter does not select it.
19
+ */
16
20
  disabled?: boolean;
21
+ /**
22
+ * @figmaProp none — behavioral. Why the tab is unavailable. With `disabled` the tab stays in
23
+ * arrow-key navigation and shows the reason on hover, focus and tap (disabled-states DIS-1…DIS-3).
24
+ */
25
+ disabledReason?: ReactNode;
26
+ /**
27
+ * @figmaProp none — a11y
28
+ * Tooltip on the tab, opened by hover and keyboard focus and read as the tab's description —
29
+ * typically why a `disabled` tab is unavailable (disabled-states DIS-1, DIS-2). Falsy renders none.
30
+ * A `disabledReason` takes its place while the tab is disabled.
31
+ */
32
+ tooltip?: ReactNode;
17
33
  className?: string;
18
34
  sx?: unknown;
19
35
  /** @internal Injected by `StyledTabs` so a value-less tab selects by position (MUI parity). */
@@ -3,4 +3,7 @@ export type SubmitChangedPatch<TFieldValues extends FieldValues> = (dirty: Parti
3
3
  export interface SubmitChangedOptions {
4
4
  touchInvalidFields?: boolean;
5
5
  }
6
+ /**
7
+ * Does not set react-hook-form `formState.isSubmitting`; callers drive the busy state from their own pending flag.
8
+ */
6
9
  export declare function submitChanged<TFieldValues extends FieldValues>(methods: UseFormReturn<TFieldValues>, onPatch: SubmitChangedPatch<TFieldValues>, onInvalid?: () => void, options?: SubmitChangedOptions): Promise<boolean>;
@@ -6,3 +6,12 @@ export declare const tabbableWithin: (root: ParentNode) => HTMLElement[];
6
6
  * disabled control, which the caller has to decide about.
7
7
  */
8
8
  export declare const firstTabbable: (root: HTMLElement) => HTMLElement | null;
9
+ /**
10
+ * The element a description belongs to for `root`: its first focusable element, including a
11
+ * roving-tabindex item (`tabindex="-1"`, an unselected tab or a menu item) that arrow keys still move
12
+ * focus to — inside a `tablist` or `menu` the wrapper would be a child those roles do not allow.
13
+ * Unlike `firstTabbable` it ignores a temporarily unreachable subtree: a reasoned control inside a
14
+ * collapsed accordion panel (`inert`) or a `display: none` parent is still the element a description
15
+ * belongs to once the panel opens, and neither state change re-renders the describer.
16
+ */
17
+ export declare const firstFocusable: (root: HTMLElement) => HTMLElement | null;
@@ -1,3 +1,4 @@
1
1
  export * from './cn';
2
2
  export * from './omit';
3
3
  export * from './prepareForSlot';
4
+ export * from './uiCoreLocale';
@@ -0,0 +1 @@
1
+ export type UiCoreLocale = 'en' | 'no';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Development-only: a `StyledDialog` with no `dialogTitle`, `dialogLabel`, `ariaLabel` or heading in its
3
+ * content has no accessible name (WCAG 4.1.2, axe `aria-dialog-name`). Silent in production.
4
+ */
5
+ export declare function warnUnnamedDialog(dataTest: string | undefined): void;
@@ -1,7 +1,11 @@
1
1
  import { MouseEvent, ReactNode } from 'react';
2
+ import { TooltipProps } from '../../components/data-display/tooltip/StyledTooltip';
2
3
  export interface StyledMenuItemProps {
3
4
  children?: ReactNode;
4
5
  disabled?: boolean;
6
+ /** Why a disabled item can't be used; keeps it reachable by arrow keys and shows the reason. */
7
+ disabledReason?: ReactNode;
8
+ reasonPlacement?: TooltipProps['placement'];
5
9
  selected?: boolean;
6
10
  className?: string;
7
11
  classes?: {
@@ -15,4 +19,4 @@ export interface StyledMenuItemProps {
15
19
  * Table row-action menu item (replaces MUI `MenuItem`). Native `role="menuitem"`; disabled stays
16
20
  * focusable but non-interactive. TASK-404.
17
21
  */
18
- export declare const StyledMenuItem: ({ children, disabled, selected, className, classes, onClick, onMouseDown, onMouseUp, }: StyledMenuItemProps) => JSX.Element;
22
+ export declare const StyledMenuItem: ({ children, disabled, disabledReason, reasonPlacement, selected, className, classes, onClick, onMouseDown, onMouseUp, }: StyledMenuItemProps) => JSX.Element;