chore(viz): remove legacy explore_json + viz.py pipeline (#41714)

Co-authored-by: Claude Code <noreply@anthropic.com>
This commit is contained in:
Evan Rusackas
2026-08-06 13:22:15 -07:00
committed by GitHub
co-authored by Claude Code
parent 01a9fdc621
commit 2ecce20e48
620 changed files with 12961 additions and 17622 deletions
@@ -0,0 +1,173 @@
/**
* 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 react/sort-prop-types */
import * as d3 from 'd3v3';
import { getSequentialSchemeRegistry } from '@superset-ui/core';
import parcoords from './vendor/parcoords/d3.parcoords';
import divgrid from './vendor/parcoords/divgrid';
interface ParcoordChart {
width(w: number): ParcoordChart;
height(h: number): ParcoordChart;
color(c: Function): ParcoordChart;
alpha(a: number): ParcoordChart;
composite(c: string): ParcoordChart;
data(d: Record<string, unknown>[]): ParcoordChart;
dimensions(cols: string[]): ParcoordChart;
types(t: Record<string, string>): ParcoordChart;
render(): ParcoordChart;
createAxes(): ParcoordChart;
shadows(): ParcoordChart;
reorderable(): ParcoordChart;
brushMode(mode: string): ParcoordChart;
highlight(d: Record<string, unknown>[]): void;
unhighlight(): void;
on(event: string, callback: Function): void;
}
interface ParallelCoordinatesProps {
data: Record<string, unknown>[];
width: number;
height: number;
colorMetric: string;
defaultLineColor: string;
includeSeries: boolean;
isDarkMode: boolean;
linearColorScheme: string;
metrics: string[];
series: string;
showDatatable: boolean;
}
function ParallelCoordinates(
element: HTMLElement,
props: ParallelCoordinatesProps,
) {
const {
data,
width,
height,
colorMetric,
defaultLineColor,
includeSeries,
isDarkMode,
linearColorScheme,
metrics,
series,
showDatatable,
} = props;
const cols = includeSeries ? [series].concat(metrics) : metrics;
const ttypes: Record<string, string> = {};
ttypes[series] = 'string';
metrics.forEach(v => {
ttypes[v] = 'number';
});
const colorScale = colorMetric
? getSequentialSchemeRegistry()
.get(linearColorScheme)
?.createLinearScale(
d3.extent(
data,
(d: Record<string, unknown>) => d[colorMetric] as number,
),
)
: null;
const brightenForDarkMode = (colorStr: string): string => {
const hsl = d3.hsl(colorStr);
if (hsl.l < 0.5) {
hsl.l = Math.min(1, hsl.l + 0.4);
return hsl.toString();
}
return colorStr;
};
const color = (d: Record<string, unknown>): string => {
if (!colorScale) {
return defaultLineColor;
}
const baseColor = (colorScale as Function)(d[colorMetric]) as string;
return isDarkMode ? brightenForDarkMode(baseColor) : baseColor;
};
const container = d3
.select(element)
.classed('superset-legacy-chart-parallel-coordinates', true);
container.selectAll('*').remove();
const effHeight = showDatatable ? height / 2 : height;
const div = container
.append('div')
.style('height', `${effHeight}px`)
.classed('parcoords', true);
const chart = (parcoords()(div.node()) as unknown as ParcoordChart)
.width(width)
.color(color)
.alpha(0.5)
.composite(isDarkMode ? 'screen' : 'darken')
.height(effHeight)
.data(data)
.dimensions(cols)
.types(ttypes)
.render()
.createAxes()
.shadows()
.reorderable()
.brushMode('1D-axes');
if (showDatatable) {
// create data table, row hover highlighting
const grid = divgrid();
container
.append('div')
.style('height', `${effHeight}px`)
.datum(data)
.call(grid)
.classed('parcoords grid', true)
.selectAll('.row')
.on({
mouseover(d: Record<string, unknown>) {
chart.highlight([d]);
},
mouseout: chart.unhighlight,
});
// update data table on brush event
chart.on('brush', (d: Record<string, unknown>[]) => {
d3.select('.grid')
.datum(d)
.call(grid)
.selectAll('.row')
.on({
mouseover(dd: Record<string, unknown>) {
chart.highlight([dd]);
},
mouseout: chart.unhighlight,
});
});
}
}
ParallelCoordinates.displayName = 'ParallelCoordinates';
export default ParallelCoordinates;
@@ -0,0 +1,137 @@
/**
* 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 { type ComponentProps } from 'react';
import { reactify, addAlpha } from '@superset-ui/core';
import { styled } from '@apache-superset/core/theme';
import Component from './ParallelCoordinates';
const ReactComponent = reactify(Component);
interface ParallelCoordinatesWrapperProps {
className?: string;
[key: string]: unknown;
}
const ParallelCoordinates = ({
className,
...otherProps
}: ParallelCoordinatesWrapperProps) => (
<div className={className}>
{/* Props are injected by the chart framework at runtime */}
<ReactComponent
{...(otherProps as unknown as ComponentProps<typeof ReactComponent>)}
/>
</div>
);
export default styled(ParallelCoordinates)`
${({ theme }) => `
.superset-legacy-chart-parallel-coordinates {
div.grid {
overflow: auto;
div.row {
&:hover {
background-color: ${theme.colorBgTextHover};
}
}
}
}
.parcoords svg,
.parcoords canvas {
font-size: ${theme.fontSizeSM}px;
position: absolute;
}
.parcoords > canvas {
pointer-events: none;
}
.parcoords text.label {
font: 100%;
font-size: ${theme.fontSizeSM}px;
fill: ${theme.colorText};
cursor: drag;
}
.parcoords rect.background {
fill: transparent;
}
.parcoords rect.background:hover {
fill: ${addAlpha(theme.colorBorder, 0.2)};
}
.parcoords .resize rect {
fill: ${addAlpha(theme.colorText, 0.1)};
}
.parcoords rect.extent {
fill: ${addAlpha(theme.colorBgContainer, 0.25)};
stroke: ${addAlpha(theme.colorText, 0.6)};
}
.parcoords .axis line,
.parcoords .axis path {
fill: none;
stroke: ${theme.colorText};
shape-rendering: crispEdges;
}
.parcoords .axis text {
fill: ${theme.colorText};
}
.parcoords canvas {
opacity: 1;
-moz-transition: opacity 0.3s;
-webkit-transition: opacity 0.3s;
-o-transition: opacity 0.3s;
}
.parcoords canvas.faded {
opacity: 35%;
}
.parcoords {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: ${theme.colorBgContainer};
}
/* data table styles */
.parcoords .row,
.parcoords .header {
clear: left;
font-size: ${theme.fontSizeSM}px;
line-height: 18px;
height: 18px;
margin: 0px;
}
.parcoords .row:nth-of-type(odd) {
background: ${addAlpha(theme.colorText, 0.05)};
}
.parcoords .header {
font-weight: ${theme.fontWeightStrong};
}
.parcoords .cell {
float: left;
overflow: hidden;
white-space: nowrap;
width: 100px;
height: 18px;
}
.parcoords .col-0 {
width: 180px;
}
`}
`;
@@ -0,0 +1,53 @@
/**
* 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,
QueryFormData,
QueryFormMetric,
} from '@superset-ui/core';
import { buildSortMetricOrderby } from '@superset-ui/chart-controls';
/**
* Mirrors the query the legacy `para` viz built server-side: one query
* grouped by `series` selecting all metrics (including the secondary
* "color" metric, aliased into `metrics` by extractQueryFields). The
* sort metric is added to the select list so its ordering is visible in
* the result, and ordering is applied when `order_desc` is set.
*/
export default function buildQuery(formData: QueryFormData) {
const { timeseries_limit_metric, order_desc } = formData;
return buildQueryContext(formData, baseQueryObject => {
const { metrics, orderby } = buildSortMetricOrderby({
metrics: ensureIsArray(baseQueryObject.metrics) as QueryFormMetric[],
timeseriesLimitMetric: timeseries_limit_metric,
order_desc,
orderOnlyWhenDesc: true,
});
return [
{
...baseQueryObject,
metrics,
// own the ordering entirely: the legacy pipeline ignored residual
// orderby fields (e.g. order_by_cols left over from other viz types)
orderby: orderby.length > 0 ? orderby : undefined,
},
];
});
}
@@ -0,0 +1,69 @@
/**
* 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 '@apache-superset/core/translation';
import { ControlPanelConfig } from '@superset-ui/chart-controls';
const config: ControlPanelConfig = {
controlPanelSections: [
{
label: t('Query'),
expanded: true,
controlSetRows: [
['series'],
['metrics'],
['secondary_metric'],
['adhoc_filters'],
['limit', 'row_limit'],
['timeseries_limit_metric'],
['order_desc'],
],
},
{
label: t('Options'),
expanded: true,
controlSetRows: [
[
{
name: 'show_datatable',
config: {
type: 'CheckboxControl',
label: t('Data Table'),
default: false,
renderTrigger: true,
description: t('Whether to display the interactive data table'),
},
},
{
name: 'include_series',
config: {
type: 'CheckboxControl',
label: t('Include Series'),
renderTrigger: true,
default: false,
description: t('Include series name as an axis'),
},
},
],
['linear_color_scheme'],
],
},
],
};
export default config;
Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

@@ -0,0 +1,56 @@
/**
* 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 '@apache-superset/core/translation';
import { ChartMetadata, ChartPlugin } from '@superset-ui/core';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
import thumbnailDark from './images/thumbnail-dark.png';
import example1 from './images/example1.jpg';
import example1Dark from './images/example1-dark.jpg';
import example2 from './images/example2.jpg';
import example2Dark from './images/example2-dark.jpg';
import controlPanel from './controlPanel';
const metadata = new ChartMetadata({
category: t('Ranking'),
credits: ['https://syntagmatic.github.io/parallel-coordinates'],
description: t(
'Plots the individual metrics for each row in the data vertically and links them together as a line. This chart is useful for comparing multiple metrics across all of the samples or rows in the data.',
),
exampleGallery: [
{ url: example1, urlDark: example1Dark },
{ url: example2, urlDark: example2Dark },
],
name: t('Parallel Coordinates'),
tags: [t('Directional'), t('Relational')],
thumbnail,
thumbnailDark,
});
export default class ParallelCoordinatesChartPlugin extends ChartPlugin {
constructor() {
super({
loadChart: () => import('./ReactParallelCoordinates'),
loadBuildQuery: () => import('./buildQuery'),
metadata,
transformProps,
controlPanel,
});
}
}
@@ -0,0 +1,86 @@
/*
* 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 { SuperChart } from '@superset-ui/core';
import ParallelCoordinatesChartPlugin from '@superset-ui/plugin-chart-parallel-coordinates';
import { withResizableChartDemo } from '@storybook-shared';
import data from './data';
new ParallelCoordinatesChartPlugin()
.configure({ key: 'parallel-coordinates' })
.register();
export default {
title: 'Chart Plugins/plugin-chart-parallel-coordinates',
decorators: [withResizableChartDemo],
args: {
includeSeries: false,
linearColorScheme: 'schemeRdYlBu',
showDatatable: false,
},
argTypes: {
includeSeries: {
control: 'boolean',
description: 'Include series name in the chart',
},
linearColorScheme: {
control: 'select',
options: [
'schemeRdYlBu',
'schemeBlues',
'schemeGreens',
'schemeOranges',
'schemePurples',
],
},
showDatatable: {
control: 'boolean',
description: 'Show data table below chart',
},
},
};
export const Basic = ({
includeSeries,
linearColorScheme,
showDatatable,
width,
height,
}: {
includeSeries: boolean;
linearColorScheme: string;
showDatatable: boolean;
width: number;
height: number;
}) => (
<SuperChart
chartType="parallel-coordinates"
width={width}
height={height}
queriesData={[{ data }]}
formData={{
include_series: includeSeries,
linear_color_scheme: linearColorScheme,
metrics: ['sum__SP_POP_TOTL', 'sum__SP_RUR_TOTL_ZS', 'sum__SH_DYN_AIDS'],
secondary_metric: 'sum__SP_POP_TOTL',
series: 'country_name',
show_datatable: showDatatable,
}}
/>
);
@@ -0,0 +1,81 @@
/*
* 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 [
{
country_name: 'China',
sum__SP_POP_TOTL: 1344130000.0,
sum__SP_RUR_TOTL_ZS: 49.427,
sum__SH_DYN_AIDS: 0.0,
},
{
country_name: 'India',
sum__SP_POP_TOTL: 1247446011.0,
sum__SP_RUR_TOTL_ZS: 68.724,
sum__SH_DYN_AIDS: 0.0,
},
{
country_name: 'United States',
sum__SP_POP_TOTL: 311721632.0,
sum__SP_RUR_TOTL_ZS: 19.06,
sum__SH_DYN_AIDS: 0.0,
},
{
country_name: 'Indonesia',
sum__SP_POP_TOTL: 244808254.0,
sum__SP_RUR_TOTL_ZS: 49.288,
sum__SH_DYN_AIDS: 540000.0,
},
{
country_name: 'Brazil',
sum__SP_POP_TOTL: 200517584.0,
sum__SP_RUR_TOTL_ZS: 15.377,
sum__SH_DYN_AIDS: 0.0,
},
{
country_name: 'Pakistan',
sum__SP_POP_TOTL: 173669648.0,
sum__SP_RUR_TOTL_ZS: 62.993,
sum__SH_DYN_AIDS: 52000.0,
},
{
country_name: 'Nigeria',
sum__SP_POP_TOTL: 163770669.0,
sum__SP_RUR_TOTL_ZS: 55.638,
sum__SH_DYN_AIDS: 3000000.0,
},
{
country_name: 'Bangladesh',
sum__SP_POP_TOTL: 153405612.0,
sum__SP_RUR_TOTL_ZS: 68.775,
sum__SH_DYN_AIDS: 7800.0,
},
{
country_name: 'Russian Federation',
sum__SP_POP_TOTL: 142960868.0,
sum__SP_RUR_TOTL_ZS: 26.268,
sum__SH_DYN_AIDS: 0.0,
},
{
country_name: 'Japan',
sum__SP_POP_TOTL: 127817277.0,
sum__SP_RUR_TOTL_ZS: 8.752,
sum__SH_DYN_AIDS: 0.0,
},
];
@@ -0,0 +1,48 @@
/**
* 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 { ChartProps } from '@superset-ui/core';
import { isThemeDark } from '@apache-superset/core/theme';
export default function transformProps(chartProps: ChartProps) {
const { width, height, formData, queriesData, theme } = chartProps;
const {
includeSeries,
linearColorScheme,
metrics,
secondaryMetric,
series,
showDatatable,
} = formData;
return {
width,
height,
data: queriesData[0].data,
defaultLineColor: theme.colorTextTertiary,
includeSeries,
isDarkMode: isThemeDark(theme),
linearColorScheme,
metrics: metrics.map((m: { label?: string } | string) =>
typeof m === 'string' ? m : m.label || m,
),
colorMetric: secondaryMetric?.label || secondaryMetric,
series,
showDatatable,
};
}
@@ -0,0 +1,78 @@
/* [LICENSE TBD] */
// @ts-nocheck
/* eslint-disable */
// from http://bl.ocks.org/3687826
export default function (config?) {
var columns = [];
var dg = function (selection) {
if (columns.length == 0) columns = d3.keys(selection.data()[0][0]);
// header
selection
.selectAll('.header')
.data([true])
.enter()
.append('div')
.attr('class', 'header');
var header = selection.select('.header').selectAll('.cell').data(columns);
header
.enter()
.append('div')
.attr('class', function (d, i) {
return 'col-' + i;
})
.classed('cell', true);
selection.selectAll('.header .cell').text(function (d) {
return d;
});
header.exit().remove();
// rows
var rows = selection.selectAll('.row').data(function (d) {
return d;
});
rows.enter().append('div').attr('class', 'row');
rows.exit().remove();
var cells = selection
.selectAll('.row')
.selectAll('.cell')
.data(function (d) {
return columns.map(function (col) {
return d[col];
});
});
// cells
cells
.enter()
.append('div')
.attr('class', function (d, i) {
return 'col-' + i;
})
.classed('cell', true);
cells.exit().remove();
selection.selectAll('.cell').text(function (d) {
return d;
});
return dg;
};
dg.columns = function (_) {
if (!arguments.length) return columns;
columns = _;
return this;
};
return dg;
}