From b190fa664cbe7416a4512b4d31bf0e4e84bcd72d Mon Sep 17 00:00:00 2001 From: Stephen Watkins Date: Thu, 17 Oct 2024 13:26:56 -0400 Subject: [PATCH] feat(Icon): add (official) support for image symbols (#1411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 Changes We had support for images in `Icon` but this makes it official and adjusts types/docs. - Adds example of using `img` as `Icon` symbol - Update docs - Update types ## ✅ Checklist Easy UI has certain UX standards that must be met. In general, non-trivial changes should meet the following criteria: - [ ] ~Visuals match Design Specs in Figma~ - [x] Stories accompany any component changes - [x] Code is in accordance with our style guide - [ ] ~Design tokens are utilized~ - [x] Unit tests accompany any component changes - [x] TSDoc is written for any API surface area - [x] Specs are up-to-date - [x] Console is free from warnings - [x] No accessibility violations are reported - [x] Cross-browser check is performed (Chrome, Safari, Firefox) - [x] Changeset is added ~Strikethrough~ any items that are not applicable to this pull request. --- .changeset/clever-feet-clean.md | 5 +++++ documentation/specs/Icon.md | 2 +- easy-ui-react/src/Icon/Icon.module.scss | 2 +- easy-ui-react/src/Icon/Icon.stories.tsx | 12 +++++++++++- easy-ui-react/src/Icon/Icon.test.tsx | 11 ++++++++++- easy-ui-react/src/Icon/Icon.tsx | 19 +++++++++++++++++-- easy-ui-react/src/types.ts | 8 +++++--- 7 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 .changeset/clever-feet-clean.md diff --git a/.changeset/clever-feet-clean.md b/.changeset/clever-feet-clean.md new file mode 100644 index 000000000..fcbe3e9d8 --- /dev/null +++ b/.changeset/clever-feet-clean.md @@ -0,0 +1,5 @@ +--- +"@easypost/easy-ui": minor +--- + +feat(Icon): add support for images diff --git a/documentation/specs/Icon.md b/documentation/specs/Icon.md index ff0bfdb80..e34c7a671 100644 --- a/documentation/specs/Icon.md +++ b/documentation/specs/Icon.md @@ -14,7 +14,7 @@ type ResponsiveIconSize = { type IconColor = TokenNamespace<"color">; interface Icon { - symbol: ReactElement; + symbol: ReactElement | ReactElement>; size?: IconSize | ResponsiveIconSize; color?: IconColor; accessibilityLabel?: string; diff --git a/easy-ui-react/src/Icon/Icon.module.scss b/easy-ui-react/src/Icon/Icon.module.scss index 611f3a79f..c68ef7a0b 100644 --- a/easy-ui-react/src/Icon/Icon.module.scss +++ b/easy-ui-react/src/Icon/Icon.module.scss @@ -21,7 +21,7 @@ } } -.Svg { +.symbol { position: relative; display: block; width: 100%; diff --git a/easy-ui-react/src/Icon/Icon.stories.tsx b/easy-ui-react/src/Icon/Icon.stories.tsx index c23d35abf..8b18e4bee 100644 --- a/easy-ui-react/src/Icon/Icon.stories.tsx +++ b/easy-ui-react/src/Icon/Icon.stories.tsx @@ -3,7 +3,7 @@ import ErrorIcon from "@easypost/easy-ui-icons/Error"; import InfoIcon from "@easypost/easy-ui-icons/Info"; import WarningIcon from "@easypost/easy-ui-icons/Warning"; import { Meta, StoryObj } from "@storybook/react"; -import React from "react"; +import React, { ComponentProps } from "react"; import { createColorTokensControl, createLabelledOptionsControl, @@ -23,6 +23,7 @@ const meta: Meta = { Info: InfoIcon, Warning: WarningIcon, Error: ErrorIcon, + FedExLogoImg: FedExLogoImg, }), size: createLabelledOptionsControl({ md: "md", @@ -54,3 +55,12 @@ export const Controls: Story = { symbol: CheckCircleIcon, }, }; + +function FedExLogoImg(props: ComponentProps<"img">) { + return ( + + ); +} diff --git a/easy-ui-react/src/Icon/Icon.test.tsx b/easy-ui-react/src/Icon/Icon.test.tsx index eda1e8325..4d95585c4 100644 --- a/easy-ui-react/src/Icon/Icon.test.tsx +++ b/easy-ui-react/src/Icon/Icon.test.tsx @@ -1,6 +1,6 @@ import CheckCircleIcon from "@easypost/easy-ui-icons/CheckCircle"; import { render, screen } from "@testing-library/react"; -import React from "react"; +import React, { ComponentProps } from "react"; import { getResponsiveDesignToken, getComponentThemeToken, @@ -39,4 +39,13 @@ describe("", () => { getResponsiveDesignToken("icon", "size", "size.icon", "sm"), ); }); + + it("should render an image", () => { + const CarrierLogo = (props: ComponentProps<"img">) => ( + + ); + render(); + expect(screen.getByRole("img", { hidden: false })).toBeInTheDocument(); + expect(screen.getByTitle("Carrier name")).toBeInTheDocument(); + }); }); diff --git a/easy-ui-react/src/Icon/Icon.tsx b/easy-ui-react/src/Icon/Icon.tsx index f417bfeb3..ce8a47718 100644 --- a/easy-ui-react/src/Icon/Icon.tsx +++ b/easy-ui-react/src/Icon/Icon.tsx @@ -22,7 +22,12 @@ export type IconColor = | "primary-inverse"; export type IconProps = { - /** Icon symbol SVG source from @easypost/easy-ui-icons */ + /** + * Icon symbol. + * + * This can be an SVG source from @easypost/easy-ui-icons or a React + * element that renders to an img. + */ symbol: IconSymbol; /** Size of the icon */ size?: ResponsiveProp; @@ -82,6 +87,16 @@ export type IconProps = { * return ; * } * ``` + * @example + * _With image symbol:_ + * ```tsx + * import { Icon } from "@easypost/easy-ui/Icon"; + * + * export function Component() { + * const CarrierLogo = (props) => ; + * return ; + * } + * ``` */ export function Icon({ symbol: Symbol, @@ -101,7 +116,7 @@ export function Icon({ return ( = Namespace< export type ThemeColorAliases = ThemeTokenNamespace<"color">; -type IconSymbolProps = React.SVGProps & { +type IconSvgSymbolProps = React.SVGProps & { title?: string; titleId?: string; }; -export type IconSymbol = React.FunctionComponent; +export type IconSymbol = + | React.FunctionComponent + | React.FunctionComponent>; export type SpaceScale = DesignTokenNamespace<"space">; export type ResponsiveSpaceScale = ResponsiveProp;