From bbf7586fe80b065704cf09dac6dda651a017913c Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Tue, 14 Jan 2025 13:59:32 -0800 Subject: [PATCH] fix: fix/suppress webpack console warnings (#31830) --- .../src/layers/common.test.ts | 145 ++++++++++++++++++ .../src/layers/common.tsx | 41 ++++- superset-frontend/webpack.config.js | 3 + 3 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts new file mode 100644 index 000000000..87b134f8b --- /dev/null +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts @@ -0,0 +1,145 @@ +/** + * 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 { JsonObject, QueryFormData } from '@superset-ui/core'; +import { getAggFunc, commonLayerProps } from './common'; + +const partialformData: Partial = { + viz_type: 'table', + datasource: '3_sqla', +}; + +describe('getAggFunc', () => { + it('returns correct function for sum', () => { + const aggFunc = getAggFunc('sum'); + const result = aggFunc([1, 2, 3, 4]); + expect(result).toBe(10); + }); + + it('returns correct function for min', () => { + const aggFunc = getAggFunc('min'); + const result = aggFunc([1, 2, 3, 4]); + expect(result).toBe(1); + }); + + it('returns correct function for max', () => { + const aggFunc = getAggFunc('max'); + const result = aggFunc([1, 2, 3, 4]); + expect(result).toBe(4); + }); + + it('returns correct function for mean', () => { + const aggFunc = getAggFunc('mean'); + const result = aggFunc([1, 2, 3, 4]); + expect(result).toBe(2.5); + }); + + it('returns correct function for median', () => { + const aggFunc = getAggFunc('median'); + const result = aggFunc([1, 2, 3, 4, 5]); + expect(result).toBe(3); + }); + + it('returns correct function for variance', () => { + const aggFunc = getAggFunc('variance'); + const result = aggFunc([1, 2, 3, 4]); + expect(result).toBeCloseTo(1.6666666666666667); + }); + + it('returns correct function for deviation', () => { + const aggFunc = getAggFunc('deviation'); + const result = aggFunc([1, 2, 3, 4]); + expect(result).toBeCloseTo(1.29099, 5); + }); + + it('returns correct function for count', () => { + const aggFunc = getAggFunc('count'); + const result = aggFunc([1, 2, 3, 4]); + expect(result).toBe(4); + }); + + it('returns correct function for p95 (percentiles)', () => { + const aggFunc = getAggFunc('p95'); + const result = aggFunc([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + expect(result).toBeCloseTo(9.55, 5); + }); + + it('throws an error for unsupported aggregation type', () => { + expect(() => getAggFunc('unsupported')).toThrow( + 'Unsupported aggregation type: unsupported', + ); + }); +}); + +describe('commonLayerProps', () => { + const mockSetTooltip = jest.fn(); + const mockSetTooltipContent = jest.fn( + () => (o: JsonObject) => `Tooltip for ${o}`, + ); + const mockOnSelect = jest.fn(); + + it('returns correct props when js_tooltip is provided', () => { + const formData = { + ...partialformData, + js_tooltip: 'tooltip => tooltip.content', + } as QueryFormData; + const props = commonLayerProps( + formData, + mockSetTooltip, + mockSetTooltipContent, + ); + expect(props.pickable).toBe(true); + expect(props.onHover).toBeDefined(); + }); + + it('calls onHover and sets tooltip', () => { + const formData = { ...partialformData, js_tooltip: null } as QueryFormData; + const props = commonLayerProps( + formData, + mockSetTooltip, + mockSetTooltipContent, + ); + + const mockObject = { picked: true, x: 10, y: 20 }; + props.onHover?.(mockObject); + expect(mockSetTooltip).toHaveBeenCalledWith({ + content: expect.any(Function), // Matches any function + x: 10, + y: 20, + }); + }); + + it('calls onSelect when table_filter is enabled', () => { + const formData = { + ...partialformData, + table_filter: true, + line_column: 'name', + } as QueryFormData; + const props = commonLayerProps( + formData, + mockSetTooltip, + mockSetTooltipContent, + mockOnSelect, + ); + + const mockObject = { object: { name: 'John Doe' } }; + props.onClick?.(mockObject); + expect(mockOnSelect).toHaveBeenCalledWith('John Doe'); + }); +}); diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.tsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.tsx index 4ff84f90e..111614a76 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.tsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.tsx @@ -11,15 +11,23 @@ * * 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 + * "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 { ReactNode } from 'react'; -import d3array, { +import { ascending as d3ascending, quantile as d3quantile, + count as d3count, + sum as d3sum, + mean as d3mean, + min as d3min, + max as d3max, + median as d3median, + variance as d3variance, + deviation as d3deviation, } from 'd3-array'; import { JsonObject, JsonValue, QueryFormData } from '@superset-ui/core'; import sandboxedEval from '../utils/sandbox'; @@ -79,6 +87,18 @@ const percentiles = { p99: 0.99, }; +/* Supported d3-array functions */ +const d3functions: Record = { + sum: d3sum, + count: d3count, + min: d3min, + max: d3max, + mean: d3mean, + median: d3median, + variance: d3variance, + deviation: d3deviation, +}; + /* Get a stat function that operates on arrays, aligns with control=js_agg_function */ export function getAggFunc( type = 'sum', @@ -87,10 +107,12 @@ export function getAggFunc( if (type === 'count') { return (arr: number[]) => arr.length; } + let d3func: ( iterable: Array, accessor?: (object: JsonObject) => number | undefined, ) => number[] | number | undefined; + if (type in percentiles) { d3func = (arr, acc: (object: JsonObject) => number | undefined) => { let sortedArr; @@ -104,12 +126,15 @@ export function getAggFunc( return d3quantile(sortedArr, percentiles[type], acc); }; + } else if (type in d3functions) { + d3func = d3functions[type]; } else { - d3func = d3array[type]; - } - if (!accessor) { - return (arr: JsonObject[]) => d3func(arr); + throw new Error(`Unsupported aggregation type: ${type}`); } - return (arr: JsonObject[]) => d3func(arr.map(x => accessor(x))); + if (!accessor) { + return (arr: number[]) => d3func(arr); + } + + return (arr: number[]) => d3func(arr.map(x => accessor(x))); } diff --git a/superset-frontend/webpack.config.js b/superset-frontend/webpack.config.js index 83b250763..1434d8818 100644 --- a/superset-frontend/webpack.config.js +++ b/superset-frontend/webpack.config.js @@ -231,6 +231,9 @@ const config = { message: /export 'withTooltipPropTypes' \(imported as 'vxTooltipPropTypes'\) was not found/, }, + { + message: /Can't resolve.*superset_text/, + }, ], performance: { assetFilter(assetFilename) {