From 7512bff1bd26dcbc1d3a64aa3de157ba05f38fcd Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Wed, 2 Apr 2025 16:50:04 -0400 Subject: [PATCH 1/4] feat(Badge): new colors --- easy-ui-react/src/Badge/Badge.module.scss | 268 +++++++++++++++++++++- easy-ui-react/src/Badge/Badge.stories.tsx | 31 +-- easy-ui-react/src/Badge/Badge.test.tsx | 21 +- easy-ui-react/src/Badge/Badge.tsx | 57 +++-- 4 files changed, 319 insertions(+), 58 deletions(-) diff --git a/easy-ui-react/src/Badge/Badge.module.scss b/easy-ui-react/src/Badge/Badge.module.scss index 29aaad162..c58d4270a 100644 --- a/easy-ui-react/src/Badge/Badge.module.scss +++ b/easy-ui-react/src/Badge/Badge.module.scss @@ -1,7 +1,7 @@ @use "../styles/common" as *; .root { - @include font-style("small-button"); + @include font-style("subtitle2"); border-radius: design-token("shape.border_radius.lg"); display: inline-flex; // Ensures the background colors do not bleed out of the border radius edges @@ -31,11 +31,17 @@ } // prettier-ignore -.variantPrimary { - @include component-token("badge", "primary.color.background", design-token("color.primary.600")); - @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +.variantPrimary.hasIconOrSecondaryLabel { @include component-token("badge", "secondary.color.background", design-token("color.primary.100")); @include component-token("badge", "secondary.color.text", design-token("color.primary.800")); + @include component-token("badge", "primary.color.background", design-token("color.primary.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantPrimary:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.primary.700")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); } // prettier-ignore @@ -71,7 +77,7 @@ } // prettier-ignore -.variantSuccess { +.variantPositive { @include component-token("badge", "primary.color.background", design-token("color.positive.600")); @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); @include component-token("badge", "secondary.color.background", design-token("color.positive.100")); @@ -93,3 +99,255 @@ @include component-token("badge", "secondary.color.background", design-token("color.negative.100")); @include component-token("badge", "secondary.color.text", design-token("color.primary.800")); } + +// prettier-ignore +.variantPrimary\.100.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.primary.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.primary.100")); + @include component-token("badge", "secondary.color.text", design-token("color.primary.900")); +} + +// prettier-ignore +.variantPrimary\.100:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.primary.100")); + @include component-token("badge", "primary.color.text", design-token("color.primary.900")); +} + +// prettier-ignore +.variantPrimary\.500.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.primary.800")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.primary.500")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantPrimary\.500:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.primary.500")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantPrimary\.700.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.primary.900")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.primary.700")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantPrimary\.700:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.primary.700")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantSecondary\.100.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.secondary.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.secondary.100")); + @include component-token("badge", "secondary.color.text", design-token("color.secondary.900")); +} + +// prettier-ignore +.variantSecondary\.100:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.secondary.100")); + @include component-token("badge", "primary.color.text", design-token("color.secondary.900")); +} + +// prettier-ignore +.variantSecondary\.500.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.secondary.800")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.secondary.500")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantSecondary\.500:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.secondary.500")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantSecondary\.700.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.secondary.900")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.secondary.700")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantSecondary\.700:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.secondary.700")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantPositive\.100.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.positive.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.positive.100")); + @include component-token("badge", "secondary.color.text", design-token("color.positive.900")); +} + +// prettier-ignore +.variantPositive\.100:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.positive.100")); + @include component-token("badge", "primary.color.text", design-token("color.positive.900")); +} + +// prettier-ignore +.variantPositive\.600.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.positive.800")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.positive.600")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantPositive\.600:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.positive.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantPositive\.700.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.positive.900")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.positive.700")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantPositive\.700:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.positive.700")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantNegative\.100.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.negative.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.negative.100")); + @include component-token("badge", "secondary.color.text", design-token("color.negative.900")); +} + +// prettier-ignore +.variantNegative\.100:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.negative.100")); + @include component-token("badge", "primary.color.text", design-token("color.negative.900")); +} + +// prettier-ignore +.variantNegative\.400.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.negative.700")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.negative.400")); + @include component-token("badge", "secondary.color.text", design-token("color.negative.900")); +} + +// prettier-ignore +.variantNegative\.400:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.negative.400")); + @include component-token("badge", "primary.color.text", design-token("color.negative.900")); +} + +// prettier-ignore +.variantNegative\.600.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.negative.800")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.negative.600")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantNegative\.600:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.negative.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantWarning\.100.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.warning.500")); + @include component-token("badge", "primary.color.text", design-token("color.warning.900")); + @include component-token("badge", "secondary.color.background", design-token("color.warning.100")); + @include component-token("badge", "secondary.color.text", design-token("color.warning.900")); +} + +// prettier-ignore +.variantWarning\.100:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.warning.100")); + @include component-token("badge", "primary.color.text", design-token("color.warning.900")); +} + +// prettier-ignore +.variantWarning\.500.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.warning.700")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.warning.500")); + @include component-token("badge", "secondary.color.text", design-token("color.warning.900")); +} + +// prettier-ignore +.variantWarning\.500:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.warning.500")); + @include component-token("badge", "primary.color.text", design-token("color.warning.900")); +} + +// prettier-ignore +.variantWarning\.600.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.warning.800")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.warning.600")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantWarning\.600:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.warning.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantNeutral\.050.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.neutral.500")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.neutral.050")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.900")); +} + +// prettier-ignore +.variantNeutral\.050:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.neutral.050")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.900")); +} + +// prettier-ignore +.variantNeutral\.500.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.neutral.700")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.neutral.500")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantNeutral\.500:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.neutral.500")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantNeutral\.600.hasIconOrSecondaryLabel { + @include component-token("badge", "primary.color.background", design-token("color.neutral.800")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); + @include component-token("badge", "secondary.color.background", design-token("color.neutral.600")); + @include component-token("badge", "secondary.color.text", design-token("color.neutral.000")); +} + +// prettier-ignore +.variantNeutral\.600:not(.hasIconOrSecondaryLabel) { + @include component-token("badge", "primary.color.background", design-token("color.neutral.600")); + @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); +} diff --git a/easy-ui-react/src/Badge/Badge.stories.tsx b/easy-ui-react/src/Badge/Badge.stories.tsx index 5ee6bfb7c..9b5a9ae93 100644 --- a/easy-ui-react/src/Badge/Badge.stories.tsx +++ b/easy-ui-react/src/Badge/Badge.stories.tsx @@ -48,19 +48,6 @@ export const SimpleText: Story = { }, }; -export const SimpleIcon: Story = { - render: Template.bind({}), - args: { - icon: AnchorIcon, - accessibilityLabel: "Text to describe badge", - }, - parameters: { - controls: { - include: ["accessibilityLabel", "icon", "variant"], - }, - }, -}; - export const DetailedIcon: Story = { render: Template.bind({}), args: { @@ -98,6 +85,24 @@ export const ColorVariants: Story = { + + + + + + + + + + + + + + + + + + ), args: { diff --git a/easy-ui-react/src/Badge/Badge.test.tsx b/easy-ui-react/src/Badge/Badge.test.tsx index a4b061a18..e4135125e 100644 --- a/easy-ui-react/src/Badge/Badge.test.tsx +++ b/easy-ui-react/src/Badge/Badge.test.tsx @@ -22,11 +22,6 @@ describe("", () => { expect(screen.getByText("Badge text")).toBeInTheDocument(); }); - it("should render simple icon", () => { - render(); - expect(screen.getByText(/intent of badge/i)).toBeInTheDocument(); - }); - it("should render detailed icon", () => { render(Badge text); expect(screen.getByRole("img", { hidden: true })).toBeInTheDocument(); @@ -47,17 +42,11 @@ describe("", () => { ); }); - it("should warn on missing children or icon", () => { + it("should warn on missing children", () => { + // @ts-expect-error warning check render(); expect(consoleWarnSpy).toBeCalledWith( - expect.stringMatching(/requires one of children or icon/), - ); - }); - - it("should warn with no accessibility label on icon", () => { - render(); - expect(consoleWarnSpy).toBeCalledWith( - expect.stringMatching(/must have accessibilityLabel/), + expect.stringMatching(/requires children/), ); }); @@ -67,7 +56,9 @@ describe("", () => { accessibilityLabel="Intent of badge" secondaryLabel="Secondary text" icon={Anchor} - />, + > + Badge text + , ); expect(consoleWarnSpy).toBeCalledWith( expect.stringMatching(/secondaryLabel is not supported/), diff --git a/easy-ui-react/src/Badge/Badge.tsx b/easy-ui-react/src/Badge/Badge.tsx index 7866804e2..c1ad1f223 100644 --- a/easy-ui-react/src/Badge/Badge.tsx +++ b/easy-ui-react/src/Badge/Badge.tsx @@ -16,17 +16,34 @@ export type BadgeVariant = | "gray" | "success" | "warning" - | "danger"; + | "danger" + | "primary.100" + | "primary.500" + | "primary.700" + | "secondary.100" + | "secondary.500" + | "secondary.700" + | "positive.100" + | "positive.600" + | "positive.700" + | "negative.100" + | "negative.400" + | "negative.600" + | "warning.100" + | "warning.500" + | "warning.600" + | "neutral.050" + | "neutral.500" + | "neutral.600"; export type BadgeProps = { /** - * Accessible label for the badge if it differs from its content. Required - * for icon badges. + * Accessible label for the badge if it differs from its content. */ accessibilityLabel?: string; /** Primary badge label. */ - children?: ReactNode; + children: ReactNode; /** Badge icon */ icon?: IconSymbol; @@ -57,12 +74,6 @@ export type BadgeProps = { * ``` * * @example - * _Simple icon:_ - * ```tsx - * - * ``` - * - * @example * _Detailed text:_ * ```tsx * @@ -88,39 +99,35 @@ export function Badge(props: BadgeProps) { const className = classNames( styles.root, styles[variationName("variant", variant)], + (icon || secondaryLabel) && styles.hasIconOrSecondaryLabel, ); // Ideally the below conditions could use discriminated type unions to enforce // constraints statically but as of now it makes for too rigorous of an API. // Can consider revisiting in the future. - if (!icon && !children) { - console.warn("Badge requires one of children or icon"); + if (!children) { + console.warn("Badge requires children"); } if (secondaryLabel && icon) { console.warn("secondaryLabel is not supported on a Badge with icon"); } - if (icon && !children && !accessibilityLabel) { - console.warn("Badge with only icon must have accessibilityLabel"); - } - return ( {accessibilityLabel && {accessibilityLabel}} - - {icon ? ( + {icon && ( + - ) : ( - {children} - )} + + )} + + {children} - {children && (icon || secondaryLabel) && ( + {secondaryLabel && ( - - {icon ? <>{children} : <>{secondaryLabel}} - + {secondaryLabel} )} From 3b20f22a3919169f2371c58446fdfc94f159e546 Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Wed, 2 Apr 2025 16:50:26 -0400 Subject: [PATCH 2/4] changeset --- .changeset/young-clouds-taste.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/young-clouds-taste.md diff --git a/.changeset/young-clouds-taste.md b/.changeset/young-clouds-taste.md new file mode 100644 index 000000000..43e9264c6 --- /dev/null +++ b/.changeset/young-clouds-taste.md @@ -0,0 +1,5 @@ +--- +"@easypost/easy-ui": minor +--- + +feat(Bage): new colors From 9d3e4a26a52a9aafdc7b76a881f6d151749d0f09 Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Wed, 2 Apr 2025 16:53:42 -0400 Subject: [PATCH 3/4] typo --- easy-ui-react/src/Badge/Badge.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easy-ui-react/src/Badge/Badge.module.scss b/easy-ui-react/src/Badge/Badge.module.scss index c58d4270a..410133d56 100644 --- a/easy-ui-react/src/Badge/Badge.module.scss +++ b/easy-ui-react/src/Badge/Badge.module.scss @@ -77,7 +77,7 @@ } // prettier-ignore -.variantPositive { +.variantSuccess { @include component-token("badge", "primary.color.background", design-token("color.positive.600")); @include component-token("badge", "primary.color.text", design-token("color.neutral.000")); @include component-token("badge", "secondary.color.background", design-token("color.positive.100")); From 8363c96442982f81991230244c8ab52a3bee7eb6 Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Wed, 2 Apr 2025 17:09:14 -0400 Subject: [PATCH 4/4] fix mdx --- easy-ui-react/src/Badge/Badge.mdx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/easy-ui-react/src/Badge/Badge.mdx b/easy-ui-react/src/Badge/Badge.mdx index 845a309e2..dfdbc2e2d 100644 --- a/easy-ui-react/src/Badge/Badge.mdx +++ b/easy-ui-react/src/Badge/Badge.mdx @@ -13,12 +13,6 @@ A `` can be a simple label. -## Simple Icon - -A `` can be a single icon using the `icon` property. The `icon` property accepts any icon symbol from `@easypost/easy-ui-icons`. - - - ## Detailed Icon A `` can contain an icon and supporting label.