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>
);