You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by kg...@apache.org on 2024/03/04 11:14:15 UTC

(superset) branch master updated: feat: Responsive UI for Big Number with Time Comparison (#27375)

This is an automated email from the ASF dual-hosted git repository.

kgabryje pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 5de2530e3c feat: Responsive UI for Big Number with Time Comparison (#27375)
5de2530e3c is described below

commit 5de2530e3cdc5799d08cfbad5871d24762a47ce4
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Mon Mar 4 12:14:09 2024 +0100

    feat: Responsive UI for Big Number with Time Comparison (#27375)
---
 .../BigNumber/BigNumberPeriodOverPeriod/PopKPI.tsx | 99 +++++++++++++++-------
 .../BigNumberPeriodOverPeriod/controlPanel.ts      |  8 +-
 .../useOverflowDetection.ts                        | 63 ++++++++++++++
 3 files changed, 138 insertions(+), 32 deletions(-)

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 15d20707c1..4615341dfb 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<PopKPIComparisonValueStyleProps>`
   ${({ 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<PopKPIComparisonSymbolStyleProps>`
+const SymbolWrapper = styled.span<PopKPIComparisonSymbolStyleProps>`
   ${({ 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<HTMLDivElement>();
   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 (
-    <div ref={rootElem} css={wrapperDivStyles}>
-      <div css={bigValueContainerStyles}>
-        {bigNumber}
-        {percentDifferenceNumber !== 0 && (
-          <span css={arrowIndicatorStyle}>
-            {percentDifferenceNumber > 0 ? '↑' : '↓'}
-          </span>
-        )}
-      </div>
-      <div
-        css={css`
-          width: 100%;
-          display: table;
-        `}
+    <div css={wrapperDivStyles} ref={wrapperRef}>
+      <NumbersContainer
+        css={
+          isOverflowing &&
+          css`
+            width: fit-content;
+            margin: auto;
+            align-items: flex-start;
+          `
+        }
       >
+        <div css={bigValueContainerStyles}>
+          {bigNumber}
+          {percentDifferenceNumber !== 0 && (
+            <span css={arrowIndicatorStyle}>
+              {percentDifferenceNumber > 0 ? '↑' : '↓'}
+            </span>
+          )}
+        </div>
+
         <div
-          css={css`
-            display: table-row;
-          `}
+          css={[
+            css`
+              display: flex;
+              justify-content: space-around;
+              gap: ${flexGap}px;
+              min-width: 0;
+              flex-shrink: 1;
+            `,
+            isOverflowing
+              ? css`
+                  flex-direction: column;
+                  align-items: flex-start;
+                  width: fit-content;
+                `
+              : css`
+                  align-items: center;
+                  width: 100%;
+                `,
+          ]}
+          ref={symbolContainerRef}
         >
           {SYMBOLS_WITH_VALUES.map((symbol_with_value, index) => (
             <ComparisonValue
@@ -182,7 +219,7 @@ export default function PopKPI(props: PopKPIProps) {
             </ComparisonValue>
           ))}
         </div>
-      </div>
+      </NumbersContainer>
     </div>
   );
 }
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 5e7fb8af2f..5ac80eaf96 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 0000000000..42cda225d3
--- /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<HTMLDivElement>(null);
+  const wrapperRef = useRef<HTMLDivElement>(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 };
+};