You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by li...@apache.org on 2024/03/08 19:33:59 UTC

(superset) 01/04: feat(time-comparison-table): implement comparison columns dropdown

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

lilykuang pushed a commit to branch table-time-compare-hide-show
in repository https://gitbox.apache.org/repos/asf/superset.git

commit cc5bdc1ad5351eccde8294996a54be35a8bbe614
Author: lilykuang <ji...@gmail.com>
AuthorDate: Fri Mar 8 09:04:55 2024 -0800

    feat(time-comparison-table): implement comparison columns dropdown
---
 .../src/{index.ts => components/Dropdown.tsx}      |  20 +--
 .../src/{index.ts => components/Menu.tsx}          |  20 +--
 .../superset-ui-chart-controls/src/index.ts        |   4 +-
 .../plugins/plugin-chart-table/package.json        |   1 +
 .../plugin-chart-table/src/DataTable/DataTable.tsx |  14 ++-
 .../plugins/plugin-chart-table/src/TableChart.tsx  | 134 +++++++++++++++++++--
 6 files changed, 146 insertions(+), 47 deletions(-)

diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/components/Dropdown.tsx
similarity index 56%
copy from superset-frontend/packages/superset-ui-chart-controls/src/index.ts
copy to superset-frontend/packages/superset-ui-chart-controls/src/components/Dropdown.tsx
index 4e00929119..032365ebd5 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/index.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/components/Dropdown.tsx
@@ -16,22 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import * as sectionsModule from './sections';
 
-export * from './utils';
-export * from './constants';
-export * from './operators';
-
-// can't do `export * as sections from './sections'`, babel-transformer will fail
-export const sections = sectionsModule;
-
-export * from './components/InfoTooltipWithTrigger';
-export * from './components/ColumnOption';
-export * from './components/ColumnTypeLabel/ColumnTypeLabel';
-export * from './components/MetricOption';
-export * from './components/ControlSubSectionHeader';
-export * from './components/Tooltip';
-
-export * from './shared-controls';
-export * from './types';
-export * from './fixtures';
+export { Dropdown } from 'antd';
+export type { DropDownProps } from 'antd/lib/dropdown';
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/components/Menu.tsx
similarity index 56%
copy from superset-frontend/packages/superset-ui-chart-controls/src/index.ts
copy to superset-frontend/packages/superset-ui-chart-controls/src/components/Menu.tsx
index 4e00929119..89a7405cde 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/index.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/components/Menu.tsx
@@ -16,22 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import * as sectionsModule from './sections';
 
-export * from './utils';
-export * from './constants';
-export * from './operators';
-
-// can't do `export * as sections from './sections'`, babel-transformer will fail
-export const sections = sectionsModule;
-
-export * from './components/InfoTooltipWithTrigger';
-export * from './components/ColumnOption';
-export * from './components/ColumnTypeLabel/ColumnTypeLabel';
-export * from './components/MetricOption';
-export * from './components/ControlSubSectionHeader';
-export * from './components/Tooltip';
-
-export * from './shared-controls';
-export * from './types';
-export * from './fixtures';
+export { Menu } from 'antd';
+export type { MenuProps } from 'antd/lib/menu';
diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/index.ts
index 4e00929119..fed32cae3a 100644
--- a/superset-frontend/packages/superset-ui-chart-controls/src/index.ts
+++ b/superset-frontend/packages/superset-ui-chart-controls/src/index.ts
@@ -28,8 +28,10 @@ export const sections = sectionsModule;
 export * from './components/InfoTooltipWithTrigger';
 export * from './components/ColumnOption';
 export * from './components/ColumnTypeLabel/ColumnTypeLabel';
-export * from './components/MetricOption';
 export * from './components/ControlSubSectionHeader';
+export * from './components/Dropdown';
+export * from './components/Menu';
+export * from './components/MetricOption';
 export * from './components/Tooltip';
 
 export * from './shared-controls';
diff --git a/superset-frontend/plugins/plugin-chart-table/package.json b/superset-frontend/plugins/plugin-chart-table/package.json
index 6df1ceb33e..d43e33808e 100644
--- a/superset-frontend/plugins/plugin-chart-table/package.json
+++ b/superset-frontend/plugins/plugin-chart-table/package.json
@@ -37,6 +37,7 @@
     "xss": "^1.0.14"
   },
   "peerDependencies": {
+    "@ant-design/icons": "^5.0.1",
     "@superset-ui/chart-controls": "*",
     "@superset-ui/core": "*",
     "@testing-library/dom": "^7.29.4",
diff --git a/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx b/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
index 79ab44981e..18c7ac6214 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/DataTable/DataTable.tsx
@@ -68,6 +68,7 @@ export interface DataTableProps<D extends object> extends TableOptions<D> {
   wrapperRef?: MutableRefObject<HTMLDivElement>;
   onColumnOrderChange: () => void;
   renderGroupingHeaders?: () => JSX.Element;
+  renderTimeComparisonDropdown?: () => JSX.Element;
 }
 
 export interface RenderHTMLCellProps extends HTMLProps<HTMLTableCellElement> {
@@ -101,6 +102,7 @@ export default typedMemo(function DataTable<D extends object>({
   wrapperRef: userWrapperRef,
   onColumnOrderChange,
   renderGroupingHeaders,
+  renderTimeComparisonDropdown,
   ...moreUseTableOptions
 }: DataTableProps<D>): JSX.Element {
   const tableHooks: PluginHook<D>[] = [
@@ -117,7 +119,8 @@ export default typedMemo(function DataTable<D extends object>({
   const sortByRef = useRef([]); // cache initial `sortby` so sorting doesn't trigger page reset
   const pageSizeRef = useRef([initialPageSize, resultsSize]);
   const hasPagination = initialPageSize > 0 && resultsSize > 0; // pageSize == 0 means no pagination
-  const hasGlobalControl = hasPagination || !!searchInput;
+  const hasGlobalControl =
+    hasPagination || !!searchInput || renderTimeComparisonDropdown;
   const initialState = {
     ...initialState_,
     // zero length means all pages, the `usePagination` plugin does not
@@ -359,7 +362,7 @@ export default typedMemo(function DataTable<D extends object>({
       {hasGlobalControl ? (
         <div ref={globalControlRef} className="form-inline dt-controls">
           <div className="row">
-            <div className="col-sm-6">
+            <div className="col-sm-5">
               {hasPagination ? (
                 <SelectPageSize
                   total={resultsSize}
@@ -375,7 +378,7 @@ export default typedMemo(function DataTable<D extends object>({
               ) : null}
             </div>
             {searchInput ? (
-              <div className="col-sm-6">
+              <div className="col-sm-5">
                 <GlobalFilter<D>
                   searchInput={
                     typeof searchInput === 'boolean' ? undefined : searchInput
@@ -386,6 +389,11 @@ export default typedMemo(function DataTable<D extends object>({
                 />
               </div>
             ) : null}
+            {renderTimeComparisonDropdown ? (
+              <div className="col-sm-2" style={{ float: 'right' }}>
+                {renderTimeComparisonDropdown()}
+              </div>
+            ) : null}
           </div>
         </div>
       ) : null}
diff --git a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
index ab75914c97..7034262efc 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
+++ b/superset-frontend/plugins/plugin-chart-table/src/TableChart.tsx
@@ -50,6 +50,8 @@ import {
   tn,
   useTheme,
 } from '@superset-ui/core';
+import { Dropdown, Menu } from '@superset-ui/chart-controls';
+import { CheckOutlined, DownOutlined } from '@ant-design/icons';
 
 import { isEmpty } from 'lodash';
 import { DataColumnMeta, TableChartTransformedProps } from './types';
@@ -242,6 +244,12 @@ export default function TableChart<D extends DataRecord = DataRecord>(
     emitCrossFilters,
     enableTimeComparison,
   } = props;
+  const comparisonColumns = [
+    { key: 'all', label: t('Display all') },
+    { key: '#', label: '#' },
+    { key: '△', label: '△' },
+    { key: '%', label: '%' },
+  ];
   const timestampFormatter = useCallback(
     value => getTimeFormatterForGranularity(timeGrain)(value),
     [timeGrain],
@@ -252,6 +260,9 @@ export default function TableChart<D extends DataRecord = DataRecord>(
   });
   // keep track of whether column order changed, so that column widths can too
   const [columnOrderToggle, setColumnOrderToggle] = useState(false);
+  const [showTimeComparisonDropdown, setShowTimeComparisonDropdown] =
+    useState(false);
+  const [selectedKeys, setSelectedKeys] = useState([comparisonColumns[0].key]);
   const theme = useTheme();
 
   // only take relevant page size options
@@ -371,6 +382,33 @@ export default function TableChart<D extends DataRecord = DataRecord>(
     };
   };
 
+  const comparisonLabels = [t('Main'), '#', '△', '%'];
+  const filteredColumnsMeta = useMemo(() => {
+    if (!enableTimeComparison) {
+      return columnsMeta;
+    }
+    const allColumns = comparisonColumns[0].key;
+    const isAllColumnsKeySelected = selectedKeys.includes(allColumns);
+    const mainLabel = comparisonLabels[0];
+
+    return columnsMeta.filter(col => {
+      const { label } = col;
+
+      return (
+        label === mainLabel ||
+        !comparisonLabels.includes(label) ||
+        isAllColumnsKeySelected ||
+        selectedKeys.includes(label)
+      );
+    });
+  }, [
+    columnsMeta,
+    comparisonColumns,
+    comparisonLabels,
+    selectedKeys,
+    enableTimeComparison,
+  ]);
+
   const handleContextMenu =
     onContextMenu && !isRawRecords
       ? (
@@ -384,7 +422,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
           clientY: number,
         ) => {
           const drillToDetailFilters: BinaryQueryObjectFilterClause[] = [];
-          columnsMeta.forEach(col => {
+          filteredColumnsMeta.forEach(col => {
             if (!col.isMetric) {
               const dataRecordValue = value[col.key];
               drillToDetailFilters.push({
@@ -416,8 +454,6 @@ export default function TableChart<D extends DataRecord = DataRecord>(
         }
       : undefined;
 
-  const comparisonLabels = [t('Main'), '#', '△', '%'];
-
   const getHeaderColumns = (
     columnsMeta: DataColumnMeta[],
     enableTimeComparison?: boolean,
@@ -447,6 +483,87 @@ export default function TableChart<D extends DataRecord = DataRecord>(
     return resultMap;
   };
 
+  const renderTimeComparisonDropdown = (): JSX.Element => {
+    const allKey = comparisonColumns[0].key;
+    const handleOnClick = (data: any) => {
+      const { key } = data;
+      // Toggle 'All' key selection
+      if (key === allKey) {
+        setSelectedKeys([allKey]);
+      } else if (selectedKeys.includes(allKey)) {
+        setSelectedKeys([key]);
+      } else {
+        // Toggle selection for other keys
+        setSelectedKeys(
+          selectedKeys.includes(key)
+            ? selectedKeys.filter(k => k !== key) // Deselect if already selected
+            : [...selectedKeys, key],
+        ); // Select if not already selected
+      }
+    };
+
+    const handleOnBlur = () => {
+      if (selectedKeys.length === 3) {
+        setSelectedKeys([comparisonColumns[0].key]);
+      }
+    };
+
+    return (
+      <Dropdown
+        placement="bottomRight"
+        visible={showTimeComparisonDropdown}
+        onVisibleChange={(flag: boolean) => {
+          setShowTimeComparisonDropdown(flag);
+        }}
+        overlay={
+          <Menu
+            multiple
+            onClick={handleOnClick}
+            onBlur={handleOnBlur}
+            selectedKeys={selectedKeys}
+          >
+            <div
+              css={css`
+                max-width: 242px;
+                padding: 0 ${theme.gridUnit * 2}px;
+                color: ${theme.colors.grayscale.base};
+                font-size: ${theme.typography.sizes.s}px;
+              `}
+            >
+              {t(
+                'Select columns that will be displayed in the table. You can multiselect columns.',
+              )}
+            </div>
+            {comparisonColumns.map(column => (
+              <Menu.Item key={column.key}>
+                <span
+                  css={css`
+                    color: ${theme.colors.grayscale.dark2};
+                  `}
+                >
+                  {column.label}
+                </span>
+                <span
+                  css={css`
+                    float: right;
+                    font-size: ${theme.typography.sizes.s}px;
+                  `}
+                >
+                  {selectedKeys.includes(column.key) && <CheckOutlined />}
+                </span>
+              </Menu.Item>
+            ))}
+          </Menu>
+        }
+        trigger={['click']}
+      >
+        <span>
+          {t('Display columns')} <DownOutlined />
+        </span>
+      </Dropdown>
+    );
+  };
+
   const renderGroupingHeaders = (): JSX.Element => {
     // TODO: Make use of ColumnGroup to render the aditional headers
     const headers: any = [];
@@ -499,8 +616,8 @@ export default function TableChart<D extends DataRecord = DataRecord>(
   };
 
   const groupHeaderColumns = useMemo(
-    () => getHeaderColumns(columnsMeta, enableTimeComparison),
-    [columnsMeta, enableTimeComparison],
+    () => getHeaderColumns(filteredColumnsMeta, enableTimeComparison),
+    [filteredColumnsMeta, enableTimeComparison],
   );
 
   const getColumnConfigs = useCallback(
@@ -769,8 +886,8 @@ export default function TableChart<D extends DataRecord = DataRecord>(
   );
 
   const columns = useMemo(
-    () => columnsMeta.map(getColumnConfigs),
-    [columnsMeta, getColumnConfigs],
+    () => filteredColumnsMeta.map(getColumnConfigs),
+    [filteredColumnsMeta, getColumnConfigs],
   );
 
   const handleServerPaginationChange = useCallback(
@@ -840,6 +957,9 @@ export default function TableChart<D extends DataRecord = DataRecord>(
         renderGroupingHeaders={
           !isEmpty(groupHeaderColumns) ? renderGroupingHeaders : undefined
         }
+        renderTimeComparisonDropdown={
+          enableTimeComparison ? renderTimeComparisonDropdown : undefined
+        }
       />
     </Styles>
   );