@box/blueprint-web 14.31.0 → 14.32.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.
@@ -1,13 +1,21 @@
1
1
  import { type ReactNode } from 'react';
2
+ import { type AccordionIconComponent, type StartElementBackgroundVariant } from './types';
3
+ export type { StartElementBackgroundVariant };
2
4
  export interface AccordionItemHeaderProps {
3
5
  title: string;
4
6
  status?: number;
5
7
  iconVariant?: 'alert-error' | 'status-pending' | 'status-modified';
6
8
  iconAriaLabel?: string;
7
9
  error?: string;
10
+ /** @deprecated Prefer `startIcon`. */
8
11
  startElement?: ReactNode;
12
+ startIcon?: AccordionIconComponent;
13
+ startElementBackgroundColor?: StartElementBackgroundVariant;
14
+ subtitle?: string;
15
+ subtitleIcon?: ReactNode;
16
+ endText?: string;
9
17
  }
10
- export declare const AccordionItemHeader: ({ title, status, iconVariant, iconAriaLabel, error, startElement, }: AccordionItemHeaderProps) => import("react/jsx-runtime").JSX.Element;
18
+ export declare const AccordionItemHeader: ({ title, status, iconVariant, iconAriaLabel, error, startElement, startIcon, startElementBackgroundColor, subtitle, subtitleIcon, endText, }: AccordionItemHeaderProps) => import("react/jsx-runtime").JSX.Element;
11
19
  export interface AccordionActionSlotProps {
12
20
  action?: ReactNode;
13
21
  }
@@ -2,9 +2,10 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { ClockBadge, Alert } from '@box/blueprint-web-assets/icons/Fill';
3
3
  import { bpSurfaceBadgeSurfaceModified, Orange100, GrayWhite, SurfaceBadgeErrorSurface, SurfaceStatusSurfaceRed, SurfaceStatusSurfaceLightBlue } from '@box/blueprint-web-assets/tokens/tokens';
4
4
  import clsx from 'clsx';
5
- import { createElement } from 'react';
5
+ import { useRef, createElement } from 'react';
6
6
  import { Status } from '../status/status.js';
7
7
  import { isDefined } from '../utils/isDefined.js';
8
+ import { useFullTextTooltip } from '../utils/useFullTextTooltip.js';
8
9
  import styles from './accordion.module.js';
9
10
 
10
11
  const accordionIconVariantToIconProps = {
@@ -26,13 +27,34 @@ const statusColors = {
26
27
  error: SurfaceStatusSurfaceRed,
27
28
  default: SurfaceStatusSurfaceLightBlue
28
29
  };
30
+ const startElementVariantClassNames = {
31
+ default: styles.accordionStartElementFramedDefault,
32
+ box: styles.accordionStartElementFramedBox,
33
+ forms: styles.accordionStartElementFramedForms,
34
+ docgen: styles.accordionStartElementFramedDocgen,
35
+ sign: styles.accordionStartElementFramedSign
36
+ };
37
+ const START_ICON_SIZE = '1.25rem';
38
+ const FRAMED_START_ICON_COLOR_MAP = {
39
+ default: 'var(--bp-gray-100)',
40
+ box: 'var(--bp-box-blue-100)',
41
+ forms: 'var(--bp-green-light-110)',
42
+ docgen: 'var(--bp-grimace-120)',
43
+ sign: 'var(--bp-dark-blue-100)'
44
+ };
45
+ const DEFAULT_START_ICON_COLOR = 'var(--bp-icon-icon-on-light-secondary)';
29
46
  const AccordionItemHeader = ({
30
47
  title,
31
48
  status,
32
49
  iconVariant,
33
50
  iconAriaLabel,
34
51
  error,
35
- startElement
52
+ startElement,
53
+ startIcon,
54
+ startElementBackgroundColor,
55
+ subtitle,
56
+ subtitleIcon,
57
+ endText
36
58
  }) => {
37
59
  const iconProps = iconVariant ? accordionIconVariantToIconProps[iconVariant] : undefined;
38
60
  const {
@@ -40,15 +62,62 @@ const AccordionItemHeader = ({
40
62
  iconColor,
41
63
  icon
42
64
  } = iconProps ?? {};
65
+ const hasSubtitle = isDefined(subtitle) && subtitle !== '';
66
+ const framedStartElementClassName = startElementBackgroundColor ? startElementVariantClassNames[startElementBackgroundColor] : undefined;
67
+ const startIconColor = startElementBackgroundColor ? FRAMED_START_ICON_COLOR_MAP[startElementBackgroundColor] : DEFAULT_START_ICON_COLOR;
68
+ const renderedStartElement = startIcon ? /*#__PURE__*/createElement(startIcon, {
69
+ width: START_ICON_SIZE,
70
+ height: START_ICON_SIZE,
71
+ color: startIconColor,
72
+ 'aria-hidden': true
73
+ }) : startElement;
74
+ // Single-line ellipsis with full-title tooltip on overflow. Tooltip-only
75
+ // (no nested `Focusable`) because the title sits inside the Radix
76
+ // Accordion `<button>` trigger — adding a tabbable wrapper here would
77
+ // create nested interactives. The trigger already owns hover/focus, so
78
+ // pointing/keyboarding the row surfaces the tooltip when the text is cut.
79
+ const titleRef = useRef(null);
80
+ const {
81
+ Wrapper: TitleTooltip,
82
+ wrapperProps: titleTooltipProps
83
+ } = useFullTextTooltip({
84
+ ref: titleRef,
85
+ textValue: title
86
+ });
43
87
  return jsxs("h4", {
44
88
  className: styles.accordionHeader,
45
89
  children: [jsxs("span", {
46
90
  className: styles.accordionTitleGroup,
47
- children: [startElement && jsx("span", {
48
- className: styles.accordionStartElement,
49
- children: startElement
50
- }), jsx("span", {
51
- children: title
91
+ children: [renderedStartElement && jsx("span", {
92
+ className: clsx(styles.accordionStartElement, framedStartElementClassName, {
93
+ [styles.accordionStartElementFramed]: framedStartElementClassName
94
+ }),
95
+ style: framedStartElementClassName ? {
96
+ color: startIconColor
97
+ } : undefined,
98
+ children: renderedStartElement
99
+ }), jsxs("span", {
100
+ className: styles.accordionTitleContent,
101
+ children: [jsx(TitleTooltip, {
102
+ content: title,
103
+ ...titleTooltipProps,
104
+ children: jsx("span", {
105
+ ref: titleRef,
106
+ className: styles.accordionTitleText,
107
+ "data-testid": "accordion-item-title",
108
+ children: title
109
+ })
110
+ }), hasSubtitle && jsxs("span", {
111
+ className: styles.accordionSubtitle,
112
+ children: [subtitleIcon && jsx("span", {
113
+ "aria-hidden": "true",
114
+ className: styles.accordionSubtitleIcon,
115
+ children: subtitleIcon
116
+ }), jsx("span", {
117
+ className: styles.accordionSubtitleText,
118
+ children: subtitle
119
+ })]
120
+ })]
52
121
  })]
53
122
  }), isDefined(status) && jsx("span", {
54
123
  className: styles.accordionHeaderInfoSlot,
@@ -78,6 +147,12 @@ const AccordionItemHeader = ({
78
147
  width: '0.75rem'
79
148
  })
80
149
  })
150
+ }), isDefined(endText) && endText !== '' && jsx("span", {
151
+ className: styles.accordionHeaderInfoSlot,
152
+ children: jsx("span", {
153
+ className: styles.accordionEndText,
154
+ children: endText
155
+ })
81
156
  })]
82
157
  });
83
158
  };
@@ -77,6 +77,11 @@ const Item = props => {
77
77
  iconAriaLabel,
78
78
  title,
79
79
  startElement,
80
+ startIcon,
81
+ startElementBackgroundColor,
82
+ subtitle,
83
+ subtitleIcon,
84
+ endText,
80
85
  disabled,
81
86
  error,
82
87
  errorIconAriaLabel,
@@ -113,12 +118,17 @@ const Item = props => {
113
118
  }),
114
119
  disabled: disabled,
115
120
  children: jsx(AccordionItemHeader, {
121
+ endText: endText,
116
122
  error: error,
117
123
  iconAriaLabel: iconAriaLabel,
118
124
  iconVariant: iconVariant,
125
+ startElement: startElement,
126
+ startElementBackgroundColor: startElementBackgroundColor,
127
+ startIcon: startIcon,
119
128
  status: status,
120
- title: title,
121
- startElement: startElement
129
+ subtitle: subtitle,
130
+ subtitleIcon: subtitleIcon,
131
+ title: title
122
132
  })
123
133
  }), jsx(AccordionActionSlot, {
124
134
  action: action
@@ -1,4 +1,4 @@
1
1
  import '../index.css';
2
- var styles = {"accordionItem":"bp_accordion_module_accordionItem--4551e","accordionContent":"bp_accordion_module_accordionContent--4551e","accordionContentLayout":"bp_accordion_module_accordionContentLayout--4551e","accordion":"bp_accordion_module_accordion--4551e","accordionContentWrapper":"bp_accordion_module_accordionContentWrapper--4551e","accordionInlineErrorWrapper":"bp_accordion_module_accordionInlineErrorWrapper--4551e","highlighted":"bp_accordion_module_highlighted--4551e","accordionFixedContent":"bp_accordion_module_accordionFixedContent--4551e","accordionHeader":"bp_accordion_module_accordionHeader--4551e","accordionTitleGroup":"bp_accordion_module_accordionTitleGroup--4551e","accordionStartElement":"bp_accordion_module_accordionStartElement--4551e","accordionHeaderInfoSlot":"bp_accordion_module_accordionHeaderInfoSlot--4551e","accordionSlot":"bp_accordion_module_accordionSlot--4551e","accordionChevronWrapper":"bp_accordion_module_accordionChevronWrapper--4551e","accordionTriggerIcon":"bp_accordion_module_accordionTriggerIcon--4551e","accordionIconWrapper":"bp_accordion_module_accordionIconWrapper--4551e","bgOnly":"bp_accordion_module_bgOnly--4551e","withIcon":"bp_accordion_module_withIcon--4551e","accordionTrigger":"bp_accordion_module_accordionTrigger--4551e","disabled":"bp_accordion_module_disabled--4551e","accordionHeaderContainer":"bp_accordion_module_accordionHeaderContainer--4551e"};
2
+ var styles = {"accordionItem":"bp_accordion_module_accordionItem--c8f78","accordionContent":"bp_accordion_module_accordionContent--c8f78","accordionContentLayout":"bp_accordion_module_accordionContentLayout--c8f78","accordion":"bp_accordion_module_accordion--c8f78","accordionContentWrapper":"bp_accordion_module_accordionContentWrapper--c8f78","accordionInlineErrorWrapper":"bp_accordion_module_accordionInlineErrorWrapper--c8f78","highlighted":"bp_accordion_module_highlighted--c8f78","accordionFixedContent":"bp_accordion_module_accordionFixedContent--c8f78","accordionHeader":"bp_accordion_module_accordionHeader--c8f78","accordionTitleGroup":"bp_accordion_module_accordionTitleGroup--c8f78","accordionStartElement":"bp_accordion_module_accordionStartElement--c8f78","accordionStartElementFramed":"bp_accordion_module_accordionStartElementFramed--c8f78","accordionStartElementFramedDefault":"bp_accordion_module_accordionStartElementFramedDefault--c8f78","accordionStartElementFramedBox":"bp_accordion_module_accordionStartElementFramedBox--c8f78","accordionStartElementFramedForms":"bp_accordion_module_accordionStartElementFramedForms--c8f78","accordionStartElementFramedDocgen":"bp_accordion_module_accordionStartElementFramedDocgen--c8f78","accordionStartElementFramedSign":"bp_accordion_module_accordionStartElementFramedSign--c8f78","accordionTitleContent":"bp_accordion_module_accordionTitleContent--c8f78","accordionTitleText":"bp_accordion_module_accordionTitleText--c8f78","accordionSubtitle":"bp_accordion_module_accordionSubtitle--c8f78","accordionSubtitleIcon":"bp_accordion_module_accordionSubtitleIcon--c8f78","accordionSubtitleText":"bp_accordion_module_accordionSubtitleText--c8f78","accordionEndText":"bp_accordion_module_accordionEndText--c8f78","accordionHeaderInfoSlot":"bp_accordion_module_accordionHeaderInfoSlot--c8f78","accordionSlot":"bp_accordion_module_accordionSlot--c8f78","accordionChevronWrapper":"bp_accordion_module_accordionChevronWrapper--c8f78","accordionTriggerIcon":"bp_accordion_module_accordionTriggerIcon--c8f78","accordionIconWrapper":"bp_accordion_module_accordionIconWrapper--c8f78","bgOnly":"bp_accordion_module_bgOnly--c8f78","withIcon":"bp_accordion_module_withIcon--c8f78","accordionTrigger":"bp_accordion_module_accordionTrigger--c8f78","disabled":"bp_accordion_module_disabled--c8f78","accordionHeaderContainer":"bp_accordion_module_accordionHeaderContainer--c8f78"};
3
3
 
4
4
  export { styles as default };
@@ -1,2 +1,2 @@
1
1
  export { Accordion } from './accordion';
2
- export type { AccordionFixedItem, AccordionItem, AccordionProps, AccordionSectionItem } from './types';
2
+ export type { AccordionFixedItem, AccordionItem, AccordionProps, AccordionSectionItem, StartElementBackgroundVariant, } from './types';
@@ -1,5 +1,37 @@
1
1
  import { type AccordionMultipleProps, type AccordionSingleProps, type AccordionItemProps as RadixAccordionItemProps } from '@radix-ui/react-accordion';
2
+ import { type FunctionComponent, type PropsWithChildren, type SVGProps } from 'react';
2
3
  import { type RequireAllOrNone } from 'type-fest';
4
+ export type AccordionIconComponent = FunctionComponent<PropsWithChildren<SVGProps<SVGSVGElement>>>;
5
+ /** Tint for the optional framed start-of-row icon (modernized items only). */
6
+ export type StartElementBackgroundVariant = 'default' | 'box' | 'forms' | 'docgen' | 'sign';
7
+ /**
8
+ * Mutually-exclusive start-of-row slot. Use either `startIcon` (preferred,
9
+ * icon-component pattern) or `startElement` (legacy `ReactNode`).
10
+ */
11
+ type StartSlotProps = {
12
+ /**
13
+ * @deprecated Prefer `startIcon`, which renders the icon with
14
+ * consistent size, color, and aria attributes. Still supported for
15
+ * backward compatibility — pass any `ReactNode` to render to the
16
+ * left of the title.
17
+ */
18
+ startElement?: React.ReactNode;
19
+ startIcon?: never;
20
+ } | {
21
+ startElement?: never;
22
+ /**
23
+ * Icon **component** (e.g. `Search`, `Folder` from
24
+ * `@box/blueprint-web-assets/icons/Medium`) rendered to the left of
25
+ * the title. The Accordion handles sizing (20x20 per the Figma
26
+ * "L. Element=Icon" spec), `currentColor` inheritance, and
27
+ * `aria-hidden`, matching the icon-component pattern used by
28
+ * `BaseButton`, `IconButton`, and `BaseInlineNotice`.
29
+ *
30
+ * When combined with `startElementBackgroundColor`, the icon is
31
+ * placed inside the matching 32x32 framed container.
32
+ */
33
+ startIcon?: AccordionIconComponent;
34
+ };
3
35
  interface Loading {
4
36
  /** Loading state of the section. Loading sections are displaying a placeholder. When this is true `loadingAriaLabel` must also be provided. */
5
37
  loading: boolean;
@@ -27,21 +59,30 @@ interface IconProps {
27
59
  iconAriaLabel: string;
28
60
  }
29
61
  /**
30
- * Only one header slot is allowed at a time: `status`, `iconVariant`, or `action`.
62
+ * Only one header slot is allowed at a time: `status`, `iconVariant`, `action`, or `endText`.
31
63
  */
32
64
  type HeaderSlotProps = {
33
65
  status?: number;
34
66
  iconVariant?: never;
35
67
  iconAriaLabel?: never;
36
68
  action?: never;
69
+ endText?: never;
37
70
  } | ({
38
71
  status?: never;
39
72
  action?: never;
73
+ endText?: never;
40
74
  } & RequireAllOrNone<IconProps, keyof IconProps>) | {
41
75
  status?: never;
42
76
  iconVariant?: never;
43
77
  iconAriaLabel?: never;
44
78
  action?: React.ReactNode;
79
+ endText?: never;
80
+ } | {
81
+ status?: never;
82
+ iconVariant?: never;
83
+ iconAriaLabel?: never;
84
+ action?: never;
85
+ endText?: string;
45
86
  };
46
87
  export type AccordionBaseItem = {
47
88
  /**
@@ -65,9 +106,32 @@ export type AccordionSectionItem = AccordionBaseItem & Omit<RadixAccordionItemPr
65
106
  */
66
107
  disabled?: boolean;
67
108
  /**
68
- * Element displayed to the left of the title.
109
+ * When provided, frames the start-of-row icon in a 32x32 container
110
+ * tinted with the matching Box brand surface color. Each variant
111
+ * pairs a "brand-{variant}-tertiary" tinted background with the
112
+ * corresponding brand foreground glyph:
113
+ *
114
+ * - `default` – `surface-surface-tertiary` (#F5F5F5) background, `gray-100` (#909090) icon
115
+ * - `box` – `brand-box-tertiary` (#E5EFFA) background, `box-blue-100` (#0061D5) icon
116
+ * - `forms` – `brand-forms-tertiary` (#E9F8F2) background, `green-light-110` (#22AF74) icon
117
+ * - `docgen` – `brand-docgen-tertiary` (#ECE9F8) background, `brand-docgen-primary` (purple) icon
118
+ * - `sign` – `brand-sign-tertiary` (#E5EBF2) background, `dark-blue-100` (#003C84) icon
119
+ *
120
+ * Has no effect unless `startIcon` (or the legacy `startElement`) is
121
+ * also provided. Only applies to modernized accordion items.
69
122
  */
70
- startElement?: React.ReactNode;
123
+ startElementBackgroundColor?: StartElementBackgroundVariant;
124
+ /**
125
+ * Optional supplemental text displayed below the title in the header.
126
+ * Only applies to modernized accordion items.
127
+ */
128
+ subtitle?: string;
129
+ /**
130
+ * Optional decorative icon displayed before the subtitle text.
131
+ * Has no effect unless `subtitle` is also provided.
132
+ * Only applies to modernized accordion items.
133
+ */
134
+ subtitleIcon?: React.ReactNode;
71
135
  /**
72
136
  * When true, the item renders a colored border and tinted background when
73
137
  * expanded to visually indicate it is the active/selected item. The border
@@ -76,7 +140,7 @@ export type AccordionSectionItem = AccordionBaseItem & Omit<RadixAccordionItemPr
76
140
  * Only applies to modernized accordion items.
77
141
  */
78
142
  highlighted?: boolean;
79
- } & HeaderSlotProps;
143
+ } & HeaderSlotProps & StartSlotProps;
80
144
  export type AccordionItem = AccordionSectionItem | AccordionFixedItem;
81
145
  export type AccordionItemProps = AccordionItem & React.ComponentPropsWithRef<'div'>;
82
146
  export type AccordionProps = (AccordionSingleProps | AccordionMultipleProps) & React.ComponentPropsWithRef<'div'>;
@@ -0,0 +1 @@
1
+ export {};