@common-origin/design-system 2.16.0 → 2.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/client.js CHANGED
@@ -742,7 +742,11 @@ var Icon = function Icon(_a) {
742
742
  size = _b === void 0 ? 'lg' : _b,
743
743
  _c = _a.iconColor,
744
744
  iconColor = _c === void 0 ? 'default' : _c,
745
+ ariaLabel = _a["aria-label"],
746
+ title = _a.title,
745
747
  dataTestId = _a["data-testid"];
748
+ // Decorative unless it's given a name. A blank name is no name, so the icon stays hidden
749
+ var label = (ariaLabel === null || ariaLabel === void 0 ? void 0 : ariaLabel.trim()) || (title === null || title === void 0 ? void 0 : title.trim()) || undefined;
746
750
  // Get the icon data from the JSON file
747
751
  var iconData = data.iconsJson[name];
748
752
  if (!iconData) {
@@ -757,16 +761,23 @@ var Icon = function Icon(_a) {
757
761
  "$size": size,
758
762
  "$iconColor": iconColor,
759
763
  "data-testid": dataTestId,
760
- children: jsxRuntime.jsx("svg", {
764
+ children: jsxRuntime.jsxs("svg", __assign({
761
765
  viewBox: "0 0 24 24",
762
766
  fill: "currentColor",
763
- xmlns: "http://www.w3.org/2000/svg",
764
- role: "img",
765
- "aria-label": iconData.name,
766
- children: jsxRuntime.jsx("path", {
767
+ xmlns: "http://www.w3.org/2000/svg"
768
+ }, label ? {
769
+ role: 'img',
770
+ 'aria-label': label
771
+ } : {
772
+ 'aria-hidden': true,
773
+ focusable: false
774
+ }, {
775
+ children: [(title === null || title === void 0 ? void 0 : title.trim()) && jsxRuntime.jsx("title", {
776
+ children: title
777
+ }), jsxRuntime.jsx("path", {
767
778
  d: iconData.path
768
- })
769
- })
779
+ })]
780
+ }))
770
781
  });
771
782
  };
772
783
  var templateObject_1$H;
@@ -1098,8 +1109,7 @@ var CategoryLabel = function CategoryLabel(_a) {
1098
1109
  children: [icon && jsxRuntime.jsx(Icon, {
1099
1110
  name: icon,
1100
1111
  size: iconSize,
1101
- iconColor: variant === 'filled' ? 'inverse' : 'inherit',
1102
- "aria-hidden": "true"
1112
+ iconColor: variant === 'filled' ? 'inverse' : 'inherit'
1103
1113
  }), children]
1104
1114
  }), accessibleLabel && jsxRuntime.jsx(HiddenLabel, {
1105
1115
  children: accessibleLabel
@@ -1544,12 +1554,9 @@ var DateFormatter = function DateFormatter(_a) {
1544
1554
  var date = dateFns.parseISO(dateString);
1545
1555
  var displayText;
1546
1556
  switch (mode) {
1547
- case 'relative':
1548
- // Always use relative labels (Today/Yesterday) or fall back to day name
1549
- displayText = formatDateSmart(date, formatString);
1550
- break;
1557
+ case 'relative': // Deprecated alias for 'smart', removed in 3.0 (#82)
1551
1558
  case 'smart':
1552
- // Smart mode: relative for recent, absolute for older
1559
+ // Today, Yesterday or the day name for recent dates; formatted for older ones
1553
1560
  displayText = formatDateSmart(date, formatString);
1554
1561
  break;
1555
1562
  case 'absolute':
@@ -1791,14 +1798,18 @@ var MoneyDisplay = function MoneyDisplay(_a) {
1791
1798
  "$weight": weight,
1792
1799
  "aria-label": ariaLabel,
1793
1800
  "data-testid": dataTestId,
1794
- children: [showPositiveIcon && !showNegativeIcon && jsxRuntime.jsx(Icon, {
1801
+ children: [showPositiveIcon && !showNegativeIcon &&
1802
+ // The amount is shown without a sign, so the icon is the sign and needs a name (#85)
1803
+ jsxRuntime.jsx(Icon, {
1795
1804
  name: "addRing",
1796
1805
  size: iconSize,
1797
- iconColor: iconColor
1806
+ iconColor: iconColor,
1807
+ "aria-label": "Plus"
1798
1808
  }), showNegativeIcon && jsxRuntime.jsx(Icon, {
1799
1809
  name: "remove",
1800
1810
  size: iconSize,
1801
- iconColor: iconColor
1811
+ iconColor: iconColor,
1812
+ "aria-label": "Minus"
1802
1813
  }), jsxRuntime.jsx(Typography, {
1803
1814
  variant: typographyVariant,
1804
1815
  color: amountColor,
@@ -2200,8 +2211,7 @@ var StatusLabelBase = function StatusLabelBase(_a) {
2200
2211
  children: [showIcon && jsxRuntime.jsx(Icon, {
2201
2212
  name: statusConfig.icon,
2202
2213
  size: sizeStyles.iconSize,
2203
- iconColor: "inherit",
2204
- "aria-hidden": "true"
2214
+ iconColor: "inherit"
2205
2215
  }), displayLabel, jsxRuntime.jsxs(ScreenReaderOnly, {
2206
2216
  children: [status, " status"]
2207
2217
  })]
@@ -2528,6 +2538,12 @@ var AccountCard = function AccountCard(_a) {
2528
2538
  };
2529
2539
  var templateObject_1$t, templateObject_2$o, templateObject_3$m, templateObject_4$i, templateObject_5$f;
2530
2540
 
2541
+ /**
2542
+ * The spacing a List gives its items (#77). A ListItem without its own `spacing` uses it; outside
2543
+ * a List it's `comfortable`. Internal: not exported from the package.
2544
+ */
2545
+ var ListSpacingContext = /*#__PURE__*/React.createContext('comfortable');
2546
+
2531
2547
  var semantic$b = data.tokensJson.semantic;
2532
2548
  var _a$5 = semantic$b.motion,
2533
2549
  duration$2 = _a$5.duration,
@@ -2634,8 +2650,7 @@ var ListItem = function ListItem(_a) {
2634
2650
  selected = _f === void 0 ? false : _f,
2635
2651
  _g = _a.destructive,
2636
2652
  destructive = _g === void 0 ? false : _g,
2637
- _h = _a.spacing,
2638
- spacing = _h === void 0 ? 'comfortable' : _h,
2653
+ spacingProp = _a.spacing,
2639
2654
  children = _a.children,
2640
2655
  className = _a.className,
2641
2656
  dataTestId = _a["data-testid"],
@@ -2645,6 +2660,8 @@ var ListItem = function ListItem(_a) {
2645
2660
  customTabIndex = _a.tabIndex,
2646
2661
  customOnKeyDown = _a.onKeyDown,
2647
2662
  props = __rest(_a, ["primary", "secondary", "badge", "icon", "expandable", "expanded", "onToggle", "interactive", "onClick", "disabled", "selected", "destructive", "spacing", "children", "className", 'data-testid', "role", 'aria-selected', "id", "tabIndex", "onKeyDown"]);
2663
+ var listSpacing = React.useContext(ListSpacingContext);
2664
+ var spacing = spacingProp !== null && spacingProp !== void 0 ? spacingProp : listSpacing;
2648
2665
  var isInteractive = interactive || expandable || customRole === 'option';
2649
2666
  var contentRole = customRole === 'option' ? undefined : isInteractive ? 'button' : undefined;
2650
2667
  var ariaExpanded = expandable ? expanded : undefined;
@@ -2669,61 +2686,69 @@ var ListItem = function ListItem(_a) {
2669
2686
  handleClick();
2670
2687
  }
2671
2688
  };
2672
- return jsxRuntime.jsxs(StyledListItem, __assign({
2673
- "$interactive": isInteractive,
2674
- "$disabled": disabled,
2675
- "$selected": selected,
2676
- "$destructive": destructive,
2677
- "$spacing": spacing,
2678
- className: className,
2679
- "data-testid": dataTestId,
2680
- role: customRole || 'listitem',
2681
- id: id,
2682
- "aria-selected": ariaSelected
2683
- }, props, {
2684
- children: [jsxRuntime.jsxs(StyledItemContent, {
2685
- "$interactive": isInteractive,
2686
- "$disabled": disabled,
2687
- "$selected": selected,
2688
- "$destructive": destructive,
2689
- "$spacing": spacing,
2690
- role: contentRole,
2691
- "aria-expanded": ariaExpanded,
2692
- "aria-disabled": ariaDisabled,
2693
- "aria-current": ariaCurrent,
2694
- tabIndex: customTabIndex !== undefined ? customTabIndex : isInteractive && !disabled ? 0 : undefined,
2695
- onClick: handleClick,
2696
- onKeyDown: handleKeyDown,
2697
- children: [icon && jsxRuntime.jsx(StyledIconContainer$1, {
2698
- "aria-hidden": "true",
2699
- children: icon
2700
- }), jsxRuntime.jsxs(StyledTextContent, {
2701
- children: [jsxRuntime.jsx(Typography, {
2702
- variant: "body",
2703
- color: destructive ? 'error' : 'default',
2704
- children: primary
2705
- }), secondary && jsxRuntime.jsx(Typography, {
2706
- variant: "small",
2707
- color: "subdued",
2708
- children: secondary
2709
- })]
2710
- }), jsxRuntime.jsxs(StyledRightContent, {
2711
- children: [badge, expandable && jsxRuntime.jsx(StyledChevronIcon, {
2689
+ return (
2690
+ // The List's spacing is for its own items: reset it for this item's content, so components
2691
+ // built on ListItem (ActionSheet, even through its portal) and expanded content keep their own
2692
+ // density. A nested List provides its own spacing (#77)
2693
+ jsxRuntime.jsx(ListSpacingContext.Provider, {
2694
+ value: "comfortable",
2695
+ children: jsxRuntime.jsxs(StyledListItem, __assign({
2696
+ "$interactive": isInteractive,
2697
+ "$disabled": disabled,
2698
+ "$selected": selected,
2699
+ "$destructive": destructive,
2700
+ "$spacing": spacing,
2701
+ className: className,
2702
+ "data-testid": dataTestId,
2703
+ role: customRole || 'listitem',
2704
+ id: id,
2705
+ "aria-selected": ariaSelected
2706
+ }, props, {
2707
+ children: [jsxRuntime.jsxs(StyledItemContent, {
2708
+ "$interactive": isInteractive,
2709
+ "$disabled": disabled,
2710
+ "$selected": selected,
2711
+ "$destructive": destructive,
2712
+ "$spacing": spacing,
2713
+ role: contentRole,
2714
+ "aria-expanded": ariaExpanded,
2715
+ "aria-disabled": ariaDisabled,
2716
+ "aria-current": ariaCurrent,
2717
+ tabIndex: customTabIndex !== undefined ? customTabIndex : isInteractive && !disabled ? 0 : undefined,
2718
+ onClick: handleClick,
2719
+ onKeyDown: handleKeyDown,
2720
+ children: [icon && jsxRuntime.jsx(StyledIconContainer$1, {
2721
+ "aria-hidden": "true",
2722
+ children: icon
2723
+ }), jsxRuntime.jsxs(StyledTextContent, {
2724
+ children: [jsxRuntime.jsx(Typography, {
2725
+ variant: "body",
2726
+ color: destructive ? 'error' : 'default',
2727
+ children: primary
2728
+ }), secondary && jsxRuntime.jsx(Typography, {
2729
+ variant: "small",
2730
+ color: "subdued",
2731
+ children: secondary
2732
+ })]
2733
+ }), jsxRuntime.jsxs(StyledRightContent, {
2734
+ children: [badge, expandable && jsxRuntime.jsx(StyledChevronIcon, {
2735
+ "$expanded": expanded,
2736
+ "aria-hidden": "true",
2737
+ children: jsxRuntime.jsx(Icon, {
2738
+ name: "caretDown",
2739
+ size: "sm"
2740
+ })
2741
+ })]
2742
+ })]
2743
+ }), expandable && children && jsxRuntime.jsx(StyledExpandedContent, {
2744
+ "$spacing": spacing,
2712
2745
  "$expanded": expanded,
2713
- "aria-hidden": "true",
2714
- children: jsxRuntime.jsx(Icon, {
2715
- name: "caretDown",
2716
- size: "sm"
2717
- })
2746
+ "aria-hidden": !expanded,
2747
+ children: children
2718
2748
  })]
2719
- })]
2720
- }), expandable && children && jsxRuntime.jsx(StyledExpandedContent, {
2721
- "$spacing": spacing,
2722
- "$expanded": expanded,
2723
- "aria-hidden": !expanded,
2724
- children: children
2725
- })]
2726
- }));
2749
+ }))
2750
+ })
2751
+ );
2727
2752
  };
2728
2753
  ListItem.displayName = 'ListItem';
2729
2754
  var templateObject_1$s, templateObject_2$n, templateObject_3$l, templateObject_4$h, templateObject_5$e, templateObject_6$b, templateObject_7$9;
@@ -4470,12 +4495,12 @@ var StyledCheckbox = styled__default.default.span.withConfig({
4470
4495
  return props.$checked || props.$indeterminate ? data.tokensJson.semantic.color.background.inverse : data.tokensJson.component.input["default"].backgroundColor;
4471
4496
  }, function (props) {
4472
4497
  if (props.$state === 'error') return data.tokensJson.component.input.error.borderColor;
4473
- if (props.$checked || props.$indeterminate) return data.tokensJson.semantic.border.strong;
4498
+ if (props.$checked || props.$indeterminate) return data.tokensJson.semantic.color.border.strong;
4474
4499
  return data.tokensJson.component.input["default"].borderColor;
4475
4500
  }, function (props) {
4476
4501
  return props.$state === 'disabled' ? 'not-allowed' : 'pointer';
4477
4502
  }, data.tokensJson.semantic.motion.duration.fast, data.tokensJson.semantic.motion.easing.easeOut, HiddenCheckboxInput, function (props) {
4478
- return props.$state === 'error' ? data.tokensJson.component.input.error.hover.borderColor : props.$checked || props.$indeterminate ? data.tokensJson.semantic.border.strong : data.tokensJson.component.input.hover.borderColor;
4503
+ return props.$state === 'error' ? data.tokensJson.component.input.error.hover.borderColor : props.$checked || props.$indeterminate ? data.tokensJson.semantic.color.border.strong : data.tokensJson.component.input.hover.borderColor;
4479
4504
  }, HiddenCheckboxInput, data.tokensJson.component.input.focus.outline, data.tokensJson.semantic.border.focusOffset, function (props) {
4480
4505
  return props.$state === 'error' ? data.tokensJson.component.input.error.focus.borderColor : data.tokensJson.component.input.focus.borderColor;
4481
4506
  }, function (props) {
@@ -5466,15 +5491,17 @@ var List = function List(_a) {
5466
5491
  className = _a.className,
5467
5492
  dataTestId = _a["data-testid"],
5468
5493
  props = __rest(_a, ["children", "dividers", "spacing", "className", 'data-testid']);
5469
- return jsxRuntime.jsx(StyledList, __assign({
5470
- "$dividers": dividers,
5471
- "$spacing": spacing,
5472
- className: className,
5473
- "data-testid": dataTestId,
5474
- role: "list"
5475
- }, props, {
5476
- children: children
5477
- }));
5494
+ return jsxRuntime.jsx(ListSpacingContext.Provider, {
5495
+ value: spacing,
5496
+ children: jsxRuntime.jsx(StyledList, __assign({
5497
+ "$dividers": dividers,
5498
+ className: className,
5499
+ "data-testid": dataTestId,
5500
+ role: "list"
5501
+ }, props, {
5502
+ children: children
5503
+ }))
5504
+ });
5478
5505
  };
5479
5506
  List.displayName = 'List';
5480
5507
  var templateObject_1$c;
@@ -6463,8 +6490,7 @@ var SearchField = /*#__PURE__*/React.forwardRef(function (_a, ref) {
6463
6490
  children: [jsxRuntime.jsx(Icon, {
6464
6491
  name: "search",
6465
6492
  size: "sm",
6466
- iconColor: "subdued",
6467
- "aria-hidden": "true"
6493
+ iconColor: "subdued"
6468
6494
  }), jsxRuntime.jsx(StyledInput, __assign({
6469
6495
  ref: inputRef,
6470
6496
  type: "text",
@@ -6530,8 +6556,7 @@ var SearchField = /*#__PURE__*/React.forwardRef(function (_a, ref) {
6530
6556
  secondary: description,
6531
6557
  icon: item.type === 'recent' ? jsxRuntime.jsx(Icon, {
6532
6558
  name: "refresh",
6533
- size: "xs",
6534
- "aria-hidden": "true"
6559
+ size: "xs"
6535
6560
  }) : undefined,
6536
6561
  interactive: true,
6537
6562
  selected: index === highlightedIndex,
@@ -7194,7 +7219,7 @@ var StyledTab = styled__default.default.button.withConfig({
7194
7219
  },
7195
7220
  displayName: "TabBar__StyledTab",
7196
7221
  componentId: "sc-1g9ib0y-1"
7197
- })(templateObject_2$3 || (templateObject_2$3 = __makeTemplateObject(["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n padding: ", " ", ";\n font: ", ";\n white-space: nowrap;\n cursor: ", ";\n border: none;\n background: transparent;\n transition: ", ";\n position: relative;\n \n /* Selected tab: the light-blue selected treatment plus a blue underline (decisions 0016, 0020).\n The text darkens with the fill on hover and press so every state stays at least 4.5:1. */\n border-radius: ", " ", " 0 0;\n background-color: ", ";\n color: ", ";\n\n &::after {\n content: '';\n position: absolute;\n bottom: -1px;\n left: 0;\n right: 0;\n height: ", ";\n background-color: ", ";\n transition: ", ";\n }\n\n &:hover:not(:disabled) {\n background-color: ", ";\n color: ", ";\n }\n\n &:active:not(:disabled) {\n background-color: ", ";\n color: ", ";\n }\n\n /* Disabled state */\n opacity: ", ";\n \n /* Focus state */\n &:focus-visible {\n outline: ", " solid ", ";\n outline-offset: ", ";\n z-index: 1;\n }\n \n /* Pressed nudge */\n &:active:not(:disabled) {\n transform: translateY(1px);\n }\n"], ["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n padding: ", " ", ";\n font: ", ";\n white-space: nowrap;\n cursor: ", ";\n border: none;\n background: transparent;\n transition: ", ";\n position: relative;\n \n /* Selected tab: the light-blue selected treatment plus a blue underline (decisions 0016, 0020).\n The text darkens with the fill on hover and press so every state stays at least 4.5:1. */\n border-radius: ", " ", " 0 0;\n background-color: ", ";\n color: ", ";\n\n &::after {\n content: '';\n position: absolute;\n bottom: -1px;\n left: 0;\n right: 0;\n height: ", ";\n background-color: ", ";\n transition: ", ";\n }\n\n &:hover:not(:disabled) {\n background-color: ", ";\n color: ", ";\n }\n\n &:active:not(:disabled) {\n background-color: ", ";\n color: ", ";\n }\n\n /* Disabled state */\n opacity: ", ";\n \n /* Focus state */\n &:focus-visible {\n outline: ", " solid ", ";\n outline-offset: ", ";\n z-index: 1;\n }\n \n /* Pressed nudge */\n &:active:not(:disabled) {\n transform: translateY(1px);\n }\n"])), semantic.spacing.layout.sm, semantic.spacing.layout.md, semantic.spacing.layout.lg, semantic.typography.button2, function (props) {
7222
+ })(templateObject_2$3 || (templateObject_2$3 = __makeTemplateObject(["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n padding: ", " ", ";\n font: ", ";\n white-space: nowrap;\n cursor: ", ";\n border: none;\n background: transparent;\n transition: ", ";\n position: relative;\n \n /* Selected tab: the light-blue selected treatment plus a blue underline (decisions 0016, 0020).\n The text darkens with the fill on hover and press so every state stays at least 4.5:1. */\n border-radius: ", " ", " 0 0;\n background-color: ", ";\n color: ", ";\n\n &::after {\n content: '';\n position: absolute;\n bottom: -1px;\n left: 0;\n right: 0;\n height: ", ";\n background-color: ", ";\n transition: ", ";\n }\n\n &:hover:not(:disabled) {\n background-color: ", ";\n color: ", ";\n }\n\n &:active:not(:disabled) {\n background-color: ", ";\n color: ", ";\n }\n\n /* Disabled state */\n opacity: ", ";\n \n /* Focus state */\n &:focus-visible {\n outline: ", " solid ", ";\n outline-offset: ", ";\n z-index: 1;\n }\n\n"], ["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n padding: ", " ", ";\n font: ", ";\n white-space: nowrap;\n cursor: ", ";\n border: none;\n background: transparent;\n transition: ", ";\n position: relative;\n \n /* Selected tab: the light-blue selected treatment plus a blue underline (decisions 0016, 0020).\n The text darkens with the fill on hover and press so every state stays at least 4.5:1. */\n border-radius: ", " ", " 0 0;\n background-color: ", ";\n color: ", ";\n\n &::after {\n content: '';\n position: absolute;\n bottom: -1px;\n left: 0;\n right: 0;\n height: ", ";\n background-color: ", ";\n transition: ", ";\n }\n\n &:hover:not(:disabled) {\n background-color: ", ";\n color: ", ";\n }\n\n &:active:not(:disabled) {\n background-color: ", ";\n color: ", ";\n }\n\n /* Disabled state */\n opacity: ", ";\n \n /* Focus state */\n &:focus-visible {\n outline: ", " solid ", ";\n outline-offset: ", ";\n z-index: 1;\n }\n\n"])), semantic.spacing.layout.sm, semantic.spacing.layout.md, semantic.spacing.layout.lg, semantic.typography.button2, function (props) {
7198
7223
  return props.$disabled ? 'not-allowed' : 'pointer';
7199
7224
  }, semantic.motion.hover, semantic.border.radius.sm, semantic.border.radius.sm, function (props) {
7200
7225
  return props.$isActive ? semantic.color.background['interactive-subtle'] : 'transparent';
@@ -7678,20 +7703,14 @@ var TransactionListItem = function TransactionListItem(_a) {
7678
7703
  })
7679
7704
  })]
7680
7705
  }), (hasReceipt || hasNote) && jsxRuntime.jsxs(StyledIconIndicator, {
7681
- children: [hasReceipt && jsxRuntime.jsx("span", {
7682
- "aria-label": "Has receipt",
7683
- role: "img",
7684
- children: jsxRuntime.jsx(Icon, {
7685
- name: "fileDocSearch",
7686
- size: "xs"
7687
- })
7688
- }), hasNote && jsxRuntime.jsx("span", {
7689
- "aria-label": "Has note",
7690
- role: "img",
7691
- children: jsxRuntime.jsx(Icon, {
7692
- name: "paper",
7693
- size: "xs"
7694
- })
7706
+ children: [hasReceipt && jsxRuntime.jsx(Icon, {
7707
+ name: "fileDocSearch",
7708
+ size: "xs",
7709
+ "aria-label": "Has receipt"
7710
+ }), hasNote && jsxRuntime.jsx(Icon, {
7711
+ name: "paper",
7712
+ size: "xs",
7713
+ "aria-label": "Has note"
7695
7714
  })]
7696
7715
  })]
7697
7716
  })]