Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Currently, this repo is in Prerelease. When it is released, this project will ad

## Prerelease

### Fixes

- Fixed the double application of styles when a custom svg is passed into the `Icon` component.

## 4.1.4 (February 26, 2026)

### Updates
Expand Down
2 changes: 1 addition & 1 deletion src/components/Icons/Icon.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { changelogData } from "./iconChangelogData";
componentName="Icon"
summary="Renders commonly used icons in SVG format"
versionAdded="0.0.4"
versionLatest="4.1.1"
versionLatest="Prerelease"
/>

<Canvas of={IconStories.WithControls} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/Icons/Icon.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe("Icon", () => {
const warn = jest.spyOn(console, "warn");
render(<Icon>Not an SVG</Icon>);
expect(warn).toHaveBeenCalledWith(
"NYPL Reservoir Icon: An `svg` element must be passed to the `Icon` " +
"NYPL Reservoir Icon: Only an `svg` element can be passed to the `Icon` " +
"component as its child."
);
});
Expand Down
34 changes: 16 additions & 18 deletions src/components/Icons/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ export const Icon: ChakraComponent<
variant = "default",
...rest
} = props;
const hasChildSVG = children && (children as JSX.Element).type === "svg";
const styles = useStyleConfig("ReservoirIcon", {
align,
color,
hasChildSVG,
iconRotation,
size,
variant,
Expand All @@ -90,16 +92,21 @@ export const Icon: ChakraComponent<
title,
...rest,
};
let childSVG: any = null;

// Component prop validation
if (name && children) {
if (name && hasChildSVG) {
console.warn(
"NYPL Reservoir Icon: Pass in either a `name` prop or an `svg` element " +
"child. Do not pass both."
);
return null;
} else if (!name && !children) {
} else if (children && !hasChildSVG) {
console.warn(
"NYPL Reservoir Icon: Only an `svg` element can be passed to the `Icon` " +
"component as its child."
);
return null;
} else if (!name && !hasChildSVG) {
console.warn(
"NYPL Reservoir Icon: Pass an icon `name` prop or an SVG child to " +
"ensure an icon appears."
Expand All @@ -117,22 +124,13 @@ export const Icon: ChakraComponent<
);
}

// If no `name` prop was passed, we expect a child SVG element to be passed.
// If all prop validation passed and no `name` prop was passed, we expect a
// child SVG element was passed and render it accordingly.
// Apply icon props to the SVG child.
if (
(children as JSX.Element).type === "svg" ||
(children as JSX.Element).props?.type === "svg"
) {
childSVG = React.cloneElement(children as JSX.Element, {
...iconProps,
ref,
});
} else {
console.warn(
"NYPL Reservoir Icon: An `svg` element must be passed to the `Icon` " +
"component as its child."
);
}
const childSVG = React.cloneElement(children as JSX.Element, {
...iconProps,
ref,
});

return (
<Box ref={ref} __css={styles}>
Expand Down
9 changes: 9 additions & 0 deletions src/components/Icons/iconChangelogData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
import { ChangelogData } from "../../utils/ComponentChangelogTable";

export const changelogData: ChangelogData[] = [
{
date: "Prerelease",
version: "Prerelease",
type: "Bug Fix",
affects: ["Functionality", "Styles"],
notes: [
"Fixed the double application of styles when a custom svg is passed.",
],
},
{
date: "2025-12-11",
version: "4.1.1",
Expand Down
6 changes: 5 additions & 1 deletion src/theme/components/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const iconRotation: Record<string, { transform: string }> = {
interface IconBaseStyle extends StyleFunctionProps {
align: keyof typeof align;
color: string;
hasChildSVG: boolean;
iconRotation: keyof typeof iconRotation;
size: keyof typeof iconSizeStyles;
}
Expand All @@ -53,7 +54,10 @@ const Icon = defineStyleConfig({
};

return {
...allStyles,
// Apply styles to the root element if a custom svg is not passed
...(!props.hasChildSVG ? allStyles : {}),

// Apply styles to a child svg element
svg: {
...allStyles,
},
Expand Down