From c87a74841ea1ad05e4041748cfed3a45749e1085 Mon Sep 17 00:00:00 2001 From: Conglei Shi Date: Wed, 22 May 2019 20:09:35 -0700 Subject: [PATCH] feat(code refactoring): code refactoring --- .../src/Table.tsx | 164 ++++-------------- .../src/legacy/transformProps.ts | 45 ++++- .../src/renderer.tsx | 106 +++++++++++ 3 files changed, 175 insertions(+), 140 deletions(-) create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/renderer.tsx diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/Table.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/Table.tsx index dcdd6c3fb6..8a3b041a14 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/Table.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/Table.tsx @@ -1,17 +1,7 @@ -import React, { CSSProperties } from 'react'; +import React from 'react'; import DataTable from '@airbnb/lunar/lib/components/DataTable'; -import { Renderers, RendererProps } from '@airbnb/lunar/lib/components/DataTable/types'; -import { HEIGHT_TO_PX } from '@airbnb/lunar/lib/components/DataTable/constants'; -import { FormDataMetric, Metric } from '@superset-ui/chart'; - -type ColumnType = { - key: string; - label: string; - format?: (value: any) => string; - type: 'metric' | 'string'; - maxValue?: number; - minValue?: number; -}; +import { Renderers } from '@airbnb/lunar/lib/components/DataTable/types'; +import { getRenderer, ColumnType, heightType, Cell } from './renderer'; type Props = { data: any[]; @@ -23,15 +13,9 @@ type Props = { [key: string]: any[]; }; includeSearch?: boolean; - metrics: FormDataMetric[]; onAddFilter?: (key: string, value: number[]) => void; onRemoveFilter?: (key: string, value: number[]) => void; - orderDesc: boolean; - pageLength: number | string; - percentMetrics: string[]; tableFilter: boolean; - tableTimestampFormat: string; - timeseriesLimitMetric: FormDataMetric; }; function NOOP(key: string, value: []) {} @@ -47,17 +31,13 @@ const defaultProps = { export type TableProps = Props & Readonly; -type Cell = { - key: string; - value: any; -}; - type TableState = { selectedCells: Set; }; -const NEGATIVE_COLOR = '#ff8787'; -const POSITIVE_COLOR = '#ced4da'; +function getCellHash(cell: Cell) { + return `${cell.key}#${cell.value}`; +} class TableVis extends React.Component { static defaultProps = defaultProps; @@ -77,7 +57,7 @@ class TableVis extends React.Component { return; } const newSelectedCells = new Set(Array.from(selectedCells)); - const cellHash = `${cell.key}#${cell.value}`; + const cellHash = getCellHash(cell); if (newSelectedCells.has(cellHash)) { newSelectedCells.delete(cellHash); onRemoveFilter(cell.key, [cell.value]); @@ -90,6 +70,11 @@ class TableVis extends React.Component { }); }; + isSelected = (cell: Cell) => { + const { selectedCells } = this.state; + return selectedCells.has(getCellHash(cell)); + }; + static getDerivedStateFromProps: React.GetDerivedStateFromProps = ( props: TableProps, state: TableState, @@ -99,7 +84,12 @@ class TableVis extends React.Component { const newSelectedCells = new Set(Array.from(selectedCells)); Object.keys(filters).forEach(key => { filters[key].forEach(value => { - newSelectedCells.add(`${key}#${value}`); + newSelectedCells.add( + getCellHash({ + key, + value, + }), + ); }); }); return { @@ -110,122 +100,30 @@ class TableVis extends React.Component { render() { const { - metrics, - timeseriesLimitMetric, - orderDesc, data, + columns, alignPositiveNegative, colorPositiveNegative, - columns, + height, tableFilter, } = this.props; - const { selectedCells } = this.state; - - const sortByKey = - timeseriesLimitMetric && - ((timeseriesLimitMetric as Metric).label || (timeseriesLimitMetric as string)); - - let formattedData = data.map(row => ({ - data: row, - })); - - if (sortByKey) { - formattedData = formattedData.sort((a, b) => { - const delta = a.data[sortByKey] - b.data[sortByKey]; - if (orderDesc) { - return -delta; - } - return delta; - }); - if (metrics.indexOf(sortByKey) < 0) { - formattedData = formattedData.map(row => { - const data = { ...row.data }; - delete data[sortByKey]; - return { - data, - }; - }); - } - } - - const heightType = 'small'; - const getRenderer = (column: ColumnType) => (props: RendererProps) => { - const { key } = props; - let value = props.row.rowData.data[key]; - const cellHash = `${key}#${value}`; - const isMetric = metrics.indexOf(key) >= 0; - let Parent; - - if (isMetric) { - let left = 0; - let width = 0; - if (alignPositiveNegative) { - width = Math.abs( - Math.round((value / Math.max(column.maxValue!, Math.abs(column.minValue!))) * 100), - ); - } else { - const posExtent = Math.abs(Math.max(column.maxValue!, 0)); - const negExtent = Math.abs(Math.min(column.minValue!, 0)); - const tot = posExtent + negExtent; - left = Math.round((Math.min(negExtent + value, negExtent) / tot) * 100); - width = Math.round((Math.abs(value) / tot) * 100); - } - const color = colorPositiveNegative && value < 0 ? NEGATIVE_COLOR : POSITIVE_COLOR; - Parent = ({ children }: { children: React.ReactNode }) => { - const boxStyle: CSSProperties = { - margin: '0px -16px', - backgroundColor: tableFilter && selectedCells.has(cellHash) ? '#ffec99' : undefined, - }; - const boxContainerStyle: CSSProperties = { - position: 'relative', - height: HEIGHT_TO_PX[heightType], - textAlign: isMetric ? 'right' : 'left', - display: 'flex', - alignItems: 'center', - margin: '0px 16px', - }; - const barStyle: CSSProperties = { - background: color, - width: `${width}%`, - left: `${left}%`, - position: 'absolute', - height: 24, - }; - - return ( -
-
-
-
{children}
-
-
- ); - }; - } else { - Parent = ({ children }: { children: React.ReactNode }) => ( - {children} - ); - } - - return ( -
- {column.format ? column.format(value) : value} -
- ); - }; const renderers: Renderers = {}; columns.forEach(column => { - renderers[column.key] = getRenderer(column); + renderers[column.key] = getRenderer({ + column, + alignPositiveNegative, + colorPositiveNegative, + enableFilter: tableFilter, + isSelected: this.isSelected, + handleCellSelected: this.handleCellSelected, + }); }); - return ; + return ( + + ); } } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/legacy/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/legacy/transformProps.ts index 72f36d143d..c8385ba523 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/legacy/transformProps.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/legacy/transformProps.ts @@ -22,6 +22,12 @@ import { ChartProps, FormDataMetric, Metric } from '@superset-ui/chart'; import { getNumberFormatter, NumberFormats, NumberFormatter } from '@superset-ui/number-format'; import { getTimeFormatter, TimeFormatter } from '@superset-ui/time-format'; +const DTTM_ALIAS = '__timestamp'; + +type PlainObject = { + [key: string]: any; +}; + export default function transformProps(chartProps: ChartProps) { const { height, datasource, filters, formData, onAddFilter, payload } = chartProps; const { @@ -47,9 +53,38 @@ export default function transformProps(chartProps: ChartProps) { .filter(m => typeof records[0][m as string] === 'number'); const dataArray: { - [key: string]: any; + [key: string]: any[]; } = {}; + const sortByKey = + timeseriesLimitMetric && + ((timeseriesLimitMetric as Metric).label || (timeseriesLimitMetric as string)); + + let formattedData: { + data: PlainObject; + }[] = records.map((row: PlainObject) => ({ + data: row, + })); + + if (sortByKey) { + formattedData = formattedData.sort((a, b) => { + const delta = a.data[sortByKey] - b.data[sortByKey]; + if (orderDesc) { + return -delta; + } + return delta; + }); + if (metrics.indexOf(sortByKey) < 0) { + formattedData = formattedData.map(row => { + const data = { ...row.data }; + delete data[sortByKey]; + return { + data, + }; + }); + } + } + metrics.forEach(metric => { const arr = []; for (let i = 0; i < records.length; i += 1) { @@ -91,7 +126,7 @@ export default function transformProps(chartProps: ChartProps) { } } - if (key === '__timestamp') { + if (key === DTTM_ALIAS) { formatFunction = tsFormatter; } @@ -116,19 +151,15 @@ export default function transformProps(chartProps: ChartProps) { return { height, - data: records, + data: formattedData, alignPositiveNegative: alignPn, colorPositiveNegative: colorPn, columns: processedColumns, filters, includeSearch, - metrics, onAddFilter, orderDesc, pageLength: pageLength && parseInt(pageLength, 10), - percentMetrics, tableFilter, - tableTimestampFormat, - timeseriesLimitMetric, }; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/renderer.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/renderer.tsx new file mode 100644 index 0000000000..f19095dd8d --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugin-chart-table/src/renderer.tsx @@ -0,0 +1,106 @@ +import React, { CSSProperties } from 'react'; +import { HEIGHT_TO_PX } from '@airbnb/lunar/lib/components/DataTable/constants'; +import { RendererProps } from '@airbnb/lunar/lib/components/DataTable/types'; + +const NEGATIVE_COLOR = '#ff8787'; +const POSITIVE_COLOR = '#ced4da'; +const SELECTION_COLOR = '#ffec99'; + +export const heightType = 'micro'; + +export type ColumnType = { + key: string; + label: string; + format?: (value: any) => string; + type: 'metric' | 'string'; + maxValue?: number; + minValue?: number; +}; + +export type Cell = { + key: string; + value: any; +}; + +export const getRenderer = ({ + column, + alignPositiveNegative, + colorPositiveNegative, + enableFilter, + isSelected, + handleCellSelected, +}: { + column: ColumnType; + alignPositiveNegative: boolean; + colorPositiveNegative: boolean; + enableFilter: boolean; + isSelected: (cell: Cell) => boolean; + handleCellSelected: (cell: Cell) => any; +}) => (props: RendererProps) => { + const { key } = props; + let value = props.row.rowData.data[key]; + const isMetric = column.type === 'metric'; + let Parent; + + if (isMetric) { + let left = 0; + let width = 0; + if (alignPositiveNegative) { + width = Math.abs( + Math.round((value / Math.max(column.maxValue!, Math.abs(column.minValue!))) * 100), + ); + } else { + const posExtent = Math.abs(Math.max(column.maxValue!, 0)); + const negExtent = Math.abs(Math.min(column.minValue!, 0)); + const tot = posExtent + negExtent; + left = Math.round((Math.min(negExtent + value, negExtent) / tot) * 100); + width = Math.round((Math.abs(value) / tot) * 100); + } + const color = colorPositiveNegative && value < 0 ? NEGATIVE_COLOR : POSITIVE_COLOR; + Parent = ({ children }: { children: React.ReactNode }) => { + const boxStyle: CSSProperties = { + margin: '0px -16px', + backgroundColor: enableFilter && isSelected({ key, value }) ? SELECTION_COLOR : undefined, + }; + const boxContainerStyle: CSSProperties = { + position: 'relative', + height: HEIGHT_TO_PX[heightType], + textAlign: isMetric ? 'right' : 'left', + display: 'flex', + alignItems: 'center', + margin: '0px 16px', + }; + const barStyle: CSSProperties = { + background: color, + width: `${width}%`, + left: `${left}%`, + position: 'absolute', + height: HEIGHT_TO_PX[heightType] / 2, + }; + + return ( +
+
+
+
{children}
+
+
+ ); + }; + } else { + Parent = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + } + + return ( +
+ {column.format ? column.format(value) : value} +
+ ); +};