From b03a5b6151b27d3c13802750a3946dc3d8562fd0 Mon Sep 17 00:00:00 2001 From: kirilldyachkovskiy Date: Tue, 22 Aug 2023 12:17:40 +0300 Subject: [PATCH] feat(Icon): forward ref to component --- src/components/Icon/Icon.tsx | 180 +++++++++++++++++------------------ 1 file changed, 89 insertions(+), 91 deletions(-) diff --git a/src/components/Icon/Icon.tsx b/src/components/Icon/Icon.tsx index cf2792b52..8300c61f9 100644 --- a/src/components/Icon/Icon.tsx +++ b/src/components/Icon/Icon.tsx @@ -18,6 +18,10 @@ import './Icon.scss'; export type IconData = SVGIconData; +interface IconComposition { + prefix?: string; +} + export interface IconProps extends QAProps { data: IconData; width?: number | string; @@ -30,110 +34,104 @@ export interface IconProps extends QAProps { const b = block('icon'); -export function Icon({ - data, - width, - height, - size, - className, - fill = 'currentColor', - stroke = 'none', - qa, -}: IconProps) { - // This component supports four different ways to load and use icons: - // - svg-react-loader - // - svg-sprite-loader - // - @svgr/webpack - // - string with raw svg - - let w, h; - - if (size) { - w = size; - h = size; - } - - if (width) { - w = width; - } - - if (height) { - h = height; - } - - // Parsing viewBox to get width and height in case they were not specified - // For svg-react-loader svg attributes are available in component defaultProps - // In case with @svgr/webpack svg attributes can be fetched from the react element - // after calling svgr-component without any propses - let viewBox: string | undefined; - - if (isSpriteData(data)) { - ({viewBox} = data); - } else if (isStringSvgData(data)) { - viewBox = getStringViewBox(data); - } else if (isComponentSvgData(data)) { - ({viewBox} = data.defaultProps); - } else if (isSvgrData(data)) { - const el = data({}); - - if (el) { - ({viewBox} = el.props); +export const Icon: React.ForwardRefExoticComponent> & + IconComposition = React.forwardRef( + ({data, width, height, size, className, fill = 'currentColor', stroke = 'none', qa}, ref) => { + // This component supports four different ways to load and use icons: + // - svg-react-loader + // - svg-sprite-loader + // - @svgr/webpack + // - string with raw svg + + let w, h; + + if (size) { + w = size; + h = size; } - } - if (viewBox && (!w || !h)) { - const values = viewBox.split(/\s+|\s*,\s*/); + if (width) { + w = width; + } - if (!w) { - w = values[2]; + if (height) { + h = height; } - if (!h) { - h = values[3]; + + // Parsing viewBox to get width and height in case they were not specified + // For svg-react-loader svg attributes are available in component defaultProps + // In case with @svgr/webpack svg attributes can be fetched from the react element + // after calling svgr-component without any propses + let viewBox: string | undefined; + + if (isSpriteData(data)) { + ({viewBox} = data); + } else if (isStringSvgData(data)) { + viewBox = getStringViewBox(data); + } else if (isComponentSvgData(data)) { + ({viewBox} = data.defaultProps); + } else if (isSvgrData(data)) { + const el = data({}); + + if (el) { + ({viewBox} = el.props); + } } - } - const props = { - xmlns: 'http://www.w3.org/2000/svg', - xmlnsXlink: 'http://www.w3.org/1999/xlink', - width: w, - height: h, - className: b(null, className), - fill, - stroke, - 'data-qa': qa, - ...a11yHiddenSvgProps, - }; + if (viewBox && (!w || !h)) { + const values = viewBox.split(/\s+|\s*,\s*/); - if (isStringSvgData(data)) { - const preparedData = prepareStringData(data); + if (!w) { + w = values[2]; + } + if (!h) { + h = values[3]; + } + } - return ; - } + const props = { + xmlns: 'http://www.w3.org/2000/svg', + xmlnsXlink: 'http://www.w3.org/1999/xlink', + width: w, + height: h, + className: b(null, className), + fill, + stroke, + 'data-qa': qa, + ...a11yHiddenSvgProps, + }; + + if (isStringSvgData(data)) { + const preparedData = prepareStringData(data); + + return ; + } - if (isSpriteData(data)) { - const href = Icon.prefix + (data.url || `#${data.id}`); + if (isSpriteData(data)) { + const href = Icon.prefix + (data.url || `#${data.id}`); - return ( - - - - ); - } + return ( + + + + ); + } - // SVG wrapping is needed for compability with sprite-loader - // So we removing width and height for internal component so only external one is specifying them + // SVG wrapping is needed for compability with sprite-loader + // So we removing width and height for internal component so only external one is specifying them - const IconComponent = data; - if (IconComponent.defaultProps) { - IconComponent.defaultProps.width = IconComponent.defaultProps.height = undefined; - } + const IconComponent = data; + if (IconComponent.defaultProps) { + IconComponent.defaultProps.width = IconComponent.defaultProps.height = undefined; + } - return ( - - - - ); -} + return ( + + + + ); + }, +); Icon.displayName = 'Icon'; Icon.prefix = '';