@mediaradar/design-components 0.16.2 → 0.17.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.
package/dist/index.d.cts CHANGED
@@ -1733,6 +1733,15 @@ declare function sortProp<K extends string>(sort: SortState<K>): {
1733
1733
  key: K | null;
1734
1734
  dir: SortDir;
1735
1735
  };
1736
+ /**
1737
+ * The `aria-sort` value for column `key`'s <th>: "ascending"/"descending" on
1738
+ * the active column, nothing on the rest. aria-sort belongs on the column
1739
+ * header, not on the SortButton inside it, and only the sorted column carries it.
1740
+ */
1741
+ declare function ariaSortFor<K extends string>(sort: SortState<K> | {
1742
+ key: K | null;
1743
+ dir: SortDir;
1744
+ }, key: K): "ascending" | "descending" | undefined;
1736
1745
 
1737
1746
  interface SortButtonProps<K extends string> {
1738
1747
  label: string;
@@ -1745,6 +1754,15 @@ interface SortButtonProps<K extends string> {
1745
1754
  onSort: (key: K) => void;
1746
1755
  /** True when there are no rows to order — hides the arrow, drops the click. */
1747
1756
  disabled?: boolean;
1757
+ /**
1758
+ * False renders the label alone, as plain text with no button, for a column
1759
+ * that can never be sorted. Unlike `disabled`, nothing about it is a control.
1760
+ */
1761
+ sortable?: boolean;
1762
+ /** Matches the column's alignment, so a label that wraps lines up with the cells under it. */
1763
+ align?: "left" | "center" | "right";
1764
+ /** Optional prefix for the button's `data-testid` (`${testIdPrefix}-sort-${sortKey}`). Omit for no test-id wiring. */
1765
+ testIdPrefix?: string;
1748
1766
  }
1749
1767
  /**
1750
1768
  * @pattern SortButton
@@ -1759,16 +1777,25 @@ interface SortButtonProps<K extends string> {
1759
1777
  * standard three-state cycle — sort, reverse, then unsorted. `disabled` keeps
1760
1778
  * the column's inherited header color rather than dimming it — the column
1761
1779
  * isn't unavailable, there's just no data under it.
1762
- * @a11y Renders a <button> and sets aria-sort on it. Intended for a table header
1763
- * cell -- put it inside the <th> rather than replacing it, so the sort
1764
- * state stays associated with the column for screen readers.
1780
+ * `sortable={false}` renders just the label, for a column that is never
1781
+ * sortable; `align` matches the column's alignment.
1782
+ * @a11y Renders a <button> inside the <th>. The sort state is announced from
1783
+ * the column header, not the button: aria-sort is only valid on a
1784
+ * columnheader, so the caller sets it on the <th> with `ariaSortFor` (exported
1785
+ * alongside). It gives the active column "ascending"/"descending" and leaves
1786
+ * every other header without the attribute.
1765
1787
  * @usage
1766
1788
  * ```tsx
1767
1789
  * const [sort, setSort] = useState<SortState<'spend'>>(null)
1768
- * <SortButton label="Spend" sortKey="spend" sort={sortProp(sort)} onSort={(k) => setSort(cycleSort(sort, k))} />
1790
+ * <th aria-sort={ariaSortFor(sort, 'spend')}>
1791
+ * <SortButton label="Spend" sortKey="spend" sort={sortProp(sort)} onSort={(k) => setSort(cycleSort(sort, k))} />
1792
+ * </th>
1793
+ * <th className="text-right" aria-sort={ariaSortFor(sort, 'units')}>
1794
+ * <SortButton label="Units" sortKey="units" align="right" sort={sortProp(sort)} onSort={onSort} />
1795
+ * </th>
1769
1796
  * ```
1770
1797
  */
1771
- declare function SortButton<K extends string>({ label, sortKey, sort, onSort, disabled }: SortButtonProps<K>): react.JSX.Element;
1798
+ declare function SortButton<K extends string>({ label, sortKey, sort, onSort, disabled, sortable, align, testIdPrefix, }: SortButtonProps<K>): react.JSX.Element;
1772
1799
 
1773
1800
  interface PagerProps {
1774
1801
  /** The current page, 1-indexed. */
@@ -1883,7 +1910,7 @@ declare function Tag({ children, className }: TagProps): react.JSX.Element;
1883
1910
  * />
1884
1911
  * ```
1885
1912
  */
1886
- declare function TablePagination({ page, perPage, totalItems, onPageChange, onPerPageChange, perPageOptions, itemLabel, className, }: {
1913
+ declare function TablePagination({ page, perPage, totalItems, onPageChange, onPerPageChange, perPageOptions, itemLabel, className, testIdPrefix, }: {
1887
1914
  page: number;
1888
1915
  perPage: number;
1889
1916
  totalItems: number;
@@ -1892,6 +1919,13 @@ declare function TablePagination({ page, perPage, totalItems, onPageChange, onPe
1892
1919
  perPageOptions?: number[];
1893
1920
  itemLabel?: string;
1894
1921
  className?: string;
1922
+ /**
1923
+ * Optional prefix for `data-testid` attributes on the bar (`testIdPrefix`
1924
+ * itself), the per-page select (`-per-page`), the step buttons (`-first`,
1925
+ * `-prev`, `-next`, `-last`) and each page button (`-page-<n>`). Omit for no
1926
+ * test-id wiring.
1927
+ */
1928
+ testIdPrefix?: string;
1895
1929
  }): react.JSX.Element;
1896
1930
  type TablePaginationProps = Parameters<typeof TablePagination>[0];
1897
1931
 
@@ -2041,6 +2075,8 @@ interface ToggleProps {
2041
2075
  disabled?: boolean;
2042
2076
  /** Extra classes merged onto the track (the outer `<button>`). */
2043
2077
  className?: string;
2078
+ /** Optional prefix for the switch's `data-testid` (`${testIdPrefix}-toggle`). Omit for no test-id wiring. */
2079
+ testIdPrefix?: string;
2044
2080
  }
2045
2081
  /**
2046
2082
  * @pattern Toggle
@@ -2069,7 +2105,7 @@ interface ToggleProps {
2069
2105
  * <Toggle size="sm" checked={enabled} onChange={setEnabled} label="Only include enriched creatives" />
2070
2106
  * ```
2071
2107
  */
2072
- declare function Toggle({ checked, onChange, label, size, disabled, className }: ToggleProps): react.JSX.Element;
2108
+ declare function Toggle({ checked, onChange, label, size, disabled, className, testIdPrefix }: ToggleProps): react.JSX.Element;
2073
2109
 
2074
2110
  interface AISparkProps {
2075
2111
  size?: number;
@@ -2108,4 +2144,4 @@ interface FreshnessProps {
2108
2144
  */
2109
2145
  declare function Freshness({ level }: FreshnessProps): react.JSX.Element;
2110
2146
 
2111
- export { AISpark, type AISparkProps, AdvertiserNoResults, type AdvertiserNoResultsProps, AdvertiserQuickFilter, type AdvertiserQuickFilterProps, AdvertiserSearchPrompt, type AdvertiserSearchPromptProps, Badge, type BadgeKind, type BadgeProps, type BadgeTone, BarChartPlaceholder, type BarChartPlaceholderProps, Button, type ButtonProps, Card, type CardProps, CheckBox, CheckBoxBox, type CheckBoxBoxProps, type CheckBoxProps, ChipContent, type ChipContentProps, type ChipVariant, DEFAULT_SEARCH_MATCH, DataScopeFilter, type DataScopeFilterProps, type DateGranularity, DateRangeFilter, type DateRangeFilterProps, DateRangePicker, type DateRangePickerProps, Delta, type DeltaProps, FilterChipBar, type FilterChipBarProps, FilterDropdownFooter, type FilterDropdownFooterProps, Freshness, type FreshnessLevel, type FreshnessProps, InfoTip, type InfoTipProps, LineChartPlaceholder, type LineChartPlaceholderProps, MultiSelectFilter, type MultiSelectFilterProps, type MultiSelectGroup, NO_RESULTS_BODY, NO_RESULTS_TITLE, NameListModal, type NameListModalProps, type NoResultsAction, NoResultsEmptyState, type NoResultsEmptyStateProps, PRIMARY_ACTION_CLASS, Pager, type PagerProps, type PickerColumn, type PickerFilter, type PickerRow, PieChartPlaceholder, type PieChartPlaceholderProps, type PubBreak, PublisherBreakdownList, type PublisherBreakdownListProps, QUIET_ACTION_CLASS, SearchBar, type SearchBarProps, type SearchBarVariant, type SearchMatchMode, type SearchMatchValue, SectionRail, type SectionRailProps, SelectAllVisible, type SelectAllVisibleProps, SelectFilter, type SelectFilterProps, SelectedSection, type SelectedSectionProps, SelectionPicker, type SelectionPickerAccountList, type SelectionPickerProps, SortButton, type SortButtonProps, type SortDir, type SortState, StackedColumnPlaceholder, type StackedColumnPlaceholderProps, type SummaryGroup, type SummaryGroupItemValue, TablePagination, type TablePaginationProps, Tag, type TagProps, Toggle, type ToggleProps, Tooltip, type TooltipProps, WorkspaceAvatar, type WorkspaceAvatarProps, WorkspaceSelectedCard, type WorkspaceSelectedCardProps, buttonVariants, chipTriggerClass, cycleSort, noResultsBody, resolveRange, sortProp };
2147
+ export { AISpark, type AISparkProps, AdvertiserNoResults, type AdvertiserNoResultsProps, AdvertiserQuickFilter, type AdvertiserQuickFilterProps, AdvertiserSearchPrompt, type AdvertiserSearchPromptProps, Badge, type BadgeKind, type BadgeProps, type BadgeTone, BarChartPlaceholder, type BarChartPlaceholderProps, Button, type ButtonProps, Card, type CardProps, CheckBox, CheckBoxBox, type CheckBoxBoxProps, type CheckBoxProps, ChipContent, type ChipContentProps, type ChipVariant, DEFAULT_SEARCH_MATCH, DataScopeFilter, type DataScopeFilterProps, type DateGranularity, DateRangeFilter, type DateRangeFilterProps, DateRangePicker, type DateRangePickerProps, Delta, type DeltaProps, FilterChipBar, type FilterChipBarProps, FilterDropdownFooter, type FilterDropdownFooterProps, Freshness, type FreshnessLevel, type FreshnessProps, InfoTip, type InfoTipProps, LineChartPlaceholder, type LineChartPlaceholderProps, MultiSelectFilter, type MultiSelectFilterProps, type MultiSelectGroup, NO_RESULTS_BODY, NO_RESULTS_TITLE, NameListModal, type NameListModalProps, type NoResultsAction, NoResultsEmptyState, type NoResultsEmptyStateProps, PRIMARY_ACTION_CLASS, Pager, type PagerProps, type PickerColumn, type PickerFilter, type PickerRow, PieChartPlaceholder, type PieChartPlaceholderProps, type PubBreak, PublisherBreakdownList, type PublisherBreakdownListProps, QUIET_ACTION_CLASS, SearchBar, type SearchBarProps, type SearchBarVariant, type SearchMatchMode, type SearchMatchValue, SectionRail, type SectionRailProps, SelectAllVisible, type SelectAllVisibleProps, SelectFilter, type SelectFilterProps, SelectedSection, type SelectedSectionProps, SelectionPicker, type SelectionPickerAccountList, type SelectionPickerProps, SortButton, type SortButtonProps, type SortDir, type SortState, StackedColumnPlaceholder, type StackedColumnPlaceholderProps, type SummaryGroup, type SummaryGroupItemValue, TablePagination, type TablePaginationProps, Tag, type TagProps, Toggle, type ToggleProps, Tooltip, type TooltipProps, WorkspaceAvatar, type WorkspaceAvatarProps, WorkspaceSelectedCard, type WorkspaceSelectedCardProps, ariaSortFor, buttonVariants, chipTriggerClass, cycleSort, noResultsBody, resolveRange, sortProp };
package/dist/index.d.ts CHANGED
@@ -1733,6 +1733,15 @@ declare function sortProp<K extends string>(sort: SortState<K>): {
1733
1733
  key: K | null;
1734
1734
  dir: SortDir;
1735
1735
  };
1736
+ /**
1737
+ * The `aria-sort` value for column `key`'s <th>: "ascending"/"descending" on
1738
+ * the active column, nothing on the rest. aria-sort belongs on the column
1739
+ * header, not on the SortButton inside it, and only the sorted column carries it.
1740
+ */
1741
+ declare function ariaSortFor<K extends string>(sort: SortState<K> | {
1742
+ key: K | null;
1743
+ dir: SortDir;
1744
+ }, key: K): "ascending" | "descending" | undefined;
1736
1745
 
1737
1746
  interface SortButtonProps<K extends string> {
1738
1747
  label: string;
@@ -1745,6 +1754,15 @@ interface SortButtonProps<K extends string> {
1745
1754
  onSort: (key: K) => void;
1746
1755
  /** True when there are no rows to order — hides the arrow, drops the click. */
1747
1756
  disabled?: boolean;
1757
+ /**
1758
+ * False renders the label alone, as plain text with no button, for a column
1759
+ * that can never be sorted. Unlike `disabled`, nothing about it is a control.
1760
+ */
1761
+ sortable?: boolean;
1762
+ /** Matches the column's alignment, so a label that wraps lines up with the cells under it. */
1763
+ align?: "left" | "center" | "right";
1764
+ /** Optional prefix for the button's `data-testid` (`${testIdPrefix}-sort-${sortKey}`). Omit for no test-id wiring. */
1765
+ testIdPrefix?: string;
1748
1766
  }
1749
1767
  /**
1750
1768
  * @pattern SortButton
@@ -1759,16 +1777,25 @@ interface SortButtonProps<K extends string> {
1759
1777
  * standard three-state cycle — sort, reverse, then unsorted. `disabled` keeps
1760
1778
  * the column's inherited header color rather than dimming it — the column
1761
1779
  * isn't unavailable, there's just no data under it.
1762
- * @a11y Renders a <button> and sets aria-sort on it. Intended for a table header
1763
- * cell -- put it inside the <th> rather than replacing it, so the sort
1764
- * state stays associated with the column for screen readers.
1780
+ * `sortable={false}` renders just the label, for a column that is never
1781
+ * sortable; `align` matches the column's alignment.
1782
+ * @a11y Renders a <button> inside the <th>. The sort state is announced from
1783
+ * the column header, not the button: aria-sort is only valid on a
1784
+ * columnheader, so the caller sets it on the <th> with `ariaSortFor` (exported
1785
+ * alongside). It gives the active column "ascending"/"descending" and leaves
1786
+ * every other header without the attribute.
1765
1787
  * @usage
1766
1788
  * ```tsx
1767
1789
  * const [sort, setSort] = useState<SortState<'spend'>>(null)
1768
- * <SortButton label="Spend" sortKey="spend" sort={sortProp(sort)} onSort={(k) => setSort(cycleSort(sort, k))} />
1790
+ * <th aria-sort={ariaSortFor(sort, 'spend')}>
1791
+ * <SortButton label="Spend" sortKey="spend" sort={sortProp(sort)} onSort={(k) => setSort(cycleSort(sort, k))} />
1792
+ * </th>
1793
+ * <th className="text-right" aria-sort={ariaSortFor(sort, 'units')}>
1794
+ * <SortButton label="Units" sortKey="units" align="right" sort={sortProp(sort)} onSort={onSort} />
1795
+ * </th>
1769
1796
  * ```
1770
1797
  */
1771
- declare function SortButton<K extends string>({ label, sortKey, sort, onSort, disabled }: SortButtonProps<K>): react.JSX.Element;
1798
+ declare function SortButton<K extends string>({ label, sortKey, sort, onSort, disabled, sortable, align, testIdPrefix, }: SortButtonProps<K>): react.JSX.Element;
1772
1799
 
1773
1800
  interface PagerProps {
1774
1801
  /** The current page, 1-indexed. */
@@ -1883,7 +1910,7 @@ declare function Tag({ children, className }: TagProps): react.JSX.Element;
1883
1910
  * />
1884
1911
  * ```
1885
1912
  */
1886
- declare function TablePagination({ page, perPage, totalItems, onPageChange, onPerPageChange, perPageOptions, itemLabel, className, }: {
1913
+ declare function TablePagination({ page, perPage, totalItems, onPageChange, onPerPageChange, perPageOptions, itemLabel, className, testIdPrefix, }: {
1887
1914
  page: number;
1888
1915
  perPage: number;
1889
1916
  totalItems: number;
@@ -1892,6 +1919,13 @@ declare function TablePagination({ page, perPage, totalItems, onPageChange, onPe
1892
1919
  perPageOptions?: number[];
1893
1920
  itemLabel?: string;
1894
1921
  className?: string;
1922
+ /**
1923
+ * Optional prefix for `data-testid` attributes on the bar (`testIdPrefix`
1924
+ * itself), the per-page select (`-per-page`), the step buttons (`-first`,
1925
+ * `-prev`, `-next`, `-last`) and each page button (`-page-<n>`). Omit for no
1926
+ * test-id wiring.
1927
+ */
1928
+ testIdPrefix?: string;
1895
1929
  }): react.JSX.Element;
1896
1930
  type TablePaginationProps = Parameters<typeof TablePagination>[0];
1897
1931
 
@@ -2041,6 +2075,8 @@ interface ToggleProps {
2041
2075
  disabled?: boolean;
2042
2076
  /** Extra classes merged onto the track (the outer `<button>`). */
2043
2077
  className?: string;
2078
+ /** Optional prefix for the switch's `data-testid` (`${testIdPrefix}-toggle`). Omit for no test-id wiring. */
2079
+ testIdPrefix?: string;
2044
2080
  }
2045
2081
  /**
2046
2082
  * @pattern Toggle
@@ -2069,7 +2105,7 @@ interface ToggleProps {
2069
2105
  * <Toggle size="sm" checked={enabled} onChange={setEnabled} label="Only include enriched creatives" />
2070
2106
  * ```
2071
2107
  */
2072
- declare function Toggle({ checked, onChange, label, size, disabled, className }: ToggleProps): react.JSX.Element;
2108
+ declare function Toggle({ checked, onChange, label, size, disabled, className, testIdPrefix }: ToggleProps): react.JSX.Element;
2073
2109
 
2074
2110
  interface AISparkProps {
2075
2111
  size?: number;
@@ -2108,4 +2144,4 @@ interface FreshnessProps {
2108
2144
  */
2109
2145
  declare function Freshness({ level }: FreshnessProps): react.JSX.Element;
2110
2146
 
2111
- export { AISpark, type AISparkProps, AdvertiserNoResults, type AdvertiserNoResultsProps, AdvertiserQuickFilter, type AdvertiserQuickFilterProps, AdvertiserSearchPrompt, type AdvertiserSearchPromptProps, Badge, type BadgeKind, type BadgeProps, type BadgeTone, BarChartPlaceholder, type BarChartPlaceholderProps, Button, type ButtonProps, Card, type CardProps, CheckBox, CheckBoxBox, type CheckBoxBoxProps, type CheckBoxProps, ChipContent, type ChipContentProps, type ChipVariant, DEFAULT_SEARCH_MATCH, DataScopeFilter, type DataScopeFilterProps, type DateGranularity, DateRangeFilter, type DateRangeFilterProps, DateRangePicker, type DateRangePickerProps, Delta, type DeltaProps, FilterChipBar, type FilterChipBarProps, FilterDropdownFooter, type FilterDropdownFooterProps, Freshness, type FreshnessLevel, type FreshnessProps, InfoTip, type InfoTipProps, LineChartPlaceholder, type LineChartPlaceholderProps, MultiSelectFilter, type MultiSelectFilterProps, type MultiSelectGroup, NO_RESULTS_BODY, NO_RESULTS_TITLE, NameListModal, type NameListModalProps, type NoResultsAction, NoResultsEmptyState, type NoResultsEmptyStateProps, PRIMARY_ACTION_CLASS, Pager, type PagerProps, type PickerColumn, type PickerFilter, type PickerRow, PieChartPlaceholder, type PieChartPlaceholderProps, type PubBreak, PublisherBreakdownList, type PublisherBreakdownListProps, QUIET_ACTION_CLASS, SearchBar, type SearchBarProps, type SearchBarVariant, type SearchMatchMode, type SearchMatchValue, SectionRail, type SectionRailProps, SelectAllVisible, type SelectAllVisibleProps, SelectFilter, type SelectFilterProps, SelectedSection, type SelectedSectionProps, SelectionPicker, type SelectionPickerAccountList, type SelectionPickerProps, SortButton, type SortButtonProps, type SortDir, type SortState, StackedColumnPlaceholder, type StackedColumnPlaceholderProps, type SummaryGroup, type SummaryGroupItemValue, TablePagination, type TablePaginationProps, Tag, type TagProps, Toggle, type ToggleProps, Tooltip, type TooltipProps, WorkspaceAvatar, type WorkspaceAvatarProps, WorkspaceSelectedCard, type WorkspaceSelectedCardProps, buttonVariants, chipTriggerClass, cycleSort, noResultsBody, resolveRange, sortProp };
2147
+ export { AISpark, type AISparkProps, AdvertiserNoResults, type AdvertiserNoResultsProps, AdvertiserQuickFilter, type AdvertiserQuickFilterProps, AdvertiserSearchPrompt, type AdvertiserSearchPromptProps, Badge, type BadgeKind, type BadgeProps, type BadgeTone, BarChartPlaceholder, type BarChartPlaceholderProps, Button, type ButtonProps, Card, type CardProps, CheckBox, CheckBoxBox, type CheckBoxBoxProps, type CheckBoxProps, ChipContent, type ChipContentProps, type ChipVariant, DEFAULT_SEARCH_MATCH, DataScopeFilter, type DataScopeFilterProps, type DateGranularity, DateRangeFilter, type DateRangeFilterProps, DateRangePicker, type DateRangePickerProps, Delta, type DeltaProps, FilterChipBar, type FilterChipBarProps, FilterDropdownFooter, type FilterDropdownFooterProps, Freshness, type FreshnessLevel, type FreshnessProps, InfoTip, type InfoTipProps, LineChartPlaceholder, type LineChartPlaceholderProps, MultiSelectFilter, type MultiSelectFilterProps, type MultiSelectGroup, NO_RESULTS_BODY, NO_RESULTS_TITLE, NameListModal, type NameListModalProps, type NoResultsAction, NoResultsEmptyState, type NoResultsEmptyStateProps, PRIMARY_ACTION_CLASS, Pager, type PagerProps, type PickerColumn, type PickerFilter, type PickerRow, PieChartPlaceholder, type PieChartPlaceholderProps, type PubBreak, PublisherBreakdownList, type PublisherBreakdownListProps, QUIET_ACTION_CLASS, SearchBar, type SearchBarProps, type SearchBarVariant, type SearchMatchMode, type SearchMatchValue, SectionRail, type SectionRailProps, SelectAllVisible, type SelectAllVisibleProps, SelectFilter, type SelectFilterProps, SelectedSection, type SelectedSectionProps, SelectionPicker, type SelectionPickerAccountList, type SelectionPickerProps, SortButton, type SortButtonProps, type SortDir, type SortState, StackedColumnPlaceholder, type StackedColumnPlaceholderProps, type SummaryGroup, type SummaryGroupItemValue, TablePagination, type TablePaginationProps, Tag, type TagProps, Toggle, type ToggleProps, Tooltip, type TooltipProps, WorkspaceAvatar, type WorkspaceAvatarProps, WorkspaceSelectedCard, type WorkspaceSelectedCardProps, ariaSortFor, buttonVariants, chipTriggerClass, cycleSort, noResultsBody, resolveRange, sortProp };
package/dist/index.js CHANGED
@@ -3023,7 +3023,22 @@ function Chip({
3023
3023
  // src/SortButton.tsx
3024
3024
  import { ArrowUp, ArrowDown, ArrowUpDown } from "lucide-react";
3025
3025
  import { jsx as jsx31, jsxs as jsxs29 } from "react/jsx-runtime";
3026
- function SortButton({ label, sortKey, sort, onSort, disabled = false }) {
3026
+ var ALIGN = {
3027
+ left: "justify-start text-left",
3028
+ center: "justify-center text-center",
3029
+ right: "justify-end text-right"
3030
+ };
3031
+ function SortButton({
3032
+ label,
3033
+ sortKey,
3034
+ sort,
3035
+ onSort,
3036
+ disabled = false,
3037
+ sortable = true,
3038
+ align = "left",
3039
+ testIdPrefix
3040
+ }) {
3041
+ if (!sortable) return /* @__PURE__ */ jsx31("span", { className: cn("inline-flex", ALIGN[align]), children: label });
3027
3042
  const active = !disabled && sort.key === sortKey;
3028
3043
  return /* @__PURE__ */ jsxs29(
3029
3044
  "button",
@@ -3031,9 +3046,9 @@ function SortButton({ label, sortKey, sort, onSort, disabled = false }) {
3031
3046
  type: "button",
3032
3047
  disabled,
3033
3048
  onClick: () => onSort(sortKey),
3034
- "aria-sort": active ? sort.dir === "asc" ? "ascending" : "descending" : "none",
3049
+ "data-testid": testIdPrefix && `${testIdPrefix}-sort-${sortKey}`,
3035
3050
  className: cn(
3036
- // text-left overrides the UA default on <button>, which would centre a
3051
+ // ALIGN's text-* overrides the UA default on <button>, which would centre a
3037
3052
  // label that wraps onto two lines, out of step with the column under it.
3038
3053
  // `uppercase` is NOT this control imposing a treatment -- it is
3039
3054
  // restoring one. Every table header row sets uppercase on the <tr>,
@@ -3048,15 +3063,17 @@ function SortButton({ label, sortKey, sort, onSort, disabled = false }) {
3048
3063
  // subtly tighter than the fixed headers next to them. A control
3049
3064
  // should carry exactly the properties that do not inherit, and
3050
3065
  // nothing more.
3051
- "group inline-flex items-center gap-1 text-left uppercase transition-colors",
3066
+ "group inline-flex items-center gap-1 uppercase transition-colors",
3067
+ ALIGN[align],
3052
3068
  !disabled && "hover:text-text-primary"
3053
3069
  ),
3054
3070
  children: [
3055
3071
  label,
3056
- !disabled && (active ? sort.dir === "asc" ? /* @__PURE__ */ jsx31(ArrowUp, { size: 12, className: "shrink-0 text-brand-primaryAction" }) : /* @__PURE__ */ jsx31(ArrowDown, { size: 12, className: "shrink-0 text-brand-primaryAction" }) : /* @__PURE__ */ jsx31(
3072
+ !disabled && (active ? sort.dir === "asc" ? /* @__PURE__ */ jsx31(ArrowUp, { size: 12, "aria-hidden": "true", className: "shrink-0 text-brand-primaryAction" }) : /* @__PURE__ */ jsx31(ArrowDown, { size: 12, "aria-hidden": "true", className: "shrink-0 text-brand-primaryAction" }) : /* @__PURE__ */ jsx31(
3057
3073
  ArrowUpDown,
3058
3074
  {
3059
3075
  size: 12,
3076
+ "aria-hidden": "true",
3060
3077
  className: "shrink-0 text-text-tertiary opacity-50 transition-opacity group-hover:opacity-100"
3061
3078
  }
3062
3079
  ))
@@ -3074,6 +3091,10 @@ function cycleSort(current, key, firstDir = "desc") {
3074
3091
  function sortProp(sort) {
3075
3092
  return sort ?? { key: null, dir: "desc" };
3076
3093
  }
3094
+ function ariaSortFor(sort, key) {
3095
+ if (!sort || sort.key !== key) return void 0;
3096
+ return sort.dir === "asc" ? "ascending" : "descending";
3097
+ }
3077
3098
 
3078
3099
  // src/Pager.tsx
3079
3100
  import { ChevronLeft as ChevronLeft3, ChevronRight as ChevronRight4 } from "lucide-react";
@@ -3197,7 +3218,8 @@ function TablePagination({
3197
3218
  onPerPageChange,
3198
3219
  perPageOptions = [25, 50, 100],
3199
3220
  itemLabel = "results",
3200
- className
3221
+ className,
3222
+ testIdPrefix
3201
3223
  }) {
3202
3224
  const totalPages = Math.max(1, Math.ceil(totalItems / perPage));
3203
3225
  const current = Math.min(page, totalPages);
@@ -3208,7 +3230,7 @@ function TablePagination({
3208
3230
  btn,
3209
3231
  "text-text-secondary hover:bg-subtle hover:text-text-primary disabled:pointer-events-none disabled:opacity-30"
3210
3232
  );
3211
- return /* @__PURE__ */ jsxs31("div", { className: cn("flex flex-wrap items-center justify-between gap-x-4 gap-y-2", className), children: [
3233
+ return /* @__PURE__ */ jsxs31("div", { className: cn("flex flex-wrap items-center justify-between gap-x-4 gap-y-2", className), "data-testid": testIdPrefix, children: [
3212
3234
  /* @__PURE__ */ jsxs31("label", { className: "flex items-center gap-2 text-[13px] text-text-secondary", children: [
3213
3235
  /* @__PURE__ */ jsxs31("span", { className: "relative", children: [
3214
3236
  /* @__PURE__ */ jsx35(
@@ -3217,6 +3239,7 @@ function TablePagination({
3217
3239
  value: perPage,
3218
3240
  onChange: (e) => onPerPageChange(Number(e.target.value)),
3219
3241
  "aria-label": "Items per page",
3242
+ "data-testid": testIdPrefix && `${testIdPrefix}-per-page`,
3220
3243
  className: "appearance-none rounded-control border border-border-default bg-input py-1 pl-2.5 pr-7 text-[13px] font-medium text-text-primary focus:outline focus:outline-2 focus:outline-border-focus",
3221
3244
  children: perPageOptions.map((n) => /* @__PURE__ */ jsx35("option", { value: n, children: n }, n))
3222
3245
  }
@@ -3240,6 +3263,7 @@ function TablePagination({
3240
3263
  onClick: () => onPageChange(1),
3241
3264
  disabled: current === 1,
3242
3265
  "aria-label": "First page",
3266
+ "data-testid": testIdPrefix && `${testIdPrefix}-first`,
3243
3267
  className: stepBtn,
3244
3268
  children: /* @__PURE__ */ jsx35(ChevronFirst, { size: 16, "aria-hidden": "true" })
3245
3269
  }
@@ -3251,6 +3275,7 @@ function TablePagination({
3251
3275
  onClick: () => onPageChange(current - 1),
3252
3276
  disabled: current === 1,
3253
3277
  "aria-label": "Previous page",
3278
+ "data-testid": testIdPrefix && `${testIdPrefix}-prev`,
3254
3279
  className: stepBtn,
3255
3280
  children: /* @__PURE__ */ jsx35(ChevronLeft4, { size: 16, "aria-hidden": "true" })
3256
3281
  }
@@ -3260,6 +3285,7 @@ function TablePagination({
3260
3285
  "button",
3261
3286
  {
3262
3287
  type: "button",
3288
+ "data-testid": testIdPrefix && `${testIdPrefix}-page-${p}`,
3263
3289
  onClick: () => onPageChange(p),
3264
3290
  "aria-current": p === current ? "page" : void 0,
3265
3291
  className: cn(
@@ -3278,6 +3304,7 @@ function TablePagination({
3278
3304
  onClick: () => onPageChange(current + 1),
3279
3305
  disabled: current === totalPages,
3280
3306
  "aria-label": "Next page",
3307
+ "data-testid": testIdPrefix && `${testIdPrefix}-next`,
3281
3308
  className: stepBtn,
3282
3309
  children: /* @__PURE__ */ jsx35(ChevronRight5, { size: 16, "aria-hidden": "true" })
3283
3310
  }
@@ -3289,6 +3316,7 @@ function TablePagination({
3289
3316
  onClick: () => onPageChange(totalPages),
3290
3317
  disabled: current === totalPages,
3291
3318
  "aria-label": "Last page",
3319
+ "data-testid": testIdPrefix && `${testIdPrefix}-last`,
3292
3320
  className: stepBtn,
3293
3321
  children: /* @__PURE__ */ jsx35(ChevronLast, { size: 16, "aria-hidden": "true" })
3294
3322
  }
@@ -3730,7 +3758,7 @@ var SIZES2 = {
3730
3758
  sm: { track: "h-5 w-9", knob: "h-4 w-4", on: "translate-x-[18px]" },
3731
3759
  md: { track: "h-6 w-11", knob: "h-5 w-5", on: "translate-x-[22px]" }
3732
3760
  };
3733
- function Toggle({ checked, onChange, label, size = "md", disabled, className }) {
3761
+ function Toggle({ checked, onChange, label, size = "md", disabled, className, testIdPrefix }) {
3734
3762
  const s = SIZES2[size];
3735
3763
  return /* @__PURE__ */ jsx38(
3736
3764
  "button",
@@ -3740,6 +3768,7 @@ function Toggle({ checked, onChange, label, size = "md", disabled, className })
3740
3768
  "aria-checked": checked,
3741
3769
  "aria-label": label,
3742
3770
  disabled,
3771
+ "data-testid": testIdPrefix && `${testIdPrefix}-toggle`,
3743
3772
  onClick: () => onChange(!checked),
3744
3773
  className: cn(
3745
3774
  "relative inline-flex shrink-0 items-center rounded-full transition-colors focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
@@ -3845,6 +3874,7 @@ export {
3845
3874
  Tooltip,
3846
3875
  WorkspaceAvatar,
3847
3876
  WorkspaceSelectedCard,
3877
+ ariaSortFor,
3848
3878
  buttonVariants,
3849
3879
  chipTriggerClass,
3850
3880
  cycleSort,