From 92121a1030e63ea36cfce0442b89665d90eca46b Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Wed, 9 Jul 2025 11:24:21 +0100 Subject: [PATCH] Cascader: Fix icon not showing (#107869) * fix image path for angle-right * use proper icon utils --- .../src/components/Cascader/styles.ts | 233 +++++++++--------- .../grafana-ui/src/components/Icon/Icon.tsx | 6 +- .../grafana-ui/src/components/Icon/utils.ts | 6 + 3 files changed, 127 insertions(+), 118 deletions(-) diff --git a/packages/grafana-ui/src/components/Cascader/styles.ts b/packages/grafana-ui/src/components/Cascader/styles.ts index cef38b62dcc..68055527b24 100644 --- a/packages/grafana-ui/src/components/Cascader/styles.ts +++ b/packages/grafana-ui/src/components/Cascader/styles.ts @@ -2,6 +2,8 @@ import { css, keyframes } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; +import { getIconPath } from '../Icon/utils'; + const slideUpIn = keyframes({ '0%': { opacity: 0, @@ -58,143 +60,146 @@ const slideDownOut = keyframes({ }, }); -export const getCascaderStyles = (theme: GrafanaTheme2) => ({ - dropdown: css({ - '&.rc-cascader-dropdown': { - position: 'absolute', - // Required, otherwise the portal that the popup is shown in will render under other components - zIndex: 9999, - - '&-hidden': { - display: 'none', - }, - }, - '.rc-cascader': { - '&-menus': { - overflow: 'hidden', - background: theme.colors.background.elevated, - border: `none`, - borderRadius: theme.shape.radius.default, - boxShadow: theme.shadows.z3, - whiteSpace: 'nowrap', - - '&.slide-up-enter, &.slide-up-appear': { - animationDuration: '0.3s', - animationFillMode: 'both', - transformOrigin: '0 0', - opacity: 0, - animationTimingFunction: 'cubic-bezier(0.08, 0.82, 0.17, 1)', - animationPlayState: 'paused', +export const getCascaderStyles = (theme: GrafanaTheme2) => { + const iconPath = getIconPath('angle-right'); + return { + dropdown: css({ + '&.rc-cascader-dropdown': { + position: 'absolute', + // Required, otherwise the portal that the popup is shown in will render under other components + zIndex: 9999, + + '&-hidden': { + display: 'none', }, + }, + '.rc-cascader': { + '&-menus': { + overflow: 'hidden', + background: theme.colors.background.elevated, + border: `none`, + borderRadius: theme.shape.radius.default, + boxShadow: theme.shadows.z3, + whiteSpace: 'nowrap', - '&.slide-up-enter.slide-up-enter-active.rc-cascader-menus-placement, &.slide-up-appear.slide-up-appear-active.rc-cascader-menus-placement': - { - '&-bottomLeft': { - animationName: slideUpIn, - animationPlayState: 'running', - }, - - '&-topLeft': { - animationName: slideDownIn, - animationPlayState: 'running', - }, + '&.slide-up-enter, &.slide-up-appear': { + animationDuration: '0.3s', + animationFillMode: 'both', + transformOrigin: '0 0', + opacity: 0, + animationTimingFunction: 'cubic-bezier(0.08, 0.82, 0.17, 1)', + animationPlayState: 'paused', }, - '&.slide-up-leave': { - animationDuration: '0.3s', - animationFillMode: 'both', - transformOrigin: '0 0', - opacity: 1, - animationTimingFunction: 'cubic-bezier(0.6, 0.04, 0.98, 0.34)', - animationPlayState: 'paused', - - '&.slide-up-leave-active.rc-cascader-menus-placement': { - '&-bottomLeft': { - animationName: slideUpOut, - animationPlayState: 'running', + '&.slide-up-enter.slide-up-enter-active.rc-cascader-menus-placement, &.slide-up-appear.slide-up-appear-active.rc-cascader-menus-placement': + { + '&-bottomLeft': { + animationName: slideUpIn, + animationPlayState: 'running', + }, + + '&-topLeft': { + animationName: slideDownIn, + animationPlayState: 'running', + }, }, - '&-topLeft': { - animationName: slideDownOut, - animationPlayState: 'running', + '&.slide-up-leave': { + animationDuration: '0.3s', + animationFillMode: 'both', + transformOrigin: '0 0', + opacity: 1, + animationTimingFunction: 'cubic-bezier(0.6, 0.04, 0.98, 0.34)', + animationPlayState: 'paused', + + '&.slide-up-leave-active.rc-cascader-menus-placement': { + '&-bottomLeft': { + animationName: slideUpOut, + animationPlayState: 'running', + }, + + '&-topLeft': { + animationName: slideDownOut, + animationPlayState: 'running', + }, }, }, }, - }, - '&-menu': { - display: 'inline-block', - maxWidth: '50vw', - height: '192px', - listStyle: 'none', - margin: 0, - padding: theme.spacing(0.5), - borderRight: `1px solid ${theme.colors.border.weak}`, - overflow: 'auto', - - '&:last-child': { - borderRight: 0, - }, - - '&-item': { - height: theme.spacing(4), - lineHeight: theme.spacing(4), - padding: theme.spacing(0, 4, 0, 2), - borderRadius: theme.shape.radius.default, - cursor: 'pointer', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - transition: 'all 0.3s ease', - position: 'relative', - - '&:hover': { - background: theme.colors.action.hover, + '&-menu': { + display: 'inline-block', + maxWidth: '50vw', + height: '192px', + listStyle: 'none', + margin: 0, + padding: theme.spacing(0.5), + borderRight: `1px solid ${theme.colors.border.weak}`, + overflow: 'auto', + + '&:last-child': { + borderRight: 0, }, - '&-disabled': { - cursor: 'not-allowed', - color: theme.colors.text.disabled, + '&-item': { + height: theme.spacing(4), + lineHeight: theme.spacing(4), + padding: theme.spacing(0, 4, 0, 2), + borderRadius: theme.shape.radius.default, + cursor: 'pointer', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + transition: 'all 0.3s ease', + position: 'relative', '&:hover': { - background: 'transparent', + background: theme.colors.action.hover, }, - '&:after': { - position: 'absolute', - right: '12px', - content: "'loading'", + '&-disabled': { + cursor: 'not-allowed', color: theme.colors.text.disabled, - fontStyle: 'italic', + + '&:hover': { + background: 'transparent', + }, + + '&:after': { + position: 'absolute', + right: '12px', + content: "'loading'", + color: theme.colors.text.disabled, + fontStyle: 'italic', + }, }, - }, - '&-active': { - color: theme.colors.text.maxContrast, - background: theme.colors.background.secondary, + '&-active': { + color: theme.colors.text.maxContrast, + background: theme.colors.background.secondary, - '&:hover': { - background: theme.colors.action.hover, + '&:hover': { + background: theme.colors.action.hover, + }, }, - }, - '&-expand': { - position: 'relative', - - '&:after': { - background: theme.colors.background.secondary, - content: "''", - height: theme.spacing(3), - mask: 'url(../img/icons/unicons/angle-right.svg)', - maskType: 'luminance', - position: 'absolute', - right: 0, - top: theme.spacing(0.5), - width: theme.spacing(3), + '&-expand': { + position: 'relative', + + '&:after': { + background: theme.colors.text.primary, + content: "''", + height: theme.spacing(3), + mask: `url(${iconPath})`, + maskType: 'luminance', + position: 'absolute', + right: 0, + top: theme.spacing(0.5), + width: theme.spacing(3), + }, }, }, }, }, - }, - }), -}); + }), + }; +}; diff --git a/packages/grafana-ui/src/components/Icon/Icon.tsx b/packages/grafana-ui/src/components/Icon/Icon.tsx index f961cafcc14..59135b9a89f 100644 --- a/packages/grafana-ui/src/components/Icon/Icon.tsx +++ b/packages/grafana-ui/src/components/Icon/Icon.tsx @@ -8,7 +8,7 @@ import { useStyles2 } from '../../themes/ThemeContext'; import { IconName, IconType, IconSize } from '../../types/icon'; import { spin } from '../../utils/keyframes'; -import { getIconRoot, getIconSubDir, getSvgSize } from './utils'; +import { getIconPath, getSvgSize } from './utils'; export interface IconProps extends Omit, 'onLoad' | 'onError' | 'ref'> { name: IconName; @@ -53,12 +53,10 @@ export const Icon = React.forwardRef( // handle the deprecated 'fa fa-spinner' const iconName: IconName = name === 'fa fa-spinner' ? 'spinner' : name; - const iconRoot = getIconRoot(); const svgSize = getSvgSize(size); const svgHgt = svgSize; const svgWid = name.startsWith('gf-bar-align') ? 16 : name.startsWith('gf-interp') ? 30 : svgSize; - const subDir = getIconSubDir(iconName, type); - const svgPath = `${iconRoot}${subDir}/${iconName}.svg`; + const svgPath = getIconPath(iconName, type); const composedClassName = cx( styles.icon, diff --git a/packages/grafana-ui/src/components/Icon/utils.ts b/packages/grafana-ui/src/components/Icon/utils.ts index 263e68ac0ff..30f350ee667 100644 --- a/packages/grafana-ui/src/components/Icon/utils.ts +++ b/packages/grafana-ui/src/components/Icon/utils.ts @@ -60,3 +60,9 @@ export function getIconRoot(): string { return iconRoot; } + +export function getIconPath(name: IconName, type: IconType = 'default'): string { + const iconRoot = getIconRoot(); + const subDir = getIconSubDir(name, type); + return `${iconRoot}${subDir}/${name}.svg`; +}