mirror of https://github.com/apache/superset.git
refactor: Migrates legacy Sunburst charts to ECharts and removes legacy code (#26350)
This commit is contained in:
parent
cf20b3439c
commit
4d9144eca5
|
@ -25,7 +25,7 @@ export const WORLD_HEALTH_CHARTS = [
|
||||||
{ name: 'Most Populated Countries', viz: 'table' },
|
{ name: 'Most Populated Countries', viz: 'table' },
|
||||||
{ name: "World's Population", viz: 'big_number' },
|
{ name: "World's Population", viz: 'big_number' },
|
||||||
{ name: 'Growth Rate', viz: 'line' },
|
{ name: 'Growth Rate', viz: 'line' },
|
||||||
{ name: 'Rural Breakdown', viz: 'sunburst' },
|
{ name: 'Rural Breakdown', viz: 'sunburst_v2' },
|
||||||
{ name: "World's Pop Growth", viz: 'area' },
|
{ name: "World's Pop Growth", viz: 'area' },
|
||||||
{ name: 'Life Expectancy VS Rural %', viz: 'bubble' },
|
{ name: 'Life Expectancy VS Rural %', viz: 'bubble' },
|
||||||
{ name: 'Treemap', viz: 'treemap_v2' },
|
{ name: 'Treemap', viz: 'treemap_v2' },
|
||||||
|
|
|
@ -18,17 +18,17 @@
|
||||||
*/
|
*/
|
||||||
describe('Visualization > Sunburst', () => {
|
describe('Visualization > Sunburst', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.intercept('POST', '/superset/explore_json/**').as('getJson');
|
cy.intercept('POST', '/api/v1/chart/data**').as('chartData');
|
||||||
});
|
});
|
||||||
|
|
||||||
const SUNBURST_FORM_DATA = {
|
const SUNBURST_FORM_DATA = {
|
||||||
datasource: '2__table',
|
datasource: '2__table',
|
||||||
viz_type: 'sunburst',
|
viz_type: 'sunburst_v2',
|
||||||
slice_id: 47,
|
slice_id: 47,
|
||||||
granularity_sqla: 'year',
|
granularity_sqla: 'year',
|
||||||
time_grain_sqla: 'P1D',
|
time_grain_sqla: 'P1D',
|
||||||
time_range: 'No filter',
|
time_range: 'No filter',
|
||||||
groupby: ['region'],
|
columns: ['region'],
|
||||||
metric: 'sum__SP_POP_TOTL',
|
metric: 'sum__SP_POP_TOTL',
|
||||||
adhoc_filters: [],
|
adhoc_filters: [],
|
||||||
row_limit: 50000,
|
row_limit: 50000,
|
||||||
|
@ -37,32 +37,35 @@ describe('Visualization > Sunburst', () => {
|
||||||
|
|
||||||
function verify(formData) {
|
function verify(formData) {
|
||||||
cy.visitChartByParams(formData);
|
cy.visitChartByParams(formData);
|
||||||
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' });
|
cy.verifySliceSuccess({ waitAlias: '@chartData' });
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should work without secondary metric', () => {
|
// requires the ability to render charts using SVG only for tests
|
||||||
|
it.skip('should work without secondary metric', () => {
|
||||||
verify(SUNBURST_FORM_DATA);
|
verify(SUNBURST_FORM_DATA);
|
||||||
// There should be 7 visible arcs + 1 hidden
|
cy.get('.chart-container svg g path').should('have.length', 7);
|
||||||
cy.get('.chart-container svg g#arcs path').should('have.length', 8);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with secondary metric', () => {
|
// requires the ability to render charts using SVG only for tests
|
||||||
|
it.skip('should work with secondary metric', () => {
|
||||||
verify({
|
verify({
|
||||||
...SUNBURST_FORM_DATA,
|
...SUNBURST_FORM_DATA,
|
||||||
secondary_metric: 'sum__SP_RUR_TOTL',
|
secondary_metric: 'sum__SP_RUR_TOTL',
|
||||||
});
|
});
|
||||||
cy.get('.chart-container svg g#arcs path').should('have.length', 8);
|
cy.get('.chart-container svg g path').should('have.length', 7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with multiple groupbys', () => {
|
// requires the ability to render charts using SVG only for tests
|
||||||
|
it.skip('should work with multiple columns', () => {
|
||||||
verify({
|
verify({
|
||||||
...SUNBURST_FORM_DATA,
|
...SUNBURST_FORM_DATA,
|
||||||
groupby: ['region', 'country_name'],
|
columns: ['region', 'country_name'],
|
||||||
});
|
});
|
||||||
cy.get('.chart-container svg g#arcs path').should('have.length', 117);
|
cy.get('.chart-container svg g path').should('have.length', 221);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should work with filter', () => {
|
// requires the ability to render charts using SVG only for tests
|
||||||
|
it.skip('should work with filter', () => {
|
||||||
verify({
|
verify({
|
||||||
...SUNBURST_FORM_DATA,
|
...SUNBURST_FORM_DATA,
|
||||||
adhoc_filters: [
|
adhoc_filters: [
|
||||||
|
@ -77,7 +80,7 @@ describe('Visualization > Sunburst', () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
cy.get('.chart-container svg g#arcs path').should('have.length', 3);
|
cy.get('.chart-container svg g path').should('have.length', 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow type to search color schemes', () => {
|
it('should allow type to search color schemes', () => {
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
"@superset-ui/legacy-plugin-chart-rose": "file:./plugins/legacy-plugin-chart-rose",
|
"@superset-ui/legacy-plugin-chart-rose": "file:./plugins/legacy-plugin-chart-rose",
|
||||||
"@superset-ui/legacy-plugin-chart-sankey": "file:./plugins/legacy-plugin-chart-sankey",
|
"@superset-ui/legacy-plugin-chart-sankey": "file:./plugins/legacy-plugin-chart-sankey",
|
||||||
"@superset-ui/legacy-plugin-chart-sankey-loop": "file:./plugins/legacy-plugin-chart-sankey-loop",
|
"@superset-ui/legacy-plugin-chart-sankey-loop": "file:./plugins/legacy-plugin-chart-sankey-loop",
|
||||||
"@superset-ui/legacy-plugin-chart-sunburst": "file:./plugins/legacy-plugin-chart-sunburst",
|
|
||||||
"@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map",
|
"@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map",
|
||||||
"@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
|
"@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
|
||||||
"@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3",
|
"@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3",
|
||||||
|
@ -18338,10 +18337,6 @@
|
||||||
"resolved": "plugins/legacy-plugin-chart-sankey-loop",
|
"resolved": "plugins/legacy-plugin-chart-sankey-loop",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@superset-ui/legacy-plugin-chart-sunburst": {
|
|
||||||
"resolved": "plugins/legacy-plugin-chart-sunburst",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/@superset-ui/legacy-plugin-chart-world-map": {
|
"node_modules/@superset-ui/legacy-plugin-chart-world-map": {
|
||||||
"resolved": "plugins/legacy-plugin-chart-world-map",
|
"resolved": "plugins/legacy-plugin-chart-world-map",
|
||||||
"link": true
|
"link": true
|
||||||
|
@ -62257,7 +62252,6 @@
|
||||||
"@superset-ui/legacy-plugin-chart-rose": "*",
|
"@superset-ui/legacy-plugin-chart-rose": "*",
|
||||||
"@superset-ui/legacy-plugin-chart-sankey": "*",
|
"@superset-ui/legacy-plugin-chart-sankey": "*",
|
||||||
"@superset-ui/legacy-plugin-chart-sankey-loop": "*",
|
"@superset-ui/legacy-plugin-chart-sankey-loop": "*",
|
||||||
"@superset-ui/legacy-plugin-chart-sunburst": "*",
|
|
||||||
"@superset-ui/legacy-plugin-chart-time-table": "*",
|
"@superset-ui/legacy-plugin-chart-time-table": "*",
|
||||||
"@superset-ui/legacy-plugin-chart-world-map": "*",
|
"@superset-ui/legacy-plugin-chart-world-map": "*",
|
||||||
"@superset-ui/legacy-preset-chart-deckgl": "*",
|
"@superset-ui/legacy-preset-chart-deckgl": "*",
|
||||||
|
@ -63291,20 +63285,6 @@
|
||||||
"react": "^16.13.1"
|
"react": "^16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins/legacy-plugin-chart-sunburst": {
|
|
||||||
"name": "@superset-ui/legacy-plugin-chart-sunburst",
|
|
||||||
"version": "0.18.25",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"d3": "^3.5.17",
|
|
||||||
"prop-types": "^15.8.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@superset-ui/chart-controls": "*",
|
|
||||||
"@superset-ui/core": "*",
|
|
||||||
"react": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"plugins/legacy-plugin-chart-time-table": {
|
"plugins/legacy-plugin-chart-time-table": {
|
||||||
"name": "@superset-ui/legacy-plugin-chart-time-table",
|
"name": "@superset-ui/legacy-plugin-chart-time-table",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
@ -78157,13 +78137,6 @@
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@superset-ui/legacy-plugin-chart-sunburst": {
|
|
||||||
"version": "file:plugins/legacy-plugin-chart-sunburst",
|
|
||||||
"requires": {
|
|
||||||
"d3": "^3.5.17",
|
|
||||||
"prop-types": "^15.8.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@superset-ui/legacy-plugin-chart-world-map": {
|
"@superset-ui/legacy-plugin-chart-world-map": {
|
||||||
"version": "file:plugins/legacy-plugin-chart-world-map",
|
"version": "file:plugins/legacy-plugin-chart-world-map",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|
|
@ -105,7 +105,6 @@
|
||||||
"@superset-ui/legacy-plugin-chart-rose": "file:./plugins/legacy-plugin-chart-rose",
|
"@superset-ui/legacy-plugin-chart-rose": "file:./plugins/legacy-plugin-chart-rose",
|
||||||
"@superset-ui/legacy-plugin-chart-sankey": "file:./plugins/legacy-plugin-chart-sankey",
|
"@superset-ui/legacy-plugin-chart-sankey": "file:./plugins/legacy-plugin-chart-sankey",
|
||||||
"@superset-ui/legacy-plugin-chart-sankey-loop": "file:./plugins/legacy-plugin-chart-sankey-loop",
|
"@superset-ui/legacy-plugin-chart-sankey-loop": "file:./plugins/legacy-plugin-chart-sankey-loop",
|
||||||
"@superset-ui/legacy-plugin-chart-sunburst": "file:./plugins/legacy-plugin-chart-sunburst",
|
|
||||||
"@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map",
|
"@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map",
|
||||||
"@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
|
"@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
|
||||||
"@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3",
|
"@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3",
|
||||||
|
|
|
@ -56,13 +56,13 @@ export const wordCloudFormData = {
|
||||||
|
|
||||||
export const sunburstFormData = {
|
export const sunburstFormData = {
|
||||||
datasource: '2__table',
|
datasource: '2__table',
|
||||||
viz_type: 'sunburst',
|
viz_type: 'sunburst_v2',
|
||||||
slice_id: 47,
|
slice_id: 47,
|
||||||
url_params: {},
|
url_params: {},
|
||||||
granularity_sqla: 'year',
|
granularity_sqla: 'year',
|
||||||
time_grain_sqla: 'P1D',
|
time_grain_sqla: 'P1D',
|
||||||
time_range: '2011-01-01 : 2011-01-01',
|
time_range: '2011-01-01 : 2011-01-01',
|
||||||
groupby: ['region', 'country_name'],
|
columns: ['region', 'country_name'],
|
||||||
metric: 'sum__SP_POP_TOTL',
|
metric: 'sum__SP_POP_TOTL',
|
||||||
secondary_metric: 'sum__SP_RUR_TOTL',
|
secondary_metric: 'sum__SP_RUR_TOTL',
|
||||||
adhoc_filters: [],
|
adhoc_filters: [],
|
||||||
|
|
|
@ -80,7 +80,6 @@
|
||||||
"@superset-ui/legacy-plugin-chart-rose": "*",
|
"@superset-ui/legacy-plugin-chart-rose": "*",
|
||||||
"@superset-ui/legacy-plugin-chart-sankey": "*",
|
"@superset-ui/legacy-plugin-chart-sankey": "*",
|
||||||
"@superset-ui/legacy-plugin-chart-sankey-loop": "*",
|
"@superset-ui/legacy-plugin-chart-sankey-loop": "*",
|
||||||
"@superset-ui/legacy-plugin-chart-sunburst": "*",
|
|
||||||
"@superset-ui/legacy-plugin-chart-time-table": "*",
|
"@superset-ui/legacy-plugin-chart-time-table": "*",
|
||||||
"@superset-ui/legacy-plugin-chart-world-map": "*",
|
"@superset-ui/legacy-plugin-chart-world-map": "*",
|
||||||
"@superset-ui/legacy-preset-chart-deckgl": "*",
|
"@superset-ui/legacy-preset-chart-deckgl": "*",
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 no-magic-numbers */
|
|
||||||
import React from 'react';
|
|
||||||
import { SuperChart } from '@superset-ui/core';
|
|
||||||
import SunburstChartPlugin from '@superset-ui/legacy-plugin-chart-sunburst';
|
|
||||||
import ResizableChartDemo from '../../../shared/components/ResizableChartDemo';
|
|
||||||
import data from './data';
|
|
||||||
|
|
||||||
new SunburstChartPlugin().configure({ key: 'sunburst' }).register();
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'Legacy Chart Plugins/legacy-plugin-chart-sunburst',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const basic = () => (
|
|
||||||
<SuperChart
|
|
||||||
chartType="sunburst"
|
|
||||||
width={400}
|
|
||||||
height={400}
|
|
||||||
queriesData={[{ data }]}
|
|
||||||
formData={{
|
|
||||||
colorScheme: 'd3Category10',
|
|
||||||
metric: 'sum__SP_POP_TOTL',
|
|
||||||
secondaryMetric: 'sum__SP_RUR_TOTL',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const resizable = () => (
|
|
||||||
<ResizableChartDemo>
|
|
||||||
{({ width, height }) => (
|
|
||||||
<SuperChart
|
|
||||||
chartType="sunburst"
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
queriesData={[{ data }]}
|
|
||||||
formData={{
|
|
||||||
colorScheme: 'd3Category10',
|
|
||||||
metric: 'sum__SP_POP_TOTL',
|
|
||||||
secondaryMetric: 'sum__SP_RUR_TOTL',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</ResizableChartDemo>
|
|
||||||
);
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 sort-keys, no-magic-numbers */
|
|
||||||
export default [
|
|
||||||
['East Asia & Pacific', 'China', 1344130000.0, 664363135.0],
|
|
||||||
['South Asia', 'India', 1247446011.0, 857294797.0],
|
|
||||||
['North America', 'United States', 311721632.0, 59414143.0],
|
|
||||||
['East Asia & Pacific', 'Indonesia', 244808254.0, 120661092.0],
|
|
||||||
['Latin America & Caribbean', 'Brazil', 200517584.0, 30833589.0],
|
|
||||||
['South Asia', 'Pakistan', 173669648.0, 109399721.0],
|
|
||||||
['Sub-Saharan Africa', 'Nigeria', 163770669.0, 91118725.0],
|
|
||||||
['South Asia', 'Bangladesh', 153405612.0, 105504710.0],
|
|
||||||
['Europe & Central Asia', 'Russian Federation', 142960868.0, 37552961.0],
|
|
||||||
['East Asia & Pacific', 'Japan', 127817277.0, 11186568.0],
|
|
||||||
];
|
|
|
@ -27,7 +27,6 @@ import {
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { BigNumberChartPlugin } from '@superset-ui/plugin-chart-echarts';
|
import { BigNumberChartPlugin } from '@superset-ui/plugin-chart-echarts';
|
||||||
import LegacySankeyPlugin from '@superset-ui/legacy-plugin-chart-sankey';
|
import LegacySankeyPlugin from '@superset-ui/legacy-plugin-chart-sankey';
|
||||||
import LegacySunburstPlugin from '@superset-ui/legacy-plugin-chart-sunburst';
|
|
||||||
import { WordCloudChartPlugin } from '@superset-ui/plugin-chart-word-cloud';
|
import { WordCloudChartPlugin } from '@superset-ui/plugin-chart-word-cloud';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -50,8 +49,6 @@ new BigNumberChartPlugin().configure({ key: BIG_NUMBER }).register();
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
new LegacySankeyPlugin().configure({ key: SANKEY }).register();
|
new LegacySankeyPlugin().configure({ key: SANKEY }).register();
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
new LegacySunburstPlugin().configure({ key: SUNBURST }).register();
|
|
||||||
// eslint-disable-next-line
|
|
||||||
new WordCloudChartPlugin().configure({ key: WORD_CLOUD }).register();
|
new WordCloudChartPlugin().configure({ key: WORD_CLOUD }).register();
|
||||||
|
|
||||||
const VIS_TYPES = [BIG_NUMBER, SANKEY, SUNBURST, WORD_CLOUD, WORD_CLOUD_LEGACY];
|
const VIS_TYPES = [BIG_NUMBER, SANKEY, SUNBURST, WORD_CLOUD, WORD_CLOUD_LEGACY];
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
<!--
|
|
||||||
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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
# Change Log
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
||||||
|
|
||||||
# [0.18.0](https://github.com/apache-superset/superset-ui/compare/v0.17.87...v0.18.0) (2021-08-30)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @superset-ui/legacy-plugin-chart-sunburst
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.17.61](https://github.com/apache-superset/superset-ui/compare/v0.17.60...v0.17.61) (2021-07-02)
|
|
||||||
|
|
||||||
**Note:** Version bump only for package @superset-ui/legacy-plugin-chart-sunburst
|
|
|
@ -1,52 +0,0 @@
|
||||||
<!--
|
|
||||||
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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## @superset-ui/legacy-plugin-chart-sunburst
|
|
||||||
|
|
||||||
[![Version](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-sunburst.svg?style=flat-square)](https://www.npmjs.com/package/@superset-ui/legacy-plugin-chart-sunburst)
|
|
||||||
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui-plugins.svg?path=packages%2Fsuperset-ui-legacy-plugin-chart-sunburst&style=flat-square)](https://david-dm.org/apache-superset/superset-ui-plugins?path=packages/superset-ui-legacy-plugin-chart-sunburst)
|
|
||||||
|
|
||||||
This plugin provides Sunburst 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 SunburstChartPlugin from '@superset-ui/legacy-plugin-chart-sunburst';
|
|
||||||
|
|
||||||
new SunburstChartPlugin().configure({ key: 'sunburst' }).register();
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use it via `SuperChart`. See
|
|
||||||
[storybook](https://apache-superset.github.io/superset-ui-plugins/?selectedKind=plugin-chart-sunburst)
|
|
||||||
for more details.
|
|
||||||
|
|
||||||
```js
|
|
||||||
<SuperChart
|
|
||||||
chartType="sunburst"
|
|
||||||
width={600}
|
|
||||||
height={600}
|
|
||||||
formData={...}
|
|
||||||
queriesData={[{
|
|
||||||
data: {...},
|
|
||||||
}]}
|
|
||||||
/>
|
|
||||||
```
|
|
|
@ -1,37 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@superset-ui/legacy-plugin-chart-sunburst",
|
|
||||||
"version": "0.18.25",
|
|
||||||
"description": "Superset Legacy Chart - Sunburst",
|
|
||||||
"keywords": [
|
|
||||||
"superset"
|
|
||||||
],
|
|
||||||
"homepage": "https://github.com/apache/superset/tree/master/superset-frontend/plugins/legacy-plugin-chart-sunburst#readme",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/apache/superset/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/apache/superset.git",
|
|
||||||
"directory": "superset-frontend/plugins/legacy-plugin-chart-sunburst"
|
|
||||||
},
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"author": "Superset",
|
|
||||||
"main": "lib/index.js",
|
|
||||||
"module": "esm/index.js",
|
|
||||||
"files": [
|
|
||||||
"esm",
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"d3": "^3.5.17",
|
|
||||||
"prop-types": "^15.8.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@superset-ui/chart-controls": "*",
|
|
||||||
"@superset-ui/core": "*",
|
|
||||||
"react": "^16.13.1"
|
|
||||||
},
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
/**
|
|
||||||
* 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 { reactify, styled } from '@superset-ui/core';
|
|
||||||
import Component from './Sunburst';
|
|
||||||
|
|
||||||
const ReactComponent = reactify(Component);
|
|
||||||
|
|
||||||
const Sunburst = ({ className, ...otherProps }) => (
|
|
||||||
<div className={className}>
|
|
||||||
<ReactComponent {...otherProps} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default styled(Sunburst)`
|
|
||||||
${({ theme }) => `
|
|
||||||
.superset-legacy-chart-sunburst text {
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
}
|
|
||||||
.superset-legacy-chart-sunburst path {
|
|
||||||
stroke: ${theme.colors.grayscale.light2};
|
|
||||||
stroke-width: 0.5px;
|
|
||||||
}
|
|
||||||
.superset-legacy-chart-sunburst .center-label {
|
|
||||||
text-anchor: middle;
|
|
||||||
fill: ${theme.colors.grayscale.dark1};
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
.superset-legacy-chart-sunburst .path-abs-percent {
|
|
||||||
font-size: ${theme.typography.sizes.m}px;
|
|
||||||
font-weight: ${theme.typography.weights.bold};
|
|
||||||
}
|
|
||||||
.superset-legacy-chart-sunburst .path-cond-percent {
|
|
||||||
font-size: ${theme.typography.sizes.s}px;
|
|
||||||
}
|
|
||||||
.superset-legacy-chart-sunburst .path-metrics {
|
|
||||||
color: ${theme.colors.grayscale.base};
|
|
||||||
}
|
|
||||||
.superset-legacy-chart-sunburst .path-ratio {
|
|
||||||
color: ${theme.colors.grayscale.base};
|
|
||||||
}
|
|
||||||
|
|
||||||
.superset-legacy-chart-sunburst .breadcrumbs text {
|
|
||||||
font-weight: ${theme.typography.weights.bold};
|
|
||||||
font-size: ${theme.typography.sizes.m}px;
|
|
||||||
text-anchor: middle;
|
|
||||||
fill: ${theme.colors.grayscale.dark1};
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
`;
|
|
|
@ -1,531 +0,0 @@
|
||||||
/**
|
|
||||||
* 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 no-param-reassign, react/sort-prop-types */
|
|
||||||
import d3 from 'd3';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {
|
|
||||||
getNumberFormatter,
|
|
||||||
NumberFormats,
|
|
||||||
CategoricalColorNamespace,
|
|
||||||
getSequentialSchemeRegistry,
|
|
||||||
t,
|
|
||||||
} from '@superset-ui/core';
|
|
||||||
import wrapSvgText from './utils/wrapSvgText';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
// Each row is an array of [hierarchy-lvl1, hierarchy-lvl2, metric1, metric2]
|
|
||||||
// hierarchy-lvls are string. metrics are number
|
|
||||||
data: PropTypes.arrayOf(PropTypes.array),
|
|
||||||
width: PropTypes.number,
|
|
||||||
height: PropTypes.number,
|
|
||||||
colorScheme: PropTypes.string,
|
|
||||||
linearColorScheme: PropTypes.string,
|
|
||||||
numberFormat: PropTypes.string,
|
|
||||||
metrics: PropTypes.arrayOf(
|
|
||||||
PropTypes.oneOfType([
|
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.object, // The metric object
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
function metricLabel(metric) {
|
|
||||||
return typeof metric === 'string' || metric instanceof String
|
|
||||||
? metric
|
|
||||||
: metric.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a node in a partition layout, return an array of all of its ancestor
|
|
||||||
// nodes, highest first, but excluding the root.
|
|
||||||
function getAncestors(node) {
|
|
||||||
const path = [];
|
|
||||||
let current = node;
|
|
||||||
while (current.parent) {
|
|
||||||
path.unshift(current);
|
|
||||||
current = current.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildHierarchy(rows) {
|
|
||||||
const root = {
|
|
||||||
name: 'root',
|
|
||||||
children: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
// each record [groupby1val, groupby2val, (<string> or 0)n, m1, m2]
|
|
||||||
rows.forEach(row => {
|
|
||||||
const m1 = Number(row[row.length - 2]);
|
|
||||||
const m2 = Number(row[row.length - 1]);
|
|
||||||
const levels = row.slice(0, -2);
|
|
||||||
if (Number.isNaN(m1)) {
|
|
||||||
// e.g. if this is a header row
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let currentNode = root;
|
|
||||||
for (let level = 0; level < levels.length; level += 1) {
|
|
||||||
const children = currentNode.children || [];
|
|
||||||
const node = levels[level];
|
|
||||||
const nodeName = node ? node.toString() : t('N/A');
|
|
||||||
// If the next node has the name '0', it will
|
|
||||||
const isLeafNode = level >= levels.length - 1 || levels[level + 1] === 0;
|
|
||||||
let childNode;
|
|
||||||
|
|
||||||
if (!isLeafNode) {
|
|
||||||
childNode = children.find(
|
|
||||||
child => child.name === nodeName && child.level === level,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!childNode) {
|
|
||||||
childNode = {
|
|
||||||
name: nodeName,
|
|
||||||
children: [],
|
|
||||||
level,
|
|
||||||
};
|
|
||||||
children.push(childNode);
|
|
||||||
}
|
|
||||||
currentNode = childNode;
|
|
||||||
} else if (nodeName !== 0) {
|
|
||||||
// Reached the end of the sequence; create a leaf node.
|
|
||||||
childNode = {
|
|
||||||
name: nodeName,
|
|
||||||
m1,
|
|
||||||
m2,
|
|
||||||
};
|
|
||||||
children.push(childNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function recurse(node) {
|
|
||||||
if (node.children) {
|
|
||||||
let sums;
|
|
||||||
let m1 = 0;
|
|
||||||
let m2 = 0;
|
|
||||||
for (let i = 0; i < node.children.length; i += 1) {
|
|
||||||
sums = recurse(node.children[i]);
|
|
||||||
m1 += sums[0];
|
|
||||||
m2 += sums[1];
|
|
||||||
}
|
|
||||||
node.m1 = m1;
|
|
||||||
node.m2 = m2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [node.m1, node.m2];
|
|
||||||
}
|
|
||||||
|
|
||||||
recurse(root);
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getResponsiveContainerClass(width) {
|
|
||||||
if (width > 500) {
|
|
||||||
return 'l';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width > 200 && width <= 500) {
|
|
||||||
return 'm';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 's';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getYOffset(width) {
|
|
||||||
if (width > 500) {
|
|
||||||
return ['0', '20', '40', '60'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width > 200 && width <= 500) {
|
|
||||||
return ['0', '15', '30', '45'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['0', '10', '20', '30'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modified from http://bl.ocks.org/kerryrodden/7090426
|
|
||||||
function Sunburst(element, props) {
|
|
||||||
const container = d3.select(element);
|
|
||||||
const {
|
|
||||||
data,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
colorScheme,
|
|
||||||
linearColorScheme,
|
|
||||||
metrics,
|
|
||||||
numberFormat,
|
|
||||||
sliceId,
|
|
||||||
} = props;
|
|
||||||
const responsiveClass = getResponsiveContainerClass(width);
|
|
||||||
const isSmallWidth = responsiveClass === 's';
|
|
||||||
container.attr('class', `superset-legacy-chart-sunburst ${responsiveClass}`);
|
|
||||||
// vars with shared scope within this function
|
|
||||||
const margin = { top: 10, right: 5, bottom: 10, left: 5 };
|
|
||||||
const containerWidth = width;
|
|
||||||
const containerHeight = height;
|
|
||||||
const breadcrumbHeight = containerHeight * 0.085;
|
|
||||||
const visWidth = containerWidth - margin.left - margin.right;
|
|
||||||
const visHeight =
|
|
||||||
containerHeight - margin.top - margin.bottom - breadcrumbHeight;
|
|
||||||
const radius = Math.min(visWidth, visHeight) / 2;
|
|
||||||
|
|
||||||
let colorByCategory = true; // color by category if primary/secondary metrics match
|
|
||||||
let maxBreadcrumbs;
|
|
||||||
let breadcrumbDims; // set based on data
|
|
||||||
let totalSize; // total size of all segments; set after loading the data.
|
|
||||||
let breadcrumbs;
|
|
||||||
let vis;
|
|
||||||
let arcs;
|
|
||||||
let gMiddleText; // dom handles
|
|
||||||
|
|
||||||
const categoricalColorScale = CategoricalColorNamespace.getScale(colorScheme);
|
|
||||||
let linearColorScale;
|
|
||||||
|
|
||||||
// Helper + path gen functions
|
|
||||||
const partition = d3.layout
|
|
||||||
.partition()
|
|
||||||
.size([2 * Math.PI, radius * radius])
|
|
||||||
.value(d => d.m1);
|
|
||||||
|
|
||||||
const arc = d3.svg
|
|
||||||
.arc()
|
|
||||||
.startAngle(d => d.x)
|
|
||||||
.endAngle(d => d.x + d.dx)
|
|
||||||
.innerRadius(d => Math.sqrt(d.y))
|
|
||||||
.outerRadius(d => Math.sqrt(d.y + d.dy));
|
|
||||||
|
|
||||||
const formatNum = getNumberFormatter(
|
|
||||||
numberFormat || NumberFormats.SI_3_DIGIT,
|
|
||||||
);
|
|
||||||
const formatPerc = getNumberFormatter(NumberFormats.PERCENT_3_POINT);
|
|
||||||
|
|
||||||
container.select('svg').remove();
|
|
||||||
|
|
||||||
const svg = container
|
|
||||||
.append('svg:svg')
|
|
||||||
.attr('width', containerWidth)
|
|
||||||
.attr('height', containerHeight);
|
|
||||||
|
|
||||||
function createBreadcrumbs(firstRowData) {
|
|
||||||
// -2 bc row contains 2x metrics, +extra for %label and buffer
|
|
||||||
maxBreadcrumbs = firstRowData.length - 2 + 1;
|
|
||||||
breadcrumbDims = {
|
|
||||||
width: visWidth / maxBreadcrumbs,
|
|
||||||
height: breadcrumbHeight * 0.8, // more margin
|
|
||||||
spacing: 3,
|
|
||||||
tipTailWidth: 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
breadcrumbs = svg
|
|
||||||
.append('svg:g')
|
|
||||||
.attr('class', 'breadcrumbs')
|
|
||||||
.attr('transform', `translate(${margin.left},${margin.top})`);
|
|
||||||
|
|
||||||
breadcrumbs.append('svg:text').attr('class', 'end-label');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a string that describes the points of a breadcrumb polygon.
|
|
||||||
function breadcrumbPoints(d, i) {
|
|
||||||
const points = [];
|
|
||||||
if (isSmallWidth) {
|
|
||||||
points.push('0,0');
|
|
||||||
points.push(`${width},0`);
|
|
||||||
points.push(`${width},0`);
|
|
||||||
points.push(`${width},${breadcrumbDims.height}`);
|
|
||||||
points.push(`0,${breadcrumbDims.height}`);
|
|
||||||
if (i > 0) {
|
|
||||||
// Leftmost breadcrumb; don't include 6th vertex.
|
|
||||||
// points.push(`${breadcrumbDims.tipTailWidth},${breadcrumbDims.height / 2}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
points.push('0,0');
|
|
||||||
points.push(`${breadcrumbDims.width},0`);
|
|
||||||
points.push(
|
|
||||||
`${breadcrumbDims.width + breadcrumbDims.tipTailWidth},${
|
|
||||||
breadcrumbDims.height / 2
|
|
||||||
}`,
|
|
||||||
);
|
|
||||||
points.push(`${breadcrumbDims.width},${breadcrumbDims.height}`);
|
|
||||||
points.push(`0,${breadcrumbDims.height}`);
|
|
||||||
if (i > 0) {
|
|
||||||
// Leftmost breadcrumb; don't include 6th vertex.
|
|
||||||
points.push(
|
|
||||||
`${breadcrumbDims.tipTailWidth},${breadcrumbDims.height / 2}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return points.join(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateBreadcrumbs(sequenceArray, percentageString) {
|
|
||||||
const breadcrumbWidth = isSmallWidth ? width : breadcrumbDims.width;
|
|
||||||
const g = breadcrumbs
|
|
||||||
.selectAll('g')
|
|
||||||
.data(sequenceArray, d => d.name + d.depth);
|
|
||||||
|
|
||||||
// Add breadcrumb and label for entering nodes.
|
|
||||||
const entering = g.enter().append('svg:g');
|
|
||||||
|
|
||||||
entering
|
|
||||||
.append('svg:polygon')
|
|
||||||
.attr('points', breadcrumbPoints)
|
|
||||||
.style('fill', d =>
|
|
||||||
colorByCategory
|
|
||||||
? categoricalColorScale(d.name, sliceId)
|
|
||||||
: linearColorScale(d.m2 / d.m1),
|
|
||||||
);
|
|
||||||
|
|
||||||
entering
|
|
||||||
.append('svg:text')
|
|
||||||
.attr('x', (breadcrumbWidth + breadcrumbDims.tipTailWidth) / 2)
|
|
||||||
.attr('y', breadcrumbDims.height / 4)
|
|
||||||
.attr('dy', '0.35em')
|
|
||||||
.style('fill', d => {
|
|
||||||
// Make text white or black based on the lightness of the background
|
|
||||||
const col = d3.hsl(
|
|
||||||
colorByCategory
|
|
||||||
? categoricalColorScale(d.name, sliceId)
|
|
||||||
: linearColorScale(d.m2 / d.m1),
|
|
||||||
);
|
|
||||||
|
|
||||||
return col.l < 0.5 ? 'white' : 'black';
|
|
||||||
})
|
|
||||||
.attr('class', 'step-label')
|
|
||||||
.text(d => d.name.replace(/_/g, ' '))
|
|
||||||
.call(wrapSvgText, breadcrumbWidth, breadcrumbDims.height / 2);
|
|
||||||
|
|
||||||
// Set position for entering and updating nodes.
|
|
||||||
g.attr('transform', (d, i) => {
|
|
||||||
if (isSmallWidth) {
|
|
||||||
return `translate(0, ${
|
|
||||||
i * (breadcrumbDims.height + breadcrumbDims.spacing)
|
|
||||||
})`;
|
|
||||||
}
|
|
||||||
return `translate(${
|
|
||||||
i * (breadcrumbDims.width + breadcrumbDims.spacing)
|
|
||||||
}, 0)`;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove exiting nodes.
|
|
||||||
g.exit().remove();
|
|
||||||
|
|
||||||
// Now move and update the percentage at the end.
|
|
||||||
breadcrumbs
|
|
||||||
.select('.end-label')
|
|
||||||
.attr('x', () => {
|
|
||||||
if (isSmallWidth) {
|
|
||||||
return (breadcrumbWidth + breadcrumbDims.tipTailWidth) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
(sequenceArray.length + 0.5) *
|
|
||||||
(breadcrumbDims.width + breadcrumbDims.spacing)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.attr('y', () => {
|
|
||||||
if (isSmallWidth) {
|
|
||||||
return (sequenceArray.length + 1) * breadcrumbDims.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
return breadcrumbDims.height / 2;
|
|
||||||
})
|
|
||||||
.attr('dy', '0.35em')
|
|
||||||
.text(percentageString);
|
|
||||||
|
|
||||||
// Make the breadcrumb trail visible, if it's hidden.
|
|
||||||
breadcrumbs.style('visibility', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fade all but the current sequence, and show it in the breadcrumb trail.
|
|
||||||
function mouseenter(d) {
|
|
||||||
const sequenceArray = getAncestors(d);
|
|
||||||
const parentOfD = sequenceArray[sequenceArray.length - 2] || null;
|
|
||||||
|
|
||||||
const absolutePercentage = (d.m1 / totalSize).toPrecision(3);
|
|
||||||
const conditionalPercentage = parentOfD
|
|
||||||
? (d.m1 / parentOfD.m1).toPrecision(3)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const absolutePercString = formatPerc(absolutePercentage);
|
|
||||||
const conditionalPercString = parentOfD
|
|
||||||
? formatPerc(conditionalPercentage)
|
|
||||||
: '';
|
|
||||||
|
|
||||||
// 3 levels of text if inner-most level, 4 otherwise
|
|
||||||
const yOffsets = getYOffset(width);
|
|
||||||
let offsetIndex = 0;
|
|
||||||
|
|
||||||
// If metrics match, assume we are coloring by category
|
|
||||||
const metricsMatch = Math.abs(d.m1 - d.m2) < 0.00001;
|
|
||||||
|
|
||||||
gMiddleText.selectAll('*').remove();
|
|
||||||
|
|
||||||
offsetIndex += 1;
|
|
||||||
gMiddleText
|
|
||||||
.append('text')
|
|
||||||
.attr('class', 'path-abs-percent')
|
|
||||||
.attr('y', yOffsets[offsetIndex])
|
|
||||||
// eslint-disable-next-line prefer-template
|
|
||||||
.text(absolutePercString + ' ' + t('of total'));
|
|
||||||
|
|
||||||
const OF_PARENT_TEXT = t('of parent');
|
|
||||||
|
|
||||||
if (conditionalPercString) {
|
|
||||||
offsetIndex += 1;
|
|
||||||
gMiddleText
|
|
||||||
.append('text')
|
|
||||||
.attr('class', 'path-cond-percent')
|
|
||||||
.attr('y', yOffsets[offsetIndex])
|
|
||||||
.text(`${conditionalPercString} ${OF_PARENT_TEXT}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
offsetIndex += 1;
|
|
||||||
gMiddleText
|
|
||||||
.append('text')
|
|
||||||
.attr('class', 'path-metrics')
|
|
||||||
.attr('y', yOffsets[offsetIndex])
|
|
||||||
.text(
|
|
||||||
`${metricLabel(metrics[0])}: ${formatNum(d.m1)}${
|
|
||||||
metricsMatch ? '' : `, ${metricLabel(metrics[1])}: ${formatNum(d.m2)}`
|
|
||||||
}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
offsetIndex += 1;
|
|
||||||
gMiddleText
|
|
||||||
.append('text')
|
|
||||||
.attr('class', 'path-ratio')
|
|
||||||
.attr('y', yOffsets[offsetIndex])
|
|
||||||
.text(
|
|
||||||
metricsMatch
|
|
||||||
? ''
|
|
||||||
: `${metricLabel(metrics[1])}/${metricLabel(
|
|
||||||
metrics[0],
|
|
||||||
)}: ${formatPerc(d.m2 / d.m1)}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reset and fade all the segments.
|
|
||||||
arcs
|
|
||||||
.selectAll('path')
|
|
||||||
.style('stroke-width', null)
|
|
||||||
.style('stroke', null)
|
|
||||||
.style('opacity', 0.3);
|
|
||||||
|
|
||||||
// Then highlight only those that are an ancestor of the current segment.
|
|
||||||
arcs
|
|
||||||
.selectAll('path')
|
|
||||||
.filter(node => sequenceArray.includes(node))
|
|
||||||
.style('opacity', 1)
|
|
||||||
.style('stroke', '#aaa');
|
|
||||||
|
|
||||||
updateBreadcrumbs(sequenceArray, absolutePercString);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore everything to full opacity when moving off the visualization.
|
|
||||||
function mouseleave() {
|
|
||||||
// Hide the breadcrumb trail
|
|
||||||
breadcrumbs.style('visibility', 'hidden');
|
|
||||||
|
|
||||||
gMiddleText.selectAll('*').remove();
|
|
||||||
|
|
||||||
// Deactivate all segments during transition.
|
|
||||||
arcs.selectAll('path').on('mouseenter', null);
|
|
||||||
|
|
||||||
// Transition each segment to full opacity and then reactivate it.
|
|
||||||
arcs
|
|
||||||
.selectAll('path')
|
|
||||||
.transition()
|
|
||||||
.duration(200)
|
|
||||||
.style('opacity', 1)
|
|
||||||
.style('stroke', null)
|
|
||||||
.style('stroke-width', null)
|
|
||||||
.each('end', function end() {
|
|
||||||
d3.select(this).on('mouseenter', mouseenter);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main function to draw and set up the visualization, once we have the data.
|
|
||||||
function createVisualization(rows) {
|
|
||||||
const root = buildHierarchy(rows);
|
|
||||||
maxBreadcrumbs = rows[0].length - 2;
|
|
||||||
vis = svg
|
|
||||||
.append('svg:g')
|
|
||||||
.attr('class', 'sunburst-vis')
|
|
||||||
.attr(
|
|
||||||
'transform',
|
|
||||||
'translate(' +
|
|
||||||
`${margin.left + visWidth / 2},` +
|
|
||||||
`${
|
|
||||||
margin.top +
|
|
||||||
(isSmallWidth
|
|
||||||
? breadcrumbHeight * maxBreadcrumbs
|
|
||||||
: breadcrumbHeight) +
|
|
||||||
visHeight / 2
|
|
||||||
}` +
|
|
||||||
')',
|
|
||||||
)
|
|
||||||
.on('mouseleave', mouseleave);
|
|
||||||
|
|
||||||
arcs = vis.append('svg:g').attr('id', 'arcs');
|
|
||||||
|
|
||||||
gMiddleText = vis.append('svg:g').attr('class', 'center-label');
|
|
||||||
|
|
||||||
// Bounding circle underneath the sunburst, to make it easier to detect
|
|
||||||
// when the mouse leaves the parent g.
|
|
||||||
arcs.append('svg:circle').attr('r', radius).style('opacity', 0);
|
|
||||||
|
|
||||||
// For efficiency, filter nodes to keep only those large enough to see.
|
|
||||||
const nodes = partition.nodes(root).filter(d => d.dx > 0.005); // 0.005 radians = 0.29 degrees
|
|
||||||
|
|
||||||
if (metrics[0] !== metrics[1] && metrics[1]) {
|
|
||||||
colorByCategory = false;
|
|
||||||
const ext = d3.extent(nodes, d => d.m2 / d.m1);
|
|
||||||
linearColorScale = getSequentialSchemeRegistry()
|
|
||||||
.get(linearColorScheme)
|
|
||||||
.createLinearScale(ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
arcs
|
|
||||||
.selectAll('path')
|
|
||||||
.data(nodes)
|
|
||||||
.enter()
|
|
||||||
.append('svg:path')
|
|
||||||
.attr('display', d => (d.depth ? null : 'none'))
|
|
||||||
.attr('d', arc)
|
|
||||||
.attr('fill-rule', 'evenodd')
|
|
||||||
.style('fill', d =>
|
|
||||||
colorByCategory
|
|
||||||
? categoricalColorScale(d.name, sliceId)
|
|
||||||
: linearColorScale(d.m2 / d.m1),
|
|
||||||
)
|
|
||||||
.style('opacity', 1)
|
|
||||||
.on('mouseenter', mouseenter);
|
|
||||||
|
|
||||||
// Get total size of the tree = value of root node from partition.
|
|
||||||
totalSize = root.value;
|
|
||||||
}
|
|
||||||
createBreadcrumbs(data[0]);
|
|
||||||
createVisualization(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Sunburst.displayName = 'Sunburst';
|
|
||||||
Sunburst.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default Sunburst;
|
|
|
@ -1,108 +0,0 @@
|
||||||
/**
|
|
||||||
* 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 { t } from '@superset-ui/core';
|
|
||||||
import {
|
|
||||||
ControlPanelConfig,
|
|
||||||
ControlPanelsContainerProps,
|
|
||||||
getStandardizedControls,
|
|
||||||
sections,
|
|
||||||
} from '@superset-ui/chart-controls';
|
|
||||||
|
|
||||||
const config: ControlPanelConfig = {
|
|
||||||
controlPanelSections: [
|
|
||||||
sections.legacyRegularTime,
|
|
||||||
{
|
|
||||||
label: t('Query'),
|
|
||||||
expanded: true,
|
|
||||||
controlSetRows: [
|
|
||||||
['groupby'],
|
|
||||||
['metric'],
|
|
||||||
['secondary_metric'],
|
|
||||||
['adhoc_filters'],
|
|
||||||
['row_limit'],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'sort_by_metric',
|
|
||||||
config: {
|
|
||||||
type: 'CheckboxControl',
|
|
||||||
label: t('Sort by metric'),
|
|
||||||
description: t(
|
|
||||||
'Whether to sort results by the selected metric in descending order.',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t('Chart Options'),
|
|
||||||
expanded: true,
|
|
||||||
controlSetRows: [['color_scheme'], ['linear_color_scheme']],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
controlOverrides: {
|
|
||||||
metric: {
|
|
||||||
label: t('Primary Metric'),
|
|
||||||
description: t(
|
|
||||||
'The primary metric is used to define the arc segment sizes',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
secondary_metric: {
|
|
||||||
label: t('Secondary Metric'),
|
|
||||||
default: null,
|
|
||||||
description: t(
|
|
||||||
'[optional] this secondary metric is used to ' +
|
|
||||||
'define the color as a ratio against the primary metric. ' +
|
|
||||||
'When omitted, the color is categorical and based on labels',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
color_scheme: {
|
|
||||||
description: t(
|
|
||||||
'When only a primary metric is provided, a categorical color scale is used.',
|
|
||||||
),
|
|
||||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
|
||||||
Boolean(
|
|
||||||
!controls?.secondary_metric?.value ||
|
|
||||||
controls?.secondary_metric?.value === controls?.metric.value,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
linear_color_scheme: {
|
|
||||||
description: t(
|
|
||||||
'When a secondary metric is provided, a linear color scale is used.',
|
|
||||||
),
|
|
||||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
|
||||||
Boolean(
|
|
||||||
controls?.secondary_metric?.value &&
|
|
||||||
controls?.secondary_metric?.value !== controls?.metric.value,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
groupby: {
|
|
||||||
label: t('Hierarchy'),
|
|
||||||
description: t('This defines the level of the hierarchy'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
formDataOverrides: formData => ({
|
|
||||||
...formData,
|
|
||||||
groupby: getStandardizedControls().popAllColumns(),
|
|
||||||
metric: getStandardizedControls().shiftMetric(),
|
|
||||||
secondary_metric: getStandardizedControls().shiftMetric(),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
Binary file not shown.
Before Width: | Height: | Size: 139 KiB |
Binary file not shown.
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
Before Width: | Height: | Size: 50 KiB |
|
@ -1,47 +0,0 @@
|
||||||
/**
|
|
||||||
* 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 { t, ChartMetadata, ChartPlugin } from '@superset-ui/core';
|
|
||||||
import transformProps from './transformProps';
|
|
||||||
import thumbnail from './images/thumbnail.png';
|
|
||||||
import example from './images/example.png';
|
|
||||||
import controlPanel from './controlPanel';
|
|
||||||
|
|
||||||
const metadata = new ChartMetadata({
|
|
||||||
category: t('Part of a Whole'),
|
|
||||||
credits: ['https://bl.ocks.org/kerryrodden/7090426'],
|
|
||||||
description: t(
|
|
||||||
'Uses circles to visualize the flow of data through different stages of a system. Hover over individual paths in the visualization to understand the stages a value took. Useful for multi-stage, multi-group visualizing funnels and pipelines.',
|
|
||||||
),
|
|
||||||
exampleGallery: [{ url: example }],
|
|
||||||
name: t('Sunburst Chart'),
|
|
||||||
tags: [t('Aesthetic'), t('Legacy'), t('Multi-Levels'), t('Proportional')],
|
|
||||||
thumbnail,
|
|
||||||
useLegacyApi: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default class SunburstChartPlugin extends ChartPlugin {
|
|
||||||
constructor() {
|
|
||||||
super({
|
|
||||||
loadChart: () => import('./ReactSunburst'),
|
|
||||||
metadata,
|
|
||||||
transformProps,
|
|
||||||
controlPanel,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
export default function transformProps(chartProps) {
|
|
||||||
const { width, height, formData, queriesData, datasource } = chartProps;
|
|
||||||
const { colorScheme, linearColorScheme, metric, secondaryMetric, sliceId } =
|
|
||||||
formData;
|
|
||||||
|
|
||||||
const returnProps = {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
data: queriesData[0].data,
|
|
||||||
colorScheme,
|
|
||||||
linearColorScheme,
|
|
||||||
metrics: [metric, secondaryMetric],
|
|
||||||
sliceId,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (datasource && datasource.metrics) {
|
|
||||||
const metricWithFormat = datasource.metrics.find(
|
|
||||||
({ metric_name: metricName, d3format }) =>
|
|
||||||
metricName === formData.metric && d3format,
|
|
||||||
);
|
|
||||||
if (metricWithFormat) {
|
|
||||||
Object.assign(returnProps, { numberFormat: metricWithFormat.d3format });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnProps;
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Utility function that takes a d3 svg:text selection and a max width, and splits the
|
|
||||||
text's text across multiple tspan lines such that any given line does not exceed max width
|
|
||||||
|
|
||||||
If text does not span multiple lines AND adjustedY is passed,
|
|
||||||
will set the text to the passed val
|
|
||||||
*/
|
|
||||||
import d3 from 'd3';
|
|
||||||
|
|
||||||
export default function wrapSvgText(text, width, adjustedY) {
|
|
||||||
const lineHeight = 1;
|
|
||||||
// ems
|
|
||||||
text.each(function each() {
|
|
||||||
const d3Text = d3.select(this);
|
|
||||||
const words = d3Text.text().split(/\s+/);
|
|
||||||
let line = [];
|
|
||||||
let lineNumber = 0;
|
|
||||||
const x = d3Text.attr('x');
|
|
||||||
const y = d3Text.attr('y');
|
|
||||||
const dy = parseFloat(d3Text.attr('dy'));
|
|
||||||
let tspan = d3Text
|
|
||||||
.text(null)
|
|
||||||
.append('tspan')
|
|
||||||
.attr('x', x)
|
|
||||||
.attr('y', y)
|
|
||||||
.attr('dy', `${dy}em`);
|
|
||||||
|
|
||||||
let didWrap = false;
|
|
||||||
words.forEach(word => {
|
|
||||||
line.push(word);
|
|
||||||
tspan.text(line.join(' '));
|
|
||||||
if (tspan.node().getComputedTextLength() > width) {
|
|
||||||
lineNumber += 1;
|
|
||||||
line.pop();
|
|
||||||
// remove word that pushes over the limit
|
|
||||||
tspan.text(line.join(' '));
|
|
||||||
line = [word];
|
|
||||||
tspan = d3Text
|
|
||||||
.append('tspan')
|
|
||||||
.attr('x', x)
|
|
||||||
.attr('y', y)
|
|
||||||
.attr('dy', `${lineNumber * lineHeight + dy}em`)
|
|
||||||
.text(word);
|
|
||||||
didWrap = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!didWrap && typeof adjustedY !== 'undefined') {
|
|
||||||
tspan.attr('y', adjustedY);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"declarationDir": "lib",
|
|
||||||
"outDir": "lib",
|
|
||||||
"rootDir": "src"
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"lib",
|
|
||||||
"test"
|
|
||||||
],
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"include": [
|
|
||||||
"src/**/*",
|
|
||||||
"types/**/*",
|
|
||||||
"../../types/**/*"
|
|
||||||
],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "../../packages/superset-ui-chart-controls"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "../../packages/superset-ui-core"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -43,7 +43,7 @@ export default class EchartsSunburstChartPlugin extends EchartsChartPlugin {
|
||||||
'Uses circles to visualize the flow of data through different stages of a system. Hover over individual paths in the visualization to understand the stages a value took. Useful for multi-stage, multi-group visualizing funnels and pipelines.',
|
'Uses circles to visualize the flow of data through different stages of a system. Hover over individual paths in the visualization to understand the stages a value took. Useful for multi-stage, multi-group visualizing funnels and pipelines.',
|
||||||
),
|
),
|
||||||
exampleGallery: [{ url: example1 }, { url: example2 }],
|
exampleGallery: [{ url: example1 }, { url: example2 }],
|
||||||
name: t('Sunburst Chart v2'),
|
name: t('Sunburst Chart'),
|
||||||
tags: [
|
tags: [
|
||||||
t('ECharts'),
|
t('ECharts'),
|
||||||
t('Aesthetic'),
|
t('Aesthetic'),
|
||||||
|
|
|
@ -149,7 +149,7 @@ export const sliceEntitiesForDashboard = {
|
||||||
slice_url: '/explore/?form_data=%7B%22slice_id%22%3A%20133%7D',
|
slice_url: '/explore/?form_data=%7B%22slice_id%22%3A%20133%7D',
|
||||||
slice_name: 'Rural Breakdown',
|
slice_name: 'Rural Breakdown',
|
||||||
form_data: {},
|
form_data: {},
|
||||||
viz_type: 'sunburst',
|
viz_type: 'sunburst_v2',
|
||||||
datasource: '2__table',
|
datasource: '2__table',
|
||||||
description: null,
|
description: null,
|
||||||
description_markeddown: '',
|
description_markeddown: '',
|
||||||
|
|
|
@ -37,7 +37,7 @@ jest.mock('src/components/Dropdown', () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const createProps = (viz_type = 'sunburst') =>
|
const createProps = (viz_type = 'sunburst_v2') =>
|
||||||
({
|
({
|
||||||
addDangerToast: jest.fn(),
|
addDangerToast: jest.fn(),
|
||||||
addSuccessToast: jest.fn(),
|
addSuccessToast: jest.fn(),
|
||||||
|
@ -59,7 +59,9 @@ const createProps = (viz_type = 'sunburst') =>
|
||||||
adhoc_filters: [],
|
adhoc_filters: [],
|
||||||
color_scheme: 'supersetColors',
|
color_scheme: 'supersetColors',
|
||||||
datasource: '58__table',
|
datasource: '58__table',
|
||||||
groupby: ['product_category', 'clinical_stage'],
|
...(viz_type === 'sunburst_v2'
|
||||||
|
? { columns: ['product_category', 'clinical_stage'] }
|
||||||
|
: { groupby: ['product_category', 'clinical_stage'] }),
|
||||||
linear_color_scheme: 'schemeYlOrBr',
|
linear_color_scheme: 'schemeYlOrBr',
|
||||||
metric: 'count',
|
metric: 'count',
|
||||||
queryFields: {
|
queryFields: {
|
||||||
|
@ -93,7 +95,7 @@ const createProps = (viz_type = 'sunburst') =>
|
||||||
chartStatus: 'rendered',
|
chartStatus: 'rendered',
|
||||||
showControls: true,
|
showControls: true,
|
||||||
supersetCanShare: true,
|
supersetCanShare: true,
|
||||||
formData: { slice_id: 1, datasource: '58__table', viz_type: 'sunburst' },
|
formData: { slice_id: 1, datasource: '58__table', viz_type: 'sunburst_v2' },
|
||||||
exploreUrl: '/explore',
|
exploreUrl: '/explore',
|
||||||
} as SliceHeaderControlsProps);
|
} as SliceHeaderControlsProps);
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,6 @@ const DEFAULT_ORDER = [
|
||||||
'deck_screengrid',
|
'deck_screengrid',
|
||||||
'treemap_v2',
|
'treemap_v2',
|
||||||
'box_plot',
|
'box_plot',
|
||||||
'sunburst',
|
|
||||||
'sankey',
|
'sankey',
|
||||||
'word_cloud',
|
'word_cloud',
|
||||||
'mapbox',
|
'mapbox',
|
||||||
|
|
|
@ -30,7 +30,6 @@ import ParallelCoordinatesChartPlugin from '@superset-ui/legacy-plugin-chart-par
|
||||||
import PartitionChartPlugin from '@superset-ui/legacy-plugin-chart-partition';
|
import PartitionChartPlugin from '@superset-ui/legacy-plugin-chart-partition';
|
||||||
import RoseChartPlugin from '@superset-ui/legacy-plugin-chart-rose';
|
import RoseChartPlugin from '@superset-ui/legacy-plugin-chart-rose';
|
||||||
import SankeyChartPlugin from '@superset-ui/legacy-plugin-chart-sankey';
|
import SankeyChartPlugin from '@superset-ui/legacy-plugin-chart-sankey';
|
||||||
import SunburstChartPlugin from '@superset-ui/legacy-plugin-chart-sunburst';
|
|
||||||
import TableChartPlugin from '@superset-ui/plugin-chart-table';
|
import TableChartPlugin from '@superset-ui/plugin-chart-table';
|
||||||
import { WordCloudChartPlugin } from '@superset-ui/plugin-chart-word-cloud';
|
import { WordCloudChartPlugin } from '@superset-ui/plugin-chart-word-cloud';
|
||||||
import WorldMapChartPlugin from '@superset-ui/legacy-plugin-chart-world-map';
|
import WorldMapChartPlugin from '@superset-ui/legacy-plugin-chart-world-map';
|
||||||
|
@ -127,7 +126,6 @@ export default class MainPreset extends Preset {
|
||||||
new PivotTableChartPluginV2().configure({ key: 'pivot_table_v2' }),
|
new PivotTableChartPluginV2().configure({ key: 'pivot_table_v2' }),
|
||||||
new RoseChartPlugin().configure({ key: 'rose' }),
|
new RoseChartPlugin().configure({ key: 'rose' }),
|
||||||
new SankeyChartPlugin().configure({ key: 'sankey' }),
|
new SankeyChartPlugin().configure({ key: 'sankey' }),
|
||||||
new SunburstChartPlugin().configure({ key: 'sunburst' }),
|
|
||||||
new TableChartPlugin().configure({ key: 'table' }),
|
new TableChartPlugin().configure({ key: 'table' }),
|
||||||
new TimePivotChartPlugin().configure({ key: 'time_pivot' }),
|
new TimePivotChartPlugin().configure({ key: 'time_pivot' }),
|
||||||
new TimeTableChartPlugin().configure({ key: 'time_table' }),
|
new TimeTableChartPlugin().configure({ key: 'time_table' }),
|
||||||
|
|
|
@ -15,14 +15,14 @@
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
slice_name: Vaccine Candidates per Country & Stage
|
slice_name: Vaccine Candidates per Country & Stage
|
||||||
viz_type: sunburst
|
viz_type: sunburst_v2
|
||||||
params:
|
params:
|
||||||
adhoc_filters: []
|
adhoc_filters: []
|
||||||
color_scheme: supersetColors
|
color_scheme: supersetColors
|
||||||
datasource: 69__table
|
datasource: 69__table
|
||||||
groupby:
|
columns:
|
||||||
- product_category
|
- product_category
|
||||||
- clinical_stage
|
- clinical_stage
|
||||||
linear_color_scheme: schemeYlOrBr
|
linear_color_scheme: schemeYlOrBr
|
||||||
metric: count
|
metric: count
|
||||||
queryFields:
|
queryFields:
|
||||||
|
@ -33,7 +33,7 @@ params:
|
||||||
slice_id: 3964
|
slice_id: 3964
|
||||||
time_range: No filter
|
time_range: No filter
|
||||||
url_params: {}
|
url_params: {}
|
||||||
viz_type: sunburst
|
viz_type: sunburst_v2
|
||||||
cache_timeout: null
|
cache_timeout: null
|
||||||
uuid: f69c556f-15fe-4a82-a8bb-69d5b6954123
|
uuid: f69c556f-15fe-4a82-a8bb-69d5b6954123
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
|
@ -397,12 +397,12 @@ def create_slices(tbl: SqlaTable) -> list[Slice]:
|
||||||
Slice(
|
Slice(
|
||||||
**slice_kwargs,
|
**slice_kwargs,
|
||||||
slice_name="Sunburst Chart",
|
slice_name="Sunburst Chart",
|
||||||
viz_type="sunburst",
|
viz_type="sunburst_v2",
|
||||||
params=get_slice_json(
|
params=get_slice_json(
|
||||||
defaults,
|
defaults,
|
||||||
viz_type="sunburst",
|
viz_type="sunburst_v2",
|
||||||
metric="sum__num",
|
metric="sum__num",
|
||||||
groupby=["gender", "state"],
|
columns=["gender", "state"],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Slice(
|
Slice(
|
||||||
|
|
|
@ -266,13 +266,13 @@ def create_slices(tbl: BaseDatasource) -> list[Slice]:
|
||||||
),
|
),
|
||||||
Slice(
|
Slice(
|
||||||
slice_name="Rural Breakdown",
|
slice_name="Rural Breakdown",
|
||||||
viz_type="sunburst",
|
viz_type="sunburst_v2",
|
||||||
datasource_type=DatasourceType.TABLE,
|
datasource_type=DatasourceType.TABLE,
|
||||||
datasource_id=tbl.id,
|
datasource_id=tbl.id,
|
||||||
params=get_slice_json(
|
params=get_slice_json(
|
||||||
defaults,
|
defaults,
|
||||||
viz_type="sunburst",
|
viz_type="sunburst_v2",
|
||||||
groupby=["region", "country_name"],
|
columns=["region", "country_name"],
|
||||||
since="2011-01-01",
|
since="2011-01-01",
|
||||||
until="2011-01-02",
|
until="2011-01-02",
|
||||||
metric=metric,
|
metric=metric,
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# 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.
|
||||||
|
"""migrate-sunburst-chart
|
||||||
|
|
||||||
|
Revision ID: a32e0c4d8646
|
||||||
|
Revises: 59a1450b3c10
|
||||||
|
Create Date: 2023-12-22 14:41:43.638321
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "a32e0c4d8646"
|
||||||
|
down_revision = "59a1450b3c10"
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
from superset import db
|
||||||
|
from superset.migrations.shared.migrate_viz import MigrateSunburst
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
bind = op.get_bind()
|
||||||
|
session = db.Session(bind=bind)
|
||||||
|
MigrateSunburst.upgrade(session)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
bind = op.get_bind()
|
||||||
|
session = db.Session(bind=bind)
|
||||||
|
MigrateSunburst.downgrade(session)
|
|
@ -1352,55 +1352,6 @@ class DistributionBarViz(BaseViz):
|
||||||
return chart_data
|
return chart_data
|
||||||
|
|
||||||
|
|
||||||
class SunburstViz(BaseViz):
|
|
||||||
|
|
||||||
"""A multi level sunburst chart"""
|
|
||||||
|
|
||||||
viz_type = "sunburst"
|
|
||||||
verbose_name = _("Sunburst")
|
|
||||||
is_timeseries = False
|
|
||||||
credits = (
|
|
||||||
"Kerry Rodden "
|
|
||||||
'@<a href="https://bl.ocks.org/kerryrodden/7090426">bl.ocks.org</a>'
|
|
||||||
)
|
|
||||||
|
|
||||||
@deprecated(deprecated_in="3.0")
|
|
||||||
def get_data(self, df: pd.DataFrame) -> VizData:
|
|
||||||
if df.empty:
|
|
||||||
return None
|
|
||||||
form_data = copy.deepcopy(self.form_data)
|
|
||||||
cols = get_column_names(form_data.get("groupby"))
|
|
||||||
cols.extend(["m1", "m2"])
|
|
||||||
metric = utils.get_metric_name(form_data["metric"])
|
|
||||||
secondary_metric = (
|
|
||||||
utils.get_metric_name(form_data["secondary_metric"])
|
|
||||||
if form_data.get("secondary_metric")
|
|
||||||
else None
|
|
||||||
)
|
|
||||||
if metric == secondary_metric or secondary_metric is None:
|
|
||||||
df.rename(columns={df.columns[-1]: "m1"}, inplace=True)
|
|
||||||
df["m2"] = df["m1"]
|
|
||||||
else:
|
|
||||||
df.rename(columns={df.columns[-2]: "m1"}, inplace=True)
|
|
||||||
df.rename(columns={df.columns[-1]: "m2"}, inplace=True)
|
|
||||||
|
|
||||||
# Re-order the columns as the query result set column ordering may differ from
|
|
||||||
# that listed in the hierarchy.
|
|
||||||
df = df[cols]
|
|
||||||
return df.to_numpy().tolist()
|
|
||||||
|
|
||||||
@deprecated(deprecated_in="3.0")
|
|
||||||
def query_obj(self) -> QueryObjectDict:
|
|
||||||
query_obj = super().query_obj()
|
|
||||||
query_obj["metrics"] = [self.form_data["metric"]]
|
|
||||||
secondary_metric = self.form_data.get("secondary_metric")
|
|
||||||
if secondary_metric and secondary_metric != self.form_data["metric"]:
|
|
||||||
query_obj["metrics"].append(secondary_metric)
|
|
||||||
if self.form_data.get("sort_by_metric", False):
|
|
||||||
query_obj["orderby"] = [(query_obj["metrics"][0], False)]
|
|
||||||
return query_obj
|
|
||||||
|
|
||||||
|
|
||||||
class SankeyViz(BaseViz):
|
class SankeyViz(BaseViz):
|
||||||
|
|
||||||
"""A Sankey diagram that requires a parent-child dataset"""
|
"""A Sankey diagram that requires a parent-child dataset"""
|
||||||
|
|
Loading…
Reference in New Issue