mirror of
https://github.com/apache/superset.git
synced 2026-04-20 16:44:46 +00:00
chore(frontend): comprehensive TypeScript quality improvements (#37625)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,27 +17,67 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
// @ts-nocheck -- Legacy D3 visualization using d3-hierarchy v1 APIs without proper type definitions.
|
||||
// Uses the same approach as NVD3Vis.ts and other heavily D3-dependent files.
|
||||
/* eslint no-param-reassign: [2, {"props": false}] */
|
||||
import d3 from 'd3';
|
||||
import PropTypes from 'prop-types';
|
||||
import { hierarchy } from 'd3-hierarchy';
|
||||
import { hierarchy, HierarchyNode } from 'd3-hierarchy';
|
||||
import {
|
||||
getNumberFormatter,
|
||||
getTimeFormatter,
|
||||
CategoricalColorNamespace,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
interface PartitionDataNode {
|
||||
name: string;
|
||||
val: number;
|
||||
children?: PartitionDataNode[];
|
||||
}
|
||||
|
||||
interface PartitionNode extends HierarchyNode<PartitionDataNode> {
|
||||
x: number;
|
||||
dx: number;
|
||||
y: number;
|
||||
dy: number;
|
||||
weight: number;
|
||||
sum: number;
|
||||
name: string;
|
||||
disp: string | number;
|
||||
color: string;
|
||||
children?: PartitionNode[];
|
||||
parent: PartitionNode | null;
|
||||
}
|
||||
|
||||
interface IcicleProps {
|
||||
data: PartitionDataNode[];
|
||||
width: number;
|
||||
height: number;
|
||||
colorScheme: string;
|
||||
dateTimeFormat: string;
|
||||
equalDateSize: boolean;
|
||||
levels: string[];
|
||||
metrics: (string | Record<string, unknown>)[];
|
||||
numberFormat: string;
|
||||
partitionLimit: number;
|
||||
partitionThreshold: number;
|
||||
timeSeriesOption: string;
|
||||
useLogScale: boolean;
|
||||
useRichTooltip: boolean;
|
||||
sliceId: number;
|
||||
}
|
||||
|
||||
// Compute dx, dy, x, y for each node and
|
||||
// return an array of nodes in breadth-first order
|
||||
function init(root) {
|
||||
const flat = [];
|
||||
function init(root: PartitionNode): PartitionNode[] {
|
||||
const flat: PartitionNode[] = [];
|
||||
const dy = 1 / (root.height + 1);
|
||||
let prev = null;
|
||||
root.each(n => {
|
||||
let prev: PartitionNode | null = null;
|
||||
root.each((n: PartitionNode) => {
|
||||
n.y = dy * n.depth;
|
||||
n.dy = dy;
|
||||
if (n.parent) {
|
||||
n.x = prev.depth === n.parent.depth ? 0 : prev.x + prev.dx;
|
||||
n.x = prev!.depth === n.parent.depth ? 0 : prev!.x + prev!.dx;
|
||||
n.dx = (n.weight / n.parent.sum) * n.parent.dx;
|
||||
} else {
|
||||
n.x = 0;
|
||||
@@ -52,8 +92,11 @@ function init(root) {
|
||||
|
||||
// Declare PropTypes for recursive data structures
|
||||
// https://github.com/facebook/react/issues/5676
|
||||
/* eslint-disable-next-line no-undef */
|
||||
const lazyFunction = f => () => f().apply(this, arguments);
|
||||
// eslint-disable-next-line func-names
|
||||
const lazyFunction = (f: () => Record<string, unknown>) =>
|
||||
function (...args: unknown[]) {
|
||||
return f().apply(this, args);
|
||||
};
|
||||
const leafType = PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
val: PropTypes.number.isRequired,
|
||||
@@ -89,9 +132,9 @@ const propTypes = {
|
||||
useRichTooltip: PropTypes.bool,
|
||||
};
|
||||
|
||||
function getAncestors(d) {
|
||||
const ancestors = [d];
|
||||
let node = d;
|
||||
function getAncestors(d: PartitionNode): PartitionNode[] {
|
||||
const ancestors: PartitionNode[] = [d];
|
||||
let node: PartitionNode = d;
|
||||
while (node.parent) {
|
||||
ancestors.push(node.parent);
|
||||
node = node.parent;
|
||||
@@ -102,7 +145,7 @@ function getAncestors(d) {
|
||||
|
||||
// This vis is based on
|
||||
// http://mbostock.github.io/d3/talk/20111018/partition.html
|
||||
function Icicle(element, props) {
|
||||
function Icicle(element: HTMLElement, props: IcicleProps): void {
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
@@ -134,11 +177,11 @@ function Icicle(element, props) {
|
||||
div.selectAll('*').remove();
|
||||
const tooltip = div.append('div').classed('partition-tooltip', true);
|
||||
|
||||
function hasDateNode(n) {
|
||||
function hasDateNode(n: PartitionNode): boolean {
|
||||
return metrics.includes(n.data.name) && hasTime;
|
||||
}
|
||||
|
||||
function getCategory(depth) {
|
||||
function getCategory(depth: number): string {
|
||||
if (!depth) {
|
||||
return 'Metric';
|
||||
}
|
||||
@@ -149,7 +192,7 @@ function Icicle(element, props) {
|
||||
return levels[depth - (hasTime ? 2 : 1)];
|
||||
}
|
||||
|
||||
function drawVis(i, dat) {
|
||||
function drawVis(i: number, dat: PartitionDataNode[]): void {
|
||||
const datum = dat[i];
|
||||
const w = width;
|
||||
const h = height / data.length;
|
||||
@@ -262,7 +305,10 @@ function Icicle(element, props) {
|
||||
: 1;
|
||||
});
|
||||
|
||||
function positionAndPopulate(tip, d) {
|
||||
function positionAndPopulate(
|
||||
tip: ReturnType<typeof div.append>,
|
||||
d: PartitionNode,
|
||||
): void {
|
||||
let t = '<table>';
|
||||
if (useRichTooltip) {
|
||||
const nodes = getAncestors(d);
|
||||
@@ -310,7 +356,7 @@ function Icicle(element, props) {
|
||||
let zoomY = h / 1;
|
||||
|
||||
// Keep text centered in its division
|
||||
function transform(d) {
|
||||
function transform(d: PartitionNode): string {
|
||||
return `translate(8,${(d.dx * zoomY) / 2})`;
|
||||
}
|
||||
|
||||
@@ -332,7 +378,7 @@ function Icicle(element, props) {
|
||||
});
|
||||
|
||||
// When clicking a subdivision, the vis will zoom into it
|
||||
function click(d) {
|
||||
function click(d: PartitionNode): boolean {
|
||||
if (!d.children) {
|
||||
if (d.parent) {
|
||||
// Clicking on the rightmost level should zoom in
|
||||
@@ -20,9 +20,22 @@ import { reactify } from '@superset-ui/core';
|
||||
import { styled } from '@apache-superset/core/ui';
|
||||
import Component from './Partition';
|
||||
|
||||
const ReactComponent = reactify(Component);
|
||||
// Type-erase the render function to allow flexible prop spreading in the wrapper.
|
||||
// The Partition render function has typed props, but the wrapper passes props via spread
|
||||
// which TypeScript cannot verify at compile time. Props are validated at runtime.
|
||||
const ReactComponent = reactify(
|
||||
Component as unknown as (
|
||||
container: HTMLDivElement,
|
||||
props: Record<string, unknown>,
|
||||
) => void,
|
||||
);
|
||||
|
||||
const Partition = ({ className, ...otherProps }) => (
|
||||
interface PartitionWrapperProps {
|
||||
className?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const Partition = ({ className, ...otherProps }: PartitionWrapperProps) => (
|
||||
<div className={className}>
|
||||
<ReactComponent {...otherProps} />
|
||||
</div>
|
||||
@@ -41,7 +54,7 @@ export default styled(Partition)`
|
||||
}
|
||||
|
||||
.superset-legacy-chart-partition rect {
|
||||
stroke: ${theme.borderColorSecondary};
|
||||
stroke: ${theme.colorBorderSecondary};
|
||||
fill: ${theme.colorBgLayout};
|
||||
fill-opacity: 80%;
|
||||
transition: fill-opacity 180ms linear;
|
||||
@@ -16,7 +16,9 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
export default function transformProps(chartProps) {
|
||||
import { ChartProps } from '@superset-ui/core';
|
||||
|
||||
export default function transformProps(chartProps: ChartProps) {
|
||||
const { width, height, datasource, formData, queriesData } = chartProps;
|
||||
const {
|
||||
colorScheme,
|
||||
@@ -32,7 +34,7 @@ export default function transformProps(chartProps) {
|
||||
timeSeriesOption,
|
||||
sliceId,
|
||||
} = formData;
|
||||
const { verboseMap } = datasource;
|
||||
const { verboseMap = {} } = datasource;
|
||||
|
||||
return {
|
||||
width,
|
||||
@@ -41,7 +43,7 @@ export default function transformProps(chartProps) {
|
||||
colorScheme,
|
||||
dateTimeFormat,
|
||||
equalDateSize,
|
||||
levels: groupby.map(g => verboseMap[g] || g),
|
||||
levels: groupby.map((g: string) => verboseMap[g] || g),
|
||||
metrics,
|
||||
numberFormat,
|
||||
partitionLimit: partitionLimit && parseInt(partitionLimit, 10),
|
||||
@@ -18,13 +18,14 @@
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import { screen, render, fireEvent, act } from '@superset-ui/core/spec';
|
||||
import type { ColumnMeta } from '@superset-ui/chart-controls';
|
||||
import OptionDescription from '../src/OptionDescription';
|
||||
|
||||
const defaultProps = {
|
||||
option: {
|
||||
label: 'Some option',
|
||||
description: 'Description for some option',
|
||||
},
|
||||
} as unknown as ColumnMeta,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
28
superset-frontend/plugins/legacy-plugin-chart-partition/types/external.d.ts
vendored
Normal file
28
superset-frontend/plugins/legacy-plugin-chart-partition/types/external.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare module '*.png' {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const value: string;
|
||||
export default value;
|
||||
}
|
||||
Reference in New Issue
Block a user