Reputation: 27507
I am trying to create a SvgIcon
component that I pass an SVG to through the props.
Because of the way React handles SVGs, when they're imported as ReactComponents (e.g. import { ReactComponent as ArrowIcon } from "icons/arrow.svg"
) I have to define my component in a dynamic way.
This is what I have so far:
const styleSvgIcon = ({ theme, stroke, fill, size, icon: Icon }) => styled(
Icon
).attrs({
viewBox: `0 0 24 24`,
})`
path {
${stroke && `stroke: ${calculateColor({ color: stroke, theme })};`}
${fill && `fill: ${calculateColor({ color: fill, theme })};`}
}
height: ${calculateSize(size)};
width: ${calculateSize(size)};
`;
const SvgIcon = ({ stroke, fill, size, icon }) => {
const theme = useTheme();
const StyledIcon = styleSvgIcon({ theme, stroke, fill, size, icon });
console.log('styledddddddddd', StyledIcon); // prints the styled component with the new styles, but on the page nothing changes
return <StyledIcon />;
};
SvgIcon.propTypes = {
stroke: PropTypes.oneOf(Object.values(SVG_ICON_COLORS)),
fill: PropTypes.oneOf(Object.values(SVG_ICON_COLORS)),
size: PropTypes.oneOf(Object.values(SVG_ICON_SIZES)),
icon: PropTypes.elementType.isRequired,
};
export default SvgIcon;
Here's my story definition:
import React from 'react';
import SvgIcon from './SvgIcon';
import * as all_icons from '../icons';
import { SVG_ICON_SIZES, SVG_ICON_COLORS } from '../constants/variants';
export default {
title: 'media/SvgIcon',
component: SvgIcon,
argTypes: {
size: {
type: 'select',
options: Object.values(SVG_ICON_SIZES),
defaultValue: SVG_ICON_SIZES.SMALL,
},
stroke: {
type: 'select',
options: Object.values(SVG_ICON_COLORS),
defaultValue: SVG_ICON_COLORS.PRIMARY,
},
fill: {
type: 'select',
options: Object.values(SVG_ICON_COLORS),
defaultValue: SVG_ICON_COLORS.PRIMARY,
},
},
};
const Template = (args) => {
const icons = Object.values(all_icons);
// return icons.map((SvgComponent, i) => (
return icons
.slice(0, 2)
.map((SvgComponent, i) => (
<SvgIcon key={i} {...args} icon={SvgComponent} />
));
};
export const Small = Template.bind({});
Small.args = {
size: SVG_ICON_SIZES.SMALL,
};
export const Medium = Template.bind({});
Medium.args = {
size: SVG_ICON_SIZES.MEDIUM,
};
export const Large = Template.bind({});
Large.args = {
size: SVG_ICON_SIZES.LARGE,
};
export const XL = Template.bind({});
XL.args = {
size: SVG_ICON_SIZES.XL,
};
This works fine locally, but when I deploy my app and try to change the props through Storybook, nothing gets updated.
However, in production, the controls do nothing.
Upvotes: 2
Views: 1183
Reputation: 27507
I FIGURED IT OUT!!!
So because I was just setting the styles without using the props like this:
size: ${calculate(size)};
styled-components somehow knows and therefore doesn't add listeners to updated props. So my StyledIcon
just becomes a static component that does not update when new styles are passed in since they are just set in stone.
Instead, passing them as props and accessing them through props adds those listeners
const SvgIcon = ({ stroke, fill, size, icon: Icon }) => {
const StyledIcon = styled(Icon).attrs({
viewBox: `0 0 24 24`,
})`
path {
stroke: ${({ stroke, theme }) =>
calculateColor({ color: stroke, theme })};
fill: ${({ fill, theme }) => calculateColor({ color: fill, theme })};
}
height: ${({ size }) => calculateSize(size)};
width: ${({ size }) => calculateSize(size)};
`;
return (
<div>
<StyledIcon size={size} fill={fill} stroke={stroke} />,{size},{fill},
{stroke}
<StyledArrowUpIcon stroke={stroke} size={size} fill={fill} />
</div>
);
};
Upvotes: 1