diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx index 15d20707c..4615341df 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { createRef, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { css, styled, t, useTheme } from '@superset-ui/core'; import { Tooltip } from '@superset-ui/chart-controls'; import { @@ -24,24 +24,33 @@ import { PopKPIComparisonValueStyleProps, PopKPIProps, } from './types'; +import { useOverflowDetection } from './useOverflowDetection'; + +const NumbersContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + width: 100%; + overflow: auto; +`; const ComparisonValue = styled.div` ${({ theme, subheaderFontSize }) => ` font-weight: ${theme.typography.weights.light}; - width: 33%; - display: table-cell; + display: flex; + justify-content: center; font-size: ${subheaderFontSize || 20}px; - text-align: center; + flex: 1 1 0px; `} `; -const SymbolWrapper = styled.div` +const SymbolWrapper = styled.span` ${({ theme, backgroundColor, textColor }) => ` background-color: ${backgroundColor}; color: ${textColor}; padding: ${theme.gridUnit}px ${theme.gridUnit * 2}px; border-radius: ${theme.gridUnit * 2}px; - display: inline-block; margin-right: ${theme.gridUnit}px; `} `; @@ -61,25 +70,23 @@ export default function PopKPI(props: PopKPIProps) { comparatorText, } = props; - const rootElem = createRef(); const theme = useTheme(); - + const flexGap = theme.gridUnit * 5; const wrapperDivStyles = css` font-family: ${theme.typography.families.sansSerif}; - position: relative; display: flex; - flex-direction: column; justify-content: center; - padding: ${theme.gridUnit * 4}px; - border-radius: ${theme.gridUnit * 2}px; + align-items: center; height: ${height}px; width: ${width}px; + overflow: auto; `; const bigValueContainerStyles = css` font-size: ${headerFontSize || 60}px; font-weight: ${theme.typography.weights.normal}; text-align: center; + margin-bottom: ${theme.gridUnit * 4}px; `; const getArrowIndicatorColor = () => { @@ -135,29 +142,59 @@ export default function PopKPI(props: PopKPIProps) { tooltipText: t('Percentage difference between the time periods'), }, ], - [prevNumber, valueDifference, percentDifferenceFormattedString], + [ + comparatorText, + prevNumber, + valueDifference, + percentDifferenceFormattedString, + ], ); + const { isOverflowing, symbolContainerRef, wrapperRef } = + useOverflowDetection(flexGap); + return ( -
-
- {bigNumber} - {percentDifferenceNumber !== 0 && ( - - {percentDifferenceNumber > 0 ? '↑' : '↓'} - - )} -
-
+ +
+ {bigNumber} + {percentDifferenceNumber !== 0 && ( + + {percentDifferenceNumber > 0 ? '↑' : '↓'} + + )} +
+
{SYMBOLS_WITH_VALUES.map((symbol_with_value, index) => ( ))}
-
+
); } diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanel.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanel.ts index 5e7fb8af2..5ac80eaf9 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanel.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/controlPanel.ts @@ -103,12 +103,18 @@ const config: ControlPanelConfig = { controlSetRows: [ ['y_axis_format'], ['currency_format'], - [headerFontSize], + [ + { + ...headerFontSize, + config: { ...headerFontSize.config, default: 0.2 }, + }, + ], [ { ...subheaderFontSize, config: { ...subheaderFontSize.config, + default: 0.125, label: t('Comparison font size'), }, }, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/useOverflowDetection.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/useOverflowDetection.ts new file mode 100644 index 000000000..42cda225d --- /dev/null +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberPeriodOverPeriod/useOverflowDetection.ts @@ -0,0 +1,63 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { useEffect, useRef, useState } from 'react'; +import { debounce } from 'lodash'; + +export const useOverflowDetection = (flexGap: number) => { + const symbolContainerRef = useRef(null); + const wrapperRef = useRef(null); + const [isOverflowing, setIsOverflowing] = useState(false); + + useEffect(() => { + let obs: ResizeObserver; + const symbolContainerElem = symbolContainerRef.current; + const wrapperElem = wrapperRef.current; + if (symbolContainerElem && wrapperElem) { + const symbolContainerChildrenElems = Array.from( + symbolContainerElem.children, + ); + obs = new ResizeObserver( + debounce(() => { + const totalChildrenWidth = symbolContainerChildrenElems.reduce( + (acc, element) => + // take symbol container's child's scroll width to account for the container growing with display: flex + acc + (element.firstElementChild?.scrollWidth ?? 0), + 0, + ); + if ( + totalChildrenWidth + + flexGap * Math.max(symbolContainerChildrenElems.length - 1, 0) > + wrapperElem.clientWidth + ) { + setIsOverflowing(true); + } else { + setIsOverflowing(false); + } + }, 500), + ); + obs.observe(document.body); + symbolContainerChildrenElems.forEach(elem => { + obs.observe(elem); + }); + } + return () => obs?.disconnect(); + }, [flexGap]); + + return { isOverflowing, symbolContainerRef, wrapperRef }; +};