@arc-ui/community-components 3.16.0 → 3.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.
Files changed (52) hide show
  1. package/.turbo/turbo-build.log +14 -14
  2. package/CHANGELOG.md +21 -0
  3. package/lib/ComparisonTable/ComparisonTable.cjs +317 -0
  4. package/lib/ComparisonTable/ComparisonTable.mjs +310 -0
  5. package/lib/ComparisonTable/styles.css +1 -0
  6. package/lib/FeaturePost/FeaturePost.cjs +12 -11
  7. package/lib/FeaturePost/FeaturePost.mjs +12 -11
  8. package/lib/FeaturePost/styles.css +1 -1
  9. package/lib/Highlights/Highlights.cjs +7 -7
  10. package/lib/Highlights/Highlights.mjs +7 -7
  11. package/lib/ProductCard/ProductCard.cjs +13 -35
  12. package/lib/ProductCard/ProductCard.mjs +12 -34
  13. package/lib/_shared/cjs/ProductCardPriceIncrease-BP39clav.cjs +38 -0
  14. package/lib/_shared/esm/ProductCardPriceIncrease-CJyG2wv-.mjs +35 -0
  15. package/lib/index.cjs +507 -199
  16. package/lib/index.cjs.map +1 -1
  17. package/lib/index.d.cts +213 -22
  18. package/lib/index.d.mts +213 -22
  19. package/lib/index.mjs +502 -200
  20. package/lib/index.mjs.map +1 -1
  21. package/lib/styles.css +1 -1
  22. package/package.json +6 -6
  23. package/src/components/ComparisonTable/ComparisonTable.module.css +503 -0
  24. package/src/components/ComparisonTable/ComparisonTable.tsx +104 -0
  25. package/src/components/ComparisonTable/components/Cell/Cell.tsx +44 -0
  26. package/src/components/ComparisonTable/components/ControlCell/ControlCell.tsx +21 -0
  27. package/src/components/ComparisonTable/components/DesktopRow/DesktopRow.tsx +59 -0
  28. package/src/components/ComparisonTable/components/DesktopTable/DesktopTable.tsx +150 -0
  29. package/src/components/ComparisonTable/components/FeatureCell/FeatureCell.module.css +56 -0
  30. package/src/components/ComparisonTable/components/FeatureCell/FeatureCell.tsx +96 -0
  31. package/src/components/ComparisonTable/components/HeaderCell/HeaderCell.tsx +59 -0
  32. package/src/components/ComparisonTable/components/MobileRow/MobileRow.tsx +77 -0
  33. package/src/components/ComparisonTable/components/MobileTable/MobileTable.tsx +103 -0
  34. package/src/components/ComparisonTable/components/Section/Section.tsx +75 -0
  35. package/src/components/ComparisonTable/components/SectionToggle/SectionToggle.tsx +63 -0
  36. package/src/components/ComparisonTable/components/SubHeaderCell/SubHeaderCell.tsx +62 -0
  37. package/src/components/ComparisonTable/index.ts +22 -0
  38. package/src/components/ComparisonTable/types/comparison-column.ts +22 -0
  39. package/src/components/ComparisonTable/types/comparison-row.ts +23 -0
  40. package/src/components/ComparisonTable/types/comparison-section.ts +39 -0
  41. package/src/components/ComparisonTable/types/comparison-sub-row.ts +8 -0
  42. package/src/components/ComparisonTable/utils/group-rows.ts +69 -0
  43. package/src/components/ComparisonTable/utils/use-section-state.ts +45 -0
  44. package/src/components/FeaturePost/FeaturePost.module.css +24 -2
  45. package/src/components/FeaturePost/FeaturePost.tsx +26 -25
  46. package/src/components/FeaturePost/types/feature-post-cta-footer.ts +4 -3
  47. package/src/components/Highlights/Highlights.tsx +5 -32
  48. package/src/components/ProductCard/ProductCardPrice/ProductCardPrice.tsx +11 -3
  49. package/src/components/ProductCard/ProductCardPriceIncrease/ProductCardPriceIncrease.module.css +1 -2
  50. package/src/components/ProductCard/ProductCardPriceIncrease/ProductCardPriceIncrease.tsx +14 -3
  51. package/src/components/index.ts +1 -0
  52. package/versions.json +1 -1
@@ -0,0 +1,63 @@
1
+ import React from "react";
2
+ import classNames from "classnames";
3
+
4
+ import { Heading, type HeadingLevel } from "@arc-ui/components/Heading";
5
+ import { Icon } from "@arc-ui/components/Icon";
6
+ import { BtIconChevronDownMid } from "@arc-ui/icons/BtIconChevronDownMid";
7
+
8
+ import styles from "../../ComparisonTable.module.css";
9
+
10
+ export const SectionToggle: React.FC<SectionToggleProps> = ({
11
+ heading,
12
+ headingLevel = "3",
13
+ isOpen,
14
+ onToggle,
15
+ controlsId,
16
+ }) => (
17
+ <button
18
+ type="button"
19
+ className={classNames(styles.sectionToggle, {
20
+ [styles.sectionToggleOpen]: isOpen,
21
+ })}
22
+ aria-expanded={isOpen}
23
+ aria-controls={controlsId}
24
+ onClick={onToggle}
25
+ >
26
+ <span className={styles.sectionToggleContent}>
27
+ <Heading level={headingLevel} size="s">
28
+ {heading}
29
+ </Heading>
30
+ <span
31
+ className={classNames(styles.sectionChevron, {
32
+ [styles.sectionChevronOpen]: isOpen,
33
+ })}
34
+ aria-hidden="true"
35
+ >
36
+ <Icon icon={BtIconChevronDownMid} size={24} />
37
+ </span>
38
+ </span>
39
+ </button>
40
+ );
41
+
42
+ export interface SectionToggleProps {
43
+ /**
44
+ * Heading text/content shown in the toggle.
45
+ */
46
+ heading: React.ReactNode;
47
+ /**
48
+ * Heading level for the section heading. Defaults to `"3"`.
49
+ */
50
+ headingLevel?: HeadingLevel;
51
+ /**
52
+ * Whether the section is currently open; drives `aria-expanded` and the chevron rotation.
53
+ */
54
+ isOpen: boolean;
55
+ /**
56
+ * Called when the toggle is activated.
57
+ */
58
+ onToggle: () => void;
59
+ /**
60
+ * Id of the collapsible region this toggle controls, for `aria-controls`.
61
+ */
62
+ controlsId: string;
63
+ }
@@ -0,0 +1,62 @@
1
+ import React from "react";
2
+ import classNames from "classnames";
3
+
4
+ import { filterAttrs } from "@arc-ui/community-utils/filter-attrs";
5
+ import { ButtonV2, type ButtonV2Props } from "@arc-ui/components/ButtonV2";
6
+ import { VerticalSpace } from "@arc-ui/components/VerticalSpace";
7
+
8
+ import {
9
+ ProductCardPrice,
10
+ type ProductCardPriceProps,
11
+ } from "../../../ProductCard/ProductCardPrice";
12
+ import {
13
+ ProductCardPriceIncrease,
14
+ type ProductCardPriceIncreaseProps,
15
+ } from "../../../ProductCard/ProductCardPriceIncrease";
16
+ import styles from "../../ComparisonTable.module.css";
17
+
18
+ export const SubHeaderCell: React.FC<SubHeaderCellProps> = ({
19
+ price,
20
+ priceIncrease,
21
+ button,
22
+ columnIndex = 0,
23
+ ...props
24
+ }) => (
25
+ <div
26
+ role="cell"
27
+ className={classNames(styles.subHeaderCell, {
28
+ [styles.highlighted]: columnIndex % 2 === 0,
29
+ })}
30
+ {...filterAttrs(props)}
31
+ >
32
+ <ProductCardPrice {...price} />
33
+ {priceIncrease && (
34
+ <ProductCardPriceIncrease
35
+ heading="Increasing to:"
36
+ hideBorder
37
+ {...priceIncrease}
38
+ />
39
+ )}
40
+ <VerticalSpace />
41
+ {button && <ButtonV2 isFullWidth {...button} />}
42
+ </div>
43
+ );
44
+
45
+ export interface SubHeaderCellProps {
46
+ /**
47
+ * Price shown at the top of the sub-header cell (prefix, value, suffix).
48
+ */
49
+ price: ProductCardPriceProps;
50
+ /**
51
+ * Optional upcoming price-increase details rendered beneath the price.
52
+ */
53
+ priceIncrease?: ProductCardPriceIncreaseProps;
54
+ /**
55
+ * Optional call-to-action button rendered at the bottom of the cell. Rendered full-width.
56
+ */
57
+ button?: ButtonV2Props;
58
+ /**
59
+ * Zero-based index of the column this cell belongs to. Used to highlight every other column, starting with the first.
60
+ */
61
+ columnIndex?: number;
62
+ }
@@ -0,0 +1,22 @@
1
+ export { ComparisonTable, type ComparisonTableProps } from "./ComparisonTable";
2
+ export {
3
+ HeaderCell,
4
+ type HeaderCellProps,
5
+ } from "./components/HeaderCell/HeaderCell";
6
+ export {
7
+ SubHeaderCell,
8
+ type SubHeaderCellProps,
9
+ } from "./components/SubHeaderCell/SubHeaderCell";
10
+ export { Cell, type CellProps } from "./components/Cell/Cell";
11
+ export {
12
+ ControlCell,
13
+ type ControlCellProps,
14
+ } from "./components/ControlCell/ControlCell";
15
+ export {
16
+ FeatureCell,
17
+ type FeatureCellProps,
18
+ } from "./components/FeatureCell/FeatureCell";
19
+ export { type ComparisonColumn } from "./types/comparison-column";
20
+ export { type ComparisonRow } from "./types/comparison-row";
21
+ export { type ComparisonSubRow } from "./types/comparison-sub-row";
22
+ export { type ComparisonSection } from "./types/comparison-section";
@@ -0,0 +1,22 @@
1
+ import { type SubHeaderCellProps } from "../components/SubHeaderCell/SubHeaderCell";
2
+
3
+ export interface ComparisonColumn {
4
+ /**
5
+ * Stable identifier for the column; used as a React key.
6
+ */
7
+ id: string;
8
+ /**
9
+ * Visible label rendered in the column header cell (e.g. a plan name).
10
+ */
11
+ label: string;
12
+ /**
13
+ * Optional promotional text displayed in a branded banner above the column
14
+ * header label. When set, the banner is rendered inside the header cell with
15
+ * a dark brand background.
16
+ */
17
+ promoted?: string;
18
+ /**
19
+ * Optional sub-header content (price, upcoming price increase, call-to-action) rendered in a row beneath the header. When any column defines this, the sub-header row is shown.
20
+ */
21
+ subheader?: SubHeaderCellProps;
22
+ }
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+
3
+ import { type ComparisonSubRow } from "./comparison-sub-row";
4
+ import { type FeatureCellProps } from "../components/FeatureCell/FeatureCell";
5
+
6
+ export interface ComparisonRow {
7
+ /**
8
+ * Feature label rendered as the row's full-width header, spanning all columns above the content row(s).
9
+ */
10
+ feature: FeatureCellProps;
11
+ /**
12
+ * One cell per column for the row's primary content row, in the same order as `columns`. Length MUST equal columns.length.
13
+ */
14
+ cells: React.ReactNode[];
15
+ /**
16
+ * Optional additional content rows rendered beneath the primary row, for the less common case where a feature spans multiple rows.
17
+ */
18
+ subRows?: ComparisonSubRow[];
19
+ /**
20
+ * Id of the `ComparisonSection` this row belongs to. Omit to render the row before all sections. An id with no matching section logs a dev warning and renders the row unsectioned.
21
+ */
22
+ sectionId?: string;
23
+ }
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+
3
+ import { type HeadingLevel } from "@arc-ui/components/Heading";
4
+
5
+ import { type ComparisonRow } from "./comparison-row";
6
+
7
+ export interface ComparisonSection {
8
+ /**
9
+ * Stable identifier for the section; used as a React key and to link the toggle to its content region for accessibility.
10
+ */
11
+ id: string;
12
+ /**
13
+ * Heading shown in the section's toggle button.
14
+ */
15
+ heading: React.ReactNode;
16
+ /**
17
+ * Heading level for the section heading, for correct document outline. Defaults to a sensible level.
18
+ */
19
+ headingLevel?: HeadingLevel;
20
+ /**
21
+ * Initial open state when the section is uncontrolled. Defaults to `false`.
22
+ */
23
+ defaultOpen?: boolean;
24
+ /**
25
+ * Controlled open state. When provided, the section is controlled and `onOpenChange` should update it.
26
+ */
27
+ open?: boolean;
28
+ /**
29
+ * Called with the next open state when the toggle is activated. Required for controlled usage; optional otherwise.
30
+ */
31
+ onOpenChange?: (open: boolean) => void;
32
+ }
33
+
34
+ /**
35
+ * Internal: a section paired with its rows after grouping. Not part of the public API.
36
+ */
37
+ export type ResolvedSection = ComparisonSection & {
38
+ rows: ComparisonRow[];
39
+ };
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+
3
+ export interface ComparisonSubRow {
4
+ /**
5
+ * One cell per column, in the same order as `columns`. Length MUST equal columns.length.
6
+ */
7
+ cells: React.ReactNode[];
8
+ }
@@ -0,0 +1,69 @@
1
+ import { type ComparisonRow } from "../types/comparison-row";
2
+ import {
3
+ type ComparisonSection,
4
+ type ResolvedSection,
5
+ } from "../types/comparison-section";
6
+
7
+ export interface GroupedRows {
8
+ /**
9
+ * Rows without a matching section, rendered before all sections, in input order.
10
+ */
11
+ unsectioned: ComparisonRow[];
12
+ /**
13
+ * Sections in `sections` array order, each with its rows in input order. Sections with no rows are dropped.
14
+ */
15
+ sections: ResolvedSection[];
16
+ }
17
+
18
+ /**
19
+ * Splits a flat list of rows into unsectioned rows and resolved sections.
20
+ * Rows are bucketed by `sectionId` in one pass, then sections are built by
21
+ * iterating `sections` (not the bucket keys) so section order follows the
22
+ * `sections` array. A `sectionId` with no matching section logs a dev-only
23
+ * warning and the row renders unsectioned, so rows are never lost.
24
+ */
25
+ export const groupRows = (
26
+ rows: ComparisonRow[] = [],
27
+ sections: ComparisonSection[] = [],
28
+ ): GroupedRows => {
29
+ const knownIds = new Set(sections.map((section) => section.id));
30
+ const rowsBySection = new Map<string, ComparisonRow[]>();
31
+ const unsectioned: ComparisonRow[] = [];
32
+
33
+ rows.forEach((row) => {
34
+ const { sectionId } = row;
35
+
36
+ if (sectionId === undefined) {
37
+ unsectioned.push(row);
38
+ return;
39
+ }
40
+
41
+ if (!knownIds.has(sectionId)) {
42
+ if (process.env.NODE_ENV !== "production") {
43
+ // eslint-disable-next-line no-console
44
+ console.warn(
45
+ `ComparisonTable: row sectionId "${sectionId}" does not match any section; rendering it unsectioned.`,
46
+ );
47
+ }
48
+ unsectioned.push(row);
49
+ return;
50
+ }
51
+
52
+ const bucket = rowsBySection.get(sectionId);
53
+ if (bucket) {
54
+ bucket.push(row);
55
+ } else {
56
+ rowsBySection.set(sectionId, [row]);
57
+ }
58
+ });
59
+
60
+ const resolvedSections: ResolvedSection[] = [];
61
+ sections.forEach((section) => {
62
+ const sectionRows = rowsBySection.get(section.id);
63
+ if (sectionRows?.length) {
64
+ resolvedSections.push({ ...section, rows: sectionRows });
65
+ }
66
+ });
67
+
68
+ return { unsectioned, sections: resolvedSections };
69
+ };
@@ -0,0 +1,45 @@
1
+ import { useCallback, useState } from "react";
2
+
3
+ export interface UseSectionStateOptions {
4
+ /**
5
+ * Initial open state when uncontrolled.
6
+ */
7
+ defaultOpen?: boolean;
8
+ /**
9
+ * Controlled open state. When defined, the hook is controlled.
10
+ */
11
+ open?: boolean;
12
+ /**
13
+ * Called with the next open state whenever the section is toggled.
14
+ */
15
+ onOpenChange?: (open: boolean) => void;
16
+ }
17
+
18
+ /**
19
+ * Optional-controlled open state for a section. When `open` is provided the
20
+ * section is controlled and the internal state is ignored; otherwise the hook
21
+ * manages its own state seeded by `defaultOpen`. `onOpenChange` is always
22
+ * called with the next value when `toggle` runs.
23
+ */
24
+ export const useSectionState = ({
25
+ defaultOpen = false,
26
+ open,
27
+ onOpenChange,
28
+ }: UseSectionStateOptions) => {
29
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
30
+
31
+ const isControlled = open !== undefined;
32
+ const isOpen = isControlled ? open : internalOpen;
33
+
34
+ const toggle = useCallback(() => {
35
+ const next = !isOpen;
36
+
37
+ if (!isControlled) {
38
+ setInternalOpen(next);
39
+ }
40
+
41
+ onOpenChange?.(next);
42
+ }, [isControlled, isOpen, onOpenChange]);
43
+
44
+ return { isOpen, toggle };
45
+ };
@@ -1,5 +1,25 @@
1
+ /* stylelint-disable selector-pseudo-class-no-unknown */
1
2
  @import url("@arc-ui/tokens-arc/dist/custom-media.css");
2
3
 
4
+ .labelContainer {
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: baseline;
8
+ }
9
+
10
+ .labelText {
11
+ max-width: 100%;
12
+ min-width: 0;
13
+ font: var(--sem-type-label-overline);
14
+ letter-spacing: 0.1em;
15
+ text-transform: uppercase;
16
+ }
17
+
18
+ .labelWordWrap {
19
+ hyphens: auto;
20
+ overflow-wrap: break-word;
21
+ }
22
+
3
23
  .labelDate {
4
24
  padding-top: var(--sem-space-padding-20);
5
25
  text-transform: uppercase;
@@ -43,8 +63,10 @@
43
63
  width: 50%;
44
64
  }
45
65
 
46
- .labelDate {
47
- padding-left: var(--sem-space-padding-70);
66
+ .labelContainer {
67
+ flex-direction: row;
68
+ flex-wrap: wrap;
69
+ column-gap: var(--sem-space-padding-70);
48
70
  }
49
71
  }
50
72
 
@@ -57,31 +57,29 @@ export const FeaturePost: React.FC<FeaturePostProps> = ({
57
57
  <div className={styles.col}>
58
58
  <div className={styles.contentContainer}>
59
59
  {label && (
60
- <Grid isGutterless isFluid>
61
- <GridRow align="baseline">
62
- <GridCol xs={12} m="auto">
63
- <Heading size="xxs" fontStyle="overline">
64
- {label.text}
65
- </Heading>
66
- </GridCol>
67
- <GridCol xs={12} m="auto">
68
- <div className={styles.labelDate}>
69
- <Text size="xs" tone="muted">
70
- {isDate(label.date) ? (
71
- <time
72
- dateTime={format(label.date as Date, "yyyy-MM-dd")}
73
- >
74
- {format(label.date as Date, "d MMMM yyyy")}
75
- </time>
76
- ) : (
77
- <span>{label.date as string}</span>
78
- )}
79
- </Text>
80
- </div>
81
- </GridCol>
82
- </GridRow>
60
+ <>
61
+ <div className={styles.labelContainer}>
62
+ <span
63
+ className={classNames(styles.labelText, {
64
+ [styles.labelWordWrap]: isHeadingWordWrap,
65
+ })}
66
+ >
67
+ {label.text}
68
+ </span>
69
+ <div className={styles.labelDate}>
70
+ <Text size="xs" tone="muted">
71
+ {isDate(label.date) ? (
72
+ <time dateTime={format(label.date as Date, "yyyy-MM-dd")}>
73
+ {format(label.date as Date, "d MMMM yyyy")}
74
+ </time>
75
+ ) : (
76
+ <span>{label.date as string}</span>
77
+ )}
78
+ </Text>
79
+ </div>
80
+ </div>
83
81
  <VerticalSpace size="24" />
84
- </Grid>
82
+ </>
85
83
  )}
86
84
 
87
85
  <Heading
@@ -119,7 +117,10 @@ export const FeaturePost: React.FC<FeaturePostProps> = ({
119
117
  )}
120
118
  <VerticalSpace size="40" />
121
119
 
122
- <ButtonV2 buttonStyle="secondary" {...footer.button} />
120
+ <ButtonV2
121
+ {...footer.button}
122
+ buttonStyle={footer.button.buttonStyle ?? "secondary"}
123
+ />
123
124
  </>
124
125
  )}
125
126
 
@@ -1,12 +1,13 @@
1
- import { type ButtonProps } from "@arc-ui/components/Button";
1
+ import { type ButtonV2Props } from "@arc-ui/components/ButtonV2";
2
2
 
3
3
  import { type AuthorProps } from "../../Author/Author";
4
4
 
5
5
  export interface FeaturePostCtaFooter {
6
6
  type: "cta";
7
+ /** CTA button options. The button style defaults to `secondary`. */
7
8
  button: Pick<
8
- ButtonProps,
9
- "label" | "onClick" | "href" | "target" | "ariaLabel"
9
+ ButtonV2Props,
10
+ "label" | "onClick" | "href" | "target" | "ariaLabel" | "buttonStyle"
10
11
  >;
11
12
  author?: AuthorProps;
12
13
  }
@@ -50,22 +50,18 @@ export const Highlights: React.FC<HighlightsProps> = ({
50
50
  <>
51
51
  <VerticalSpace size="24" />
52
52
  <ButtonV2
53
- label={cta.label}
54
- href={cta.href}
55
- onClick={cta.onClick}
56
- target={cta.target}
53
+ {...cta}
57
54
  size="m"
58
- ariaLabel={cta.ariaLabel}
59
- buttonStyle={cta.buttonStyle}
55
+ buttonStyle={cta.buttonStyle ?? "secondary"}
60
56
  icon={
61
57
  cta.buttonStyle === "compact"
62
58
  ? (cta.icon ?? BtIconArrowRightFill)
63
- : undefined
59
+ : cta.icon
64
60
  }
65
61
  iconActive={
66
62
  cta.buttonStyle === "compact"
67
63
  ? (cta.iconActive ?? BtIconArrowRightFill)
68
- : undefined
64
+ : cta.iconActive
69
65
  }
70
66
  iconPosition={cta.iconPosition ?? "beforeText"}
71
67
  />
@@ -91,28 +87,5 @@ export interface HighlightsProps extends Omit<SectionHeadingProps, "isPadded"> {
91
87
  * Optional CTA rendered at the end of Highlights.
92
88
  * Only ONE CTA is allowed.
93
89
  */
94
- cta?: HighlightsCTAProps;
90
+ cta?: ButtonV2Props;
95
91
  }
96
-
97
- /**
98
- * CTA configuration for Highlights
99
- */
100
- export type HighlightsCTAProps = Pick<
101
- ButtonV2Props,
102
- "label" | "href" | "onClick" | "iconPosition" | "ariaLabel" | "target"
103
- > & {
104
- /**
105
- * Allowed button styles only.
106
- * Primary is intentionally NOT supported.
107
- */
108
- buttonStyle: "secondary" | "compact";
109
-
110
- /**
111
- * Icon override & Active icon override
112
- * - undefined → uses default BtIconArrowRightFill for compact style
113
- * - null → icon disabled
114
- * - string → custom icon
115
- */
116
- icon?: string | null;
117
- iconActive?: string | null;
118
- };
@@ -15,9 +15,17 @@ export const ProductCardPrice: React.FC<ProductCardPriceProps> = ({
15
15
  {prefix}
16
16
  </Text>
17
17
  )}
18
- <Heading color="brand" size="m">
19
- {value}
20
- </Heading>
18
+ <div style={{ display: "flex", alignItems: "baseline" }}>
19
+ <Heading color="brand" size="s">
20
+ {value.slice(0, 1)}
21
+ </Heading>
22
+ <Heading color="brand" size="l">
23
+ {value.slice(1, -3)}
24
+ </Heading>
25
+ <Heading color="brand" size="s">
26
+ {value.slice(-3)}
27
+ </Heading>
28
+ </div>
21
29
  {suffix && (
22
30
  <Text tone="muted" size="s">
23
31
  {suffix}
@@ -16,8 +16,7 @@
16
16
  }
17
17
 
18
18
  .priceIncreaseItem {
19
- display: flex;
20
- gap: 8px;
19
+ display: block;
21
20
  }
22
21
 
23
22
  .priceIncreaseList li:not(:last-child) {
@@ -5,7 +5,12 @@ import { VerticalSpace } from "@arc-ui/components/VerticalSpace";
5
5
 
6
6
  export const ProductCardPriceIncrease: React.FC<
7
7
  ProductCardPriceIncreaseProps
8
- > = ({ heading = "Price increasing to", headingLevel = "4", items = [] }) => {
8
+ > = ({
9
+ heading = "Price increasing to",
10
+ headingLevel = "4",
11
+ items = [],
12
+ hideBorder = false,
13
+ }) => {
9
14
  return (
10
15
  <>
11
16
  <VerticalSpace size="16" />
@@ -14,11 +19,14 @@ export const ProductCardPriceIncrease: React.FC<
14
19
  </Heading>
15
20
  <VerticalSpace size="12" />
16
21
  <div className={styles.priceIncreaseWrapper}>
17
- <div className={styles.priceIncreaseBorder} />
22
+ {!hideBorder && <div className={styles.priceIncreaseBorder} />}
18
23
  <ul className={styles.priceIncreaseList}>
19
24
  {items.map((item, index) => (
20
25
  <li key={index} className={styles.priceIncreaseItem}>
21
- {item}
26
+ <>
27
+ <b>{item.slice(0, 6)}</b>
28
+ <>{item.slice(6)}</>
29
+ </>
22
30
  </li>
23
31
  ))}
24
32
  </ul>
@@ -36,4 +44,7 @@ export interface ProductCardPriceIncreaseProps {
36
44
 
37
45
  /** List of price increase entries (e.g. ["£48.99 by April 2027"]) */
38
46
  items: string[];
47
+
48
+ /** Optional prop to hide the left border */
49
+ hideBorder?: boolean;
39
50
  }
@@ -27,3 +27,4 @@ export * from "./BusinessSegmentSwitcher";
27
27
  export * from "./PartnerBrandLogos";
28
28
  export * from "./ProductCard";
29
29
  export * from "./ProductCardGrid";
30
+ export * from "./ComparisonTable";
package/versions.json CHANGED
@@ -1 +1 @@
1
- [{"version":"v3.16.0","date":null},{"version":"v3.15.0","date":"2026-09-15"},{"version":"v3.14.0","date":"2026-08-27"},{"version":"v3.13.1","date":"2026-07-30"},{"version":"v3.13.0","date":"2026-07-24"},{"version":"v3.12.0","date":"2026-07-08"},{"version":"v3.11.0","date":"2026-06-24"},{"version":"v3.10.0","date":"2026-06-16"},{"version":"v3.9.0","date":"2026-05-22"},{"version":"v3.8.1","date":"2026-05-13"},{"version":"v3.8.0","date":"2026-04-29"},{"version":"v3.7.1","date":"2026-04-22"},{"version":"v3.7.0","date":"2026-04-17"},{"version":"v3.6.0","date":"2026-04-10"},{"version":"v3.5.0","date":"2026-04-02"},{"version":"v3.4.0","date":"2026-03-26"},{"version":"v3.3.0","date":"2026-03-20"},{"version":"v3.2.0","date":"2026-03-09"},{"version":"v3.1.0","date":"2026-02-26"},{"version":"v3.0.1","date":"2026-02-18"},{"version":"v3.0.0","date":null},{"version":"v2.0.0","date":"2026-01-21"},{"version":"v1.1.0","date":"2025-11-19"},{"version":"v1.0.2","date":"2025-08-18"},{"version":"v1.0.1","date":"2025-08-05"},{"version":"v1.0.0","date":"2025-04-29"},{"version":"v0.1.0","date":null}]
1
+ [{"version":"v3.17.0","date":null},{"version":"v3.16.0","date":"2026-10-07"},{"version":"v3.15.0","date":"2026-09-15"},{"version":"v3.14.0","date":"2026-08-27"},{"version":"v3.13.1","date":"2026-07-30"},{"version":"v3.13.0","date":"2026-07-24"},{"version":"v3.12.0","date":"2026-07-08"},{"version":"v3.11.0","date":"2026-06-24"},{"version":"v3.10.0","date":"2026-06-16"},{"version":"v3.9.0","date":"2026-05-22"},{"version":"v3.8.1","date":"2026-05-13"},{"version":"v3.8.0","date":"2026-04-29"},{"version":"v3.7.1","date":"2026-04-22"},{"version":"v3.7.0","date":"2026-04-17"},{"version":"v3.6.0","date":"2026-04-10"},{"version":"v3.5.0","date":"2026-04-02"},{"version":"v3.4.0","date":"2026-03-26"},{"version":"v3.3.0","date":"2026-03-20"},{"version":"v3.2.0","date":"2026-03-09"},{"version":"v3.1.0","date":"2026-02-26"},{"version":"v3.0.1","date":"2026-02-18"},{"version":"v3.0.0","date":null},{"version":"v2.0.0","date":"2026-01-21"},{"version":"v1.1.0","date":"2025-11-19"},{"version":"v1.0.2","date":"2025-08-18"},{"version":"v1.0.1","date":"2025-08-05"},{"version":"v1.0.0","date":"2025-04-29"},{"version":"v0.1.0","date":null}]