forked from apache/superset
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(viz): Pivot table chart POC (apache#1023)
* feat(plugin-chart-pivot-table): add new plugin * Implement pivot table chart * Toggle display of grand totals * Update table viz name * Minor changes * Update types * Implement transpose pivot * Keep the original order of metrics when sorting * Use D3 value formatting * Fix type error * Explicitly cast payload to JsonObject to fix type error * Fix tests * Update react-pivottable dependency * Solve merge conflicts * Change thumbnail * Replace console logs with TODO comments * Implement z-a sorting * Update README Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com>
- Loading branch information
1 parent
7e1fd82
commit be04f6f
Showing
17 changed files
with
5,207 additions
and
4,222 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
...nd/temporary_superset_ui/superset-ui/plugins/plugin-chart-pivot-table/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
## @superset-ui/plugin-chart-pivot-table | ||
|
||
[![Version](https://img.shields.io/npm/v/@superset-ui/plugin-chart-pivot-table.svg?style=flat-square)](https://www.npmjs.com/package/@superset-ui/plugin-chart-pivot-table) | ||
|
||
This plugin provides Pivot Table for Superset. | ||
|
||
### Usage | ||
|
||
Configure `key`, which can be any `string`, and register the plugin. This `key` will be used to | ||
lookup this chart throughout the app. | ||
|
||
```js | ||
import PivotTableChartPlugin from '@superset-ui/plugin-chart-pivot-table'; | ||
|
||
new PivotTableChartPlugin().configure({ key: 'pivot-table-v2' }).register(); | ||
``` | ||
|
||
Then use it via `SuperChart`. See | ||
[storybook](https://apache-superset.github.io/superset-ui/?selectedKind=plugin-chart-pivot-table) | ||
for more details. | ||
|
||
```js | ||
<SuperChart | ||
chartType="pivot-table-v2" | ||
width={600} | ||
height={600} | ||
formData={...} | ||
queriesData={[{ | ||
data: {...}, | ||
}]} | ||
/> | ||
``` | ||
|
||
### File structure generated | ||
|
||
``` | ||
├── package.json | ||
├── README.md | ||
├── tsconfig.json | ||
├── src | ||
│ ├── PivotTableChart.tsx | ||
│ ├── images | ||
│ │ └── thumbnail.png | ||
│ ├── index.ts | ||
│ ├── plugin | ||
│ │ ├── buildQuery.ts | ||
│ │ ├── controlPanel.ts | ||
│ │ ├── index.ts | ||
│ │ └── transformProps.ts | ||
│ └── types.ts | ||
├── test | ||
│ └── index.test.ts | ||
└── types | ||
└── external.d.ts | ||
``` |
41 changes: 41 additions & 0 deletions
41
...-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-pivot-table/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"name": "@superset-ui/plugin-chart-pivot-table", | ||
"version": "0.0.0", | ||
"description": "Superset Chart - Pivot Table", | ||
"sideEffects": false, | ||
"main": "lib/index.js", | ||
"module": "esm/index.js", | ||
"files": [ | ||
"esm", | ||
"lib" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/apache-superset/superset-ui.git" | ||
}, | ||
"keywords": [ | ||
"superset" | ||
], | ||
"author": "Superset", | ||
"license": "Apache-2.0", | ||
"bugs": { | ||
"url": "https://github.com/apache-superset/superset-ui/issues" | ||
}, | ||
"homepage": "https://github.com/apache-superset/superset-ui#readme", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"dependencies": { | ||
"@superset-ui/chart-controls": "0.17.30", | ||
"@superset-ui/core": "0.17.30", | ||
"@superset-ui/react-pivottable": "^0.12.5" | ||
}, | ||
"peerDependencies": { | ||
"react": "^16.13.1" | ||
}, | ||
"devDependencies": { | ||
"@babel/types": "^7.13.12", | ||
"@types/jest": "^26.0.0", | ||
"jest": "^26.0.1" | ||
} | ||
} |
161 changes: 161 additions & 0 deletions
161
...emporary_superset_ui/superset-ui/plugins/plugin-chart-pivot-table/src/PivotTableChart.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/** | ||
* 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 React from 'react'; | ||
import { styled, AdhocMetric, getNumberFormatter } from '@superset-ui/core'; | ||
// @ts-ignore | ||
import PivotTable from '@superset-ui/react-pivottable/PivotTable'; | ||
// @ts-ignore | ||
import { sortAs, aggregatorTemplates } from '@superset-ui/react-pivottable/Utilities'; | ||
import '@superset-ui/react-pivottable/pivottable.css'; | ||
import { PivotTableProps, PivotTableStylesProps } from './types'; | ||
|
||
const Styles = styled.div<PivotTableStylesProps>` | ||
padding: ${({ theme }) => theme.gridUnit * 4}px; | ||
height: ${({ height }) => height}px; | ||
width: ${({ width }) => width}px; | ||
overflow-y: scroll; | ||
} | ||
`; | ||
|
||
// TODO: remove eslint-disable when click callbacks are implemented | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
const clickCellCallback = ( | ||
e: MouseEvent, | ||
value: number, | ||
filters: Record<string, any>, | ||
pivotData: Record<string, any>, | ||
) => { | ||
// TODO: Implement a callback | ||
}; | ||
|
||
const clickColumnHeaderCallback = ( | ||
e: MouseEvent, | ||
value: string, | ||
filters: Record<string, any>, | ||
pivotData: Record<string, any>, | ||
isSubtotal: boolean, | ||
isGrandTotal: boolean, | ||
) => { | ||
// TODO: Implement a callback | ||
}; | ||
|
||
const clickRowHeaderCallback = ( | ||
e: MouseEvent, | ||
value: string, | ||
filters: Record<string, any>, | ||
pivotData: Record<string, any>, | ||
isSubtotal: boolean, | ||
isGrandTotal: boolean, | ||
) => { | ||
// TODO: Implement a callback | ||
}; | ||
|
||
export default function PivotTableChart(props: PivotTableProps) { | ||
const { | ||
data, | ||
height, | ||
width, | ||
groupbyRows, | ||
groupbyColumns, | ||
metrics, | ||
tableRenderer, | ||
colOrder, | ||
rowOrder, | ||
aggregateFunction, | ||
transposePivot, | ||
rowSubtotalPosition, | ||
colSubtotalPosition, | ||
colTotals, | ||
rowTotals, | ||
valueFormat, | ||
} = props; | ||
|
||
const adaptiveFormatter = getNumberFormatter(valueFormat); | ||
|
||
const aggregators = (tpl => ({ | ||
Count: tpl.count(adaptiveFormatter), | ||
'Count Unique Values': tpl.countUnique(adaptiveFormatter), | ||
'List Unique Values': tpl.listUnique(', '), | ||
Sum: tpl.sum(adaptiveFormatter), | ||
Average: tpl.average(adaptiveFormatter), | ||
Median: tpl.median(adaptiveFormatter), | ||
'Sample Variance': tpl.var(1, adaptiveFormatter), | ||
'Sample Standard Deviation': tpl.stdev(1, adaptiveFormatter), | ||
Minimum: tpl.min(adaptiveFormatter), | ||
Maximum: tpl.max(adaptiveFormatter), | ||
First: tpl.first(adaptiveFormatter), | ||
Last: tpl.last(adaptiveFormatter), | ||
'Sum as Fraction of Total': tpl.fractionOf(tpl.sum(), 'total', adaptiveFormatter), | ||
'Sum as Fraction of Rows': tpl.fractionOf(tpl.sum(), 'row', adaptiveFormatter), | ||
'Sum as Fraction of Columns': tpl.fractionOf(tpl.sum(), 'col', adaptiveFormatter), | ||
'Count as Fraction of Total': tpl.fractionOf(tpl.count(), 'total', adaptiveFormatter), | ||
'Count as Fraction of Rows': tpl.fractionOf(tpl.count(), 'row', adaptiveFormatter), | ||
'Count as Fraction of Columns': tpl.fractionOf(tpl.count(), 'col', adaptiveFormatter), | ||
}))(aggregatorTemplates); | ||
|
||
const metricNames = metrics.map((metric: string | AdhocMetric) => | ||
typeof metric === 'string' ? metric : (metric.label as string), | ||
); | ||
|
||
const unpivotedData = data.reduce( | ||
(acc: Record<string, any>[], record: Record<string, any>) => [ | ||
...acc, | ||
...metricNames.map((name: string) => ({ | ||
...record, | ||
metric: name, | ||
value: record[name], | ||
})), | ||
], | ||
[], | ||
); | ||
|
||
const [rows, cols] = transposePivot | ||
? [groupbyColumns, ['metric', ...groupbyRows]] | ||
: [groupbyRows, ['metric', ...groupbyColumns]]; | ||
|
||
return ( | ||
<Styles height={height} width={width}> | ||
<PivotTable | ||
data={unpivotedData} | ||
rows={rows} | ||
cols={cols} | ||
aggregators={aggregators} | ||
aggregatorName={aggregateFunction} | ||
vals={['value']} | ||
rendererName={tableRenderer} | ||
colOrder={colOrder} | ||
rowOrder={rowOrder} | ||
sorters={{ | ||
metric: sortAs(metricNames), | ||
}} | ||
tableOptions={{ | ||
clickCallback: clickCellCallback, | ||
clickRowHeaderCallback, | ||
clickColumnHeaderCallback, | ||
colTotals, | ||
rowTotals, | ||
}} | ||
subtotalOptions={{ | ||
colSubtotalDisplay: { displayOnTop: colSubtotalPosition }, | ||
rowSubtotalDisplay: { displayOnTop: rowSubtotalPosition }, | ||
}} | ||
/> | ||
</Styles> | ||
); | ||
} |
Binary file added
BIN
+42.1 KB
...perset_ui/superset-ui/plugins/plugin-chart-pivot-table/src/images/thumbnail.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions
27
...-frontend/temporary_superset_ui/superset-ui/plugins/plugin-chart-pivot-table/src/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* 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. | ||
*/ | ||
// eslint-disable-next-line import/prefer-default-export | ||
export { default as PivotTableChartPlugin } from './plugin'; | ||
/** | ||
* Note: this file exports the default export from PivotTableChart.tsx. | ||
* If you want to export multiple visualization modules, you will need to | ||
* either add additional plugin folders (similar in structure to ./plugin) | ||
* OR export multiple instances of `ChartPlugin` extensions in ./plugin/index.ts | ||
* which in turn load exports from PivotTableChart.tsx | ||
*/ |
34 changes: 34 additions & 0 deletions
34
...mporary_superset_ui/superset-ui/plugins/plugin-chart-pivot-table/src/plugin/buildQuery.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* 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 { buildQueryContext, ensureIsArray } from '@superset-ui/core'; | ||
import { PivotTableQueryFormData } from '../types'; | ||
|
||
export default function buildQuery(formData: PivotTableQueryFormData) { | ||
const { groupbyColumns = [], groupbyRows = [] } = formData; | ||
const groupbySet = new Set([ | ||
...ensureIsArray<string>(groupbyColumns), | ||
...ensureIsArray<string>(groupbyRows), | ||
]); | ||
return buildQueryContext(formData, baseQueryObject => [ | ||
{ | ||
...baseQueryObject, | ||
columns: [...groupbySet], | ||
}, | ||
]); | ||
} |
Oops, something went wrong.