@mediaradar/design-components 0.17.0 → 0.18.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.
package/dist/index.d.cts CHANGED
@@ -398,6 +398,7 @@ declare function CheckBoxBox({ checked, indeterminate, disabled, className }: Ch
398
398
 
399
399
  /**
400
400
  * @pattern DateRangeFilter
401
+ * @status stable
401
402
  * @description Date-range control -- the two-line chip (caption + value) with
402
403
  * a chevron that opens a two-pane picker: preset shortcuts on the left, a
403
404
  * dual-month calendar with range highlighting on the right, Start/End fields,
@@ -675,6 +676,7 @@ declare function SelectFilter({ caption, value, options, onChange, align, fullWi
675
676
 
676
677
  /**
677
678
  * @pattern Card
679
+ * @status stable
678
680
  * @description Layout primitive for grouping content in a bordered, elevated
679
681
  * surface. Width is controlled by the parent container, not the Card itself,
680
682
  * so it works unchanged as a fixed-width rail item or a grid cell. Applies an
@@ -683,7 +685,8 @@ declare function SelectFilter({ caption, value, options, onChange, align, fullWi
683
685
  * switches the card into a padded content layout, replacing the header/body
684
686
  * shell mr-clientapps-insights-studio's own `CardShell` used to hand-roll.
685
687
  * @tokens rounded-card, border-border-default, bg-surface, shadow-raised,
686
- * hover:shadow-overlay, p-card-padding, text-text-primary, text-text-tertiary
688
+ * hover:shadow-overlay, p-card-padding, text-style-card-title, text-xs,
689
+ * text-text-tertiary
687
690
  * @behavior Without `title`, children render directly as the card's only
688
691
  * content -- no padding is applied, so callers that need a full-bleed image
689
692
  * or gradient flush against the card edge (the common case in
@@ -693,6 +696,10 @@ declare function SelectFilter({ caption, value, options, onChange, align, fullWi
693
696
  * standard padding, matching `CardShell`'s layout so it can be swapped in
694
697
  * directly. `staticCard` drops the hover-shadow transition for non-clickable
695
698
  * content (a KPI tile, a read-only summary) -- there's nothing to respond to.
699
+ * @a11y With `title`, the title renders as a real heading (`titleAs`,
700
+ * default `h3`) so screen-reader users can jump between cards; pick the
701
+ * level that fits the page outline rather than one that looks right --
702
+ * every level is styled the same. The card itself is an `<article>`.
696
703
  * @variant interactive - Default. Clickable/hoverable content -- deeper
697
704
  * shadow on hover signals it responds to the cursor.
698
705
  * @variant static - Set `staticCard`. Non-clickable content -- no hover
@@ -704,7 +711,7 @@ declare function SelectFilter({ caption, value, options, onChange, align, fullWi
704
711
  * <p>Body content</p>
705
712
  * </Card>
706
713
  * <Card staticCard>Non-interactive content</Card>
707
- * <Card title="Spend trend" subtitle="Last 90 days" action={<Link to="/spend">View all</Link>}>
714
+ * <Card title="Spend trend" subtitle="Last 90 days" titleAs="h2" action={<Link to="/spend">View all</Link>}>
708
715
  * <SpendChart />
709
716
  * </Card>
710
717
  * ```
@@ -718,6 +725,13 @@ interface CardProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
718
725
  * plus the card's standard content padding, matching `CardShell`.
719
726
  */
720
727
  title?: string;
728
+ /**
729
+ * Heading level for `title`. Defaults to `h3`; pass `h2` where the card's
730
+ * title is the first heading under the page's `h1`, so the page doesn't skip
731
+ * a level. Only the element changes: every level looks the same
732
+ * (`text-style-card-title`). Ignored without `title`.
733
+ */
734
+ titleAs?: "h2" | "h3" | "h4";
721
735
  /** Shown under `title` in a muted tertiary color. Ignored without `title`. */
722
736
  subtitle?: string;
723
737
  /** Trailing header content, e.g. a "View all" link. Ignored without `title`. */
@@ -839,6 +853,7 @@ interface SummaryGroup {
839
853
  }
840
854
  /**
841
855
  * @pattern WorkspaceSelectedCard
856
+ * @status stable
842
857
  * @description Sticky recap card listing what each section's picker has
843
858
  * added, as collapsible groups with per-item remove and "Remove all" --
844
859
  * generic over the `SummaryGroup` shape, no domain coupling. Reconciled from
@@ -900,6 +915,7 @@ declare function WorkspaceSelectedCard({ groups, testIdPrefix }: WorkspaceSelect
900
915
 
901
916
  /**
902
917
  * @pattern BarChartPlaceholder
918
+ * @status stable
903
919
  * @description Neutral, dataless stand-in for a ranked-bar chart --
904
920
  * descending bar profile under a blank-labelled axis frame (dashed
905
921
  * gridlines, blank y/x-tick boxes), so an unsatisfiable filter still reads
@@ -929,6 +945,7 @@ declare function BarChartPlaceholder({ height }: BarChartPlaceholderProps): reac
929
945
 
930
946
  /**
931
947
  * @pattern LineChartPlaceholder
948
+ * @status stable
932
949
  * @description Neutral, dataless stand-in for a multi-series trend chart --
933
950
  * four grey paths over the shared axis frame, each with a solid-then-dashed
934
951
  * handoff for the live chart's forecast tail, and a point at every sample.
@@ -959,6 +976,7 @@ declare function LineChartPlaceholder({ height }: LineChartPlaceholderProps): re
959
976
 
960
977
  /**
961
978
  * @pattern PieChartPlaceholder
979
+ * @status stable
962
980
  * @description Neutral, dataless stand-in for a pie chart -- a grey disc
963
981
  * seamed where slices would fall, beside a blank legend. No axis, matching
964
982
  * the live pie it stands in for. Same no-results use as
@@ -1160,6 +1178,7 @@ declare function AdvertiserNoResults({ query, hint, }: AdvertiserNoResultsProps)
1160
1178
 
1161
1179
  /**
1162
1180
  * @pattern SelectAllVisible
1181
+ * @status stable
1163
1182
  * @description "Select all visible" row for a search-first picker's result
1164
1183
  * list -- only render it once the caller decides the visible result set is
1165
1184
  * long enough to warrant it (`insights-studio-prototype`'s three advertiser-
@@ -1193,6 +1212,7 @@ declare function SelectAllVisible({ count, allSelected, onToggle, testIdPrefix }
1193
1212
 
1194
1213
  /**
1195
1214
  * @pattern SelectedSection
1215
+ * @status stable
1196
1216
  * @description Collapsible "Selected (n)" container for a search-first
1197
1217
  * picker. Selections can pile up and crowd out the live search results, so
1198
1218
  * the user can fold them away to refocus on the search. Controlled so the
@@ -1337,6 +1357,7 @@ interface SelectionPickerProps<T extends PickerRow> {
1337
1357
  }
1338
1358
  /**
1339
1359
  * @pattern SelectionPicker
1360
+ * @status stable
1340
1361
  * @description Generic search-and-filter picker for building a selection out
1341
1362
  * of a larger catalog -- a search box plus filter dropdowns above a checkbox
1342
1363
  * table, with an optional "Search / Account lists" source toggle for pulling
@@ -1364,10 +1385,12 @@ interface SelectionPickerProps<T extends PickerRow> {
1364
1385
  * with both an `onChange` and the row's own `onClick` -- `CheckBox` stops
1365
1386
  * click propagation before firing `onChange`, which is exactly what lets it
1366
1387
  * sit inside a row that toggles on its own click without double-firing.
1367
- * @a11y Search is an <input>; result rows and the account-list menu
1368
- * (role="menu") are buttons. Escape closes the menu. Loading, empty and
1369
- * error states announce through role="status", so a consumer should NOT
1370
- * wrap this in its own live region -- that would double-announce.
1388
+ * @a11y Search is an <input>; result rows are buttons. Each filter dropdown
1389
+ * is a disclosure (aria-expanded) opening a labelled group of checkboxes,
1390
+ * not a menu: it holds a search box and checkboxes, which a menu may not.
1391
+ * Escape closes it. Loading, empty and error states announce through
1392
+ * role="status", so a consumer should NOT wrap this in its own live region
1393
+ * -- that would double-announce.
1371
1394
  * @usage
1372
1395
  * ```tsx
1373
1396
  * <SelectionPicker
package/dist/index.d.ts CHANGED
@@ -398,6 +398,7 @@ declare function CheckBoxBox({ checked, indeterminate, disabled, className }: Ch
398
398
 
399
399
  /**
400
400
  * @pattern DateRangeFilter
401
+ * @status stable
401
402
  * @description Date-range control -- the two-line chip (caption + value) with
402
403
  * a chevron that opens a two-pane picker: preset shortcuts on the left, a
403
404
  * dual-month calendar with range highlighting on the right, Start/End fields,
@@ -675,6 +676,7 @@ declare function SelectFilter({ caption, value, options, onChange, align, fullWi
675
676
 
676
677
  /**
677
678
  * @pattern Card
679
+ * @status stable
678
680
  * @description Layout primitive for grouping content in a bordered, elevated
679
681
  * surface. Width is controlled by the parent container, not the Card itself,
680
682
  * so it works unchanged as a fixed-width rail item or a grid cell. Applies an
@@ -683,7 +685,8 @@ declare function SelectFilter({ caption, value, options, onChange, align, fullWi
683
685
  * switches the card into a padded content layout, replacing the header/body
684
686
  * shell mr-clientapps-insights-studio's own `CardShell` used to hand-roll.
685
687
  * @tokens rounded-card, border-border-default, bg-surface, shadow-raised,
686
- * hover:shadow-overlay, p-card-padding, text-text-primary, text-text-tertiary
688
+ * hover:shadow-overlay, p-card-padding, text-style-card-title, text-xs,
689
+ * text-text-tertiary
687
690
  * @behavior Without `title`, children render directly as the card's only
688
691
  * content -- no padding is applied, so callers that need a full-bleed image
689
692
  * or gradient flush against the card edge (the common case in
@@ -693,6 +696,10 @@ declare function SelectFilter({ caption, value, options, onChange, align, fullWi
693
696
  * standard padding, matching `CardShell`'s layout so it can be swapped in
694
697
  * directly. `staticCard` drops the hover-shadow transition for non-clickable
695
698
  * content (a KPI tile, a read-only summary) -- there's nothing to respond to.
699
+ * @a11y With `title`, the title renders as a real heading (`titleAs`,
700
+ * default `h3`) so screen-reader users can jump between cards; pick the
701
+ * level that fits the page outline rather than one that looks right --
702
+ * every level is styled the same. The card itself is an `<article>`.
696
703
  * @variant interactive - Default. Clickable/hoverable content -- deeper
697
704
  * shadow on hover signals it responds to the cursor.
698
705
  * @variant static - Set `staticCard`. Non-clickable content -- no hover
@@ -704,7 +711,7 @@ declare function SelectFilter({ caption, value, options, onChange, align, fullWi
704
711
  * <p>Body content</p>
705
712
  * </Card>
706
713
  * <Card staticCard>Non-interactive content</Card>
707
- * <Card title="Spend trend" subtitle="Last 90 days" action={<Link to="/spend">View all</Link>}>
714
+ * <Card title="Spend trend" subtitle="Last 90 days" titleAs="h2" action={<Link to="/spend">View all</Link>}>
708
715
  * <SpendChart />
709
716
  * </Card>
710
717
  * ```
@@ -718,6 +725,13 @@ interface CardProps extends Omit<React.HTMLAttributes<HTMLElement>, "title"> {
718
725
  * plus the card's standard content padding, matching `CardShell`.
719
726
  */
720
727
  title?: string;
728
+ /**
729
+ * Heading level for `title`. Defaults to `h3`; pass `h2` where the card's
730
+ * title is the first heading under the page's `h1`, so the page doesn't skip
731
+ * a level. Only the element changes: every level looks the same
732
+ * (`text-style-card-title`). Ignored without `title`.
733
+ */
734
+ titleAs?: "h2" | "h3" | "h4";
721
735
  /** Shown under `title` in a muted tertiary color. Ignored without `title`. */
722
736
  subtitle?: string;
723
737
  /** Trailing header content, e.g. a "View all" link. Ignored without `title`. */
@@ -839,6 +853,7 @@ interface SummaryGroup {
839
853
  }
840
854
  /**
841
855
  * @pattern WorkspaceSelectedCard
856
+ * @status stable
842
857
  * @description Sticky recap card listing what each section's picker has
843
858
  * added, as collapsible groups with per-item remove and "Remove all" --
844
859
  * generic over the `SummaryGroup` shape, no domain coupling. Reconciled from
@@ -900,6 +915,7 @@ declare function WorkspaceSelectedCard({ groups, testIdPrefix }: WorkspaceSelect
900
915
 
901
916
  /**
902
917
  * @pattern BarChartPlaceholder
918
+ * @status stable
903
919
  * @description Neutral, dataless stand-in for a ranked-bar chart --
904
920
  * descending bar profile under a blank-labelled axis frame (dashed
905
921
  * gridlines, blank y/x-tick boxes), so an unsatisfiable filter still reads
@@ -929,6 +945,7 @@ declare function BarChartPlaceholder({ height }: BarChartPlaceholderProps): reac
929
945
 
930
946
  /**
931
947
  * @pattern LineChartPlaceholder
948
+ * @status stable
932
949
  * @description Neutral, dataless stand-in for a multi-series trend chart --
933
950
  * four grey paths over the shared axis frame, each with a solid-then-dashed
934
951
  * handoff for the live chart's forecast tail, and a point at every sample.
@@ -959,6 +976,7 @@ declare function LineChartPlaceholder({ height }: LineChartPlaceholderProps): re
959
976
 
960
977
  /**
961
978
  * @pattern PieChartPlaceholder
979
+ * @status stable
962
980
  * @description Neutral, dataless stand-in for a pie chart -- a grey disc
963
981
  * seamed where slices would fall, beside a blank legend. No axis, matching
964
982
  * the live pie it stands in for. Same no-results use as
@@ -1160,6 +1178,7 @@ declare function AdvertiserNoResults({ query, hint, }: AdvertiserNoResultsProps)
1160
1178
 
1161
1179
  /**
1162
1180
  * @pattern SelectAllVisible
1181
+ * @status stable
1163
1182
  * @description "Select all visible" row for a search-first picker's result
1164
1183
  * list -- only render it once the caller decides the visible result set is
1165
1184
  * long enough to warrant it (`insights-studio-prototype`'s three advertiser-
@@ -1193,6 +1212,7 @@ declare function SelectAllVisible({ count, allSelected, onToggle, testIdPrefix }
1193
1212
 
1194
1213
  /**
1195
1214
  * @pattern SelectedSection
1215
+ * @status stable
1196
1216
  * @description Collapsible "Selected (n)" container for a search-first
1197
1217
  * picker. Selections can pile up and crowd out the live search results, so
1198
1218
  * the user can fold them away to refocus on the search. Controlled so the
@@ -1337,6 +1357,7 @@ interface SelectionPickerProps<T extends PickerRow> {
1337
1357
  }
1338
1358
  /**
1339
1359
  * @pattern SelectionPicker
1360
+ * @status stable
1340
1361
  * @description Generic search-and-filter picker for building a selection out
1341
1362
  * of a larger catalog -- a search box plus filter dropdowns above a checkbox
1342
1363
  * table, with an optional "Search / Account lists" source toggle for pulling
@@ -1364,10 +1385,12 @@ interface SelectionPickerProps<T extends PickerRow> {
1364
1385
  * with both an `onChange` and the row's own `onClick` -- `CheckBox` stops
1365
1386
  * click propagation before firing `onChange`, which is exactly what lets it
1366
1387
  * sit inside a row that toggles on its own click without double-firing.
1367
- * @a11y Search is an <input>; result rows and the account-list menu
1368
- * (role="menu") are buttons. Escape closes the menu. Loading, empty and
1369
- * error states announce through role="status", so a consumer should NOT
1370
- * wrap this in its own live region -- that would double-announce.
1388
+ * @a11y Search is an <input>; result rows are buttons. Each filter dropdown
1389
+ * is a disclosure (aria-expanded) opening a labelled group of checkboxes,
1390
+ * not a menu: it holds a search box and checkboxes, which a menu may not.
1391
+ * Escape closes it. Loading, empty and error states announce through
1392
+ * role="status", so a consumer should NOT wrap this in its own live region
1393
+ * -- that would double-announce.
1371
1394
  * @usage
1372
1395
  * ```tsx
1373
1396
  * <SelectionPicker
package/dist/index.js CHANGED
@@ -1485,7 +1485,7 @@ function SelectFilter({
1485
1485
  // src/Card.tsx
1486
1486
  import { forwardRef } from "react";
1487
1487
  import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1488
- var Card = forwardRef(function Card2({ staticCard, title, subtitle, action, className, children, ...rest }, ref) {
1488
+ var Card = forwardRef(function Card2({ staticCard, title, titleAs: TitleTag = "h3", subtitle, action, className, children, ...rest }, ref) {
1489
1489
  return /* @__PURE__ */ jsx12(
1490
1490
  "article",
1491
1491
  {
@@ -1500,8 +1500,8 @@ var Card = forwardRef(function Card2({ staticCard, title, subtitle, action, clas
1500
1500
  children: title ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-1 flex-col p-card-padding", children: [
1501
1501
  /* @__PURE__ */ jsxs11("div", { className: "flex items-start justify-between gap-2", children: [
1502
1502
  /* @__PURE__ */ jsxs11("div", { children: [
1503
- /* @__PURE__ */ jsx12("h3", { className: "text-[15px] font-semibold leading-snug text-text-primary", children: title }),
1504
- subtitle && /* @__PURE__ */ jsx12("p", { className: "mt-0.5 text-[12px] leading-snug text-text-tertiary", children: subtitle })
1503
+ /* @__PURE__ */ jsx12(TitleTag, { className: "text-style-card-title", children: title }),
1504
+ subtitle && /* @__PURE__ */ jsx12("p", { className: "mt-0.5 text-xs leading-snug text-text-tertiary", children: subtitle })
1505
1505
  ] }),
1506
1506
  action && /* @__PURE__ */ jsx12("div", { className: "shrink-0 text-text-tertiary", children: action })
1507
1507
  ] }),
@@ -2344,7 +2344,8 @@ function SelectionPicker({
2344
2344
  "grid items-center gap-2 px-3 py-2.5",
2345
2345
  rowGridCols,
2346
2346
  isLocked ? "cursor-default opacity-50" : "cursor-pointer hover:bg-subtle",
2347
- !isLocked && isSel && "opacity-60"
2347
+ // 70%, not lower: selected rows' text must keep 4.5:1 contrast.
2348
+ !isLocked && isSel && "opacity-70"
2348
2349
  ),
2349
2350
  children: [
2350
2351
  isLocked ? /* @__PURE__ */ jsx24("span", { "aria-hidden": "true" }) : /* @__PURE__ */ jsx24(
@@ -2579,7 +2580,6 @@ function FilterDropdown({
2579
2580
  ref: btnRef,
2580
2581
  type: "button",
2581
2582
  onClick: () => setOpen((v) => !v),
2582
- "aria-haspopup": "menu",
2583
2583
  "aria-expanded": open,
2584
2584
  "data-testid": testIdPrefix && `${testIdPrefix}-filter-${filterSlug}`,
2585
2585
  className: "inline-flex h-9 items-center gap-1.5 whitespace-nowrap rounded-control border border-border-default bg-input px-3 text-[13px] font-medium text-text-secondary transition-colors hover:bg-subtle",
@@ -2594,7 +2594,7 @@ function FilterDropdown({
2594
2594
  "div",
2595
2595
  {
2596
2596
  ref: menuRef,
2597
- role: "menu",
2597
+ role: "group",
2598
2598
  "aria-label": filter.label,
2599
2599
  style: { top: pos.top, left: pos.left },
2600
2600
  className: "fixed z-[80] min-w-[220px] rounded-card border border-border-default bg-surface p-2 shadow-overlay",