diff --git a/superset-frontend/temporary_superset_ui/superset-ui/package.json b/superset-frontend/temporary_superset_ui/superset-ui/package.json index fa649f763c39..f52207405d4c 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/package.json +++ b/superset-frontend/temporary_superset_ui/superset-ui/package.json @@ -176,6 +176,7 @@ { "files": "*.{js,jsx,ts,tsx}", "rules": { + "no-plusplus": "off", "react/jsx-no-literals": "off", "@typescript-eslint/no-explicit-any": [ "warn", diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/README.md b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/README.md index c64a92c78944..626660e901d7 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/README.md +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/README.md @@ -84,6 +84,12 @@ const render = () => ( ); ``` +##### `controls` + +There are some helpers for plugin controls that can be imported from subdirectories of +`@superset-ui/chart`. If you're building a third-party plugin, modules that may be of use are +`@superset-ui/chart/controls/selectOptions` and `@superset-ui/chart/controls/D3Formatting`. + ##### `` Coming soon. diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/package.json b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/package.json index 5a07ae395f43..78d5c2d46a92 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/package.json +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/package.json @@ -45,6 +45,8 @@ "@superset-ui/core": "^0.12.0", "@superset-ui/dimension": "^0.12.0", "@superset-ui/query": "^0.12.0", + "@superset-ui/translation": "0.x", + "@superset-ui/validator": "0.x", "react": "^16.13.1" } } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/D3Formatting.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/D3Formatting.ts new file mode 100644 index 000000000000..fa377ab547dc --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/D3Formatting.ts @@ -0,0 +1,49 @@ +/** + * 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. + */ + +// D3 specific formatting config + +export const D3_FORMAT_DOCS = 'D3 format syntax: https://github.com/d3/d3-format'; + +// input choices & options +export const D3_FORMAT_OPTIONS = [ + ['SMART_NUMBER', 'Adaptative formating'], + ['~g', 'Original value'], + [',d', ',d (12345.432 => 12,345)'], + ['.1s', '.1s (12345.432 => 10k)'], + ['.3s', '.3s (12345.432 => 12.3k)'], + [',.1%', ',.1% (12345.432 => 1,234,543.2%)'], + ['.3%', '.3% (12345.432 => 1234543.200%)'], + ['.4r', '.4r (12345.432 => 12350)'], + [',.3f', ',.3f (12345.432 => 12,345.432)'], + ['+,', '+, (12345.432 => +12,345.432)'], + ['$,.2f', '$,.2f (12345.432 => $12,345.43)'], + ['DURATION', 'Duration in ms (66000 => 1m 6s)'], + ['DURATION_SUB', 'Duration in ms (100.40008 => 100ms 400µs 80ns)'], +]; + +export const D3_TIME_FORMAT_OPTIONS = [ + ['smart_date', 'Adaptative formating'], + ['%d/%m/%Y', '%d/%m/%Y | 14/01/2019'], + ['%m/%d/%Y', '%m/%d/%Y | 01/14/2019'], + ['%Y-%m-%d', '%Y-%m-%d | 2019-01-14'], + ['%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S | 2019-01-14 01:32:10'], + ['%d-%m-%Y %H:%M:%S', '%Y-%m-%d %H:%M:%S | 14-01-2019 01:32:10'], + ['%H:%M:%S', '%H:%M:%S | 01:32:10'], +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/index.ts new file mode 100644 index 000000000000..41390cc153a4 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/index.ts @@ -0,0 +1,5 @@ +import * as sectionsModule from './sections'; + +export const sections = sectionsModule; +export * from './D3Formatting'; +export * from './selectOptions'; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/sections.tsx b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/sections.tsx new file mode 100644 index 000000000000..2b296197093d --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/sections.tsx @@ -0,0 +1,109 @@ +/** + * 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/translation'; + +// A few standard controls sections that are used internally. +// Not recommended for use in third-party plugins. + +export const druidTimeSeries = { + label: t('Time'), + expanded: true, + description: t('Time related form attributes'), + controlSetRows: [['time_range']], +}; + +export const datasourceAndVizType = { + label: t('Datasource & Chart Type'), + expanded: true, + controlSetRows: [ + ['datasource'], + ['viz_type'], + [ + { + name: 'slice_id', + config: { + type: 'HiddenControl', + label: t('Chart ID'), + hidden: true, + description: t('The id of the active chart'), + }, + }, + { + name: 'cache_timeout', + config: { + type: 'HiddenControl', + label: t('Cache Timeout (seconds)'), + hidden: true, + description: t('The number of seconds before expiring the cache'), + }, + }, + { + name: 'url_params', + config: { + type: 'HiddenControl', + label: t('URL Parameters'), + hidden: true, + description: t('Extra parameters for use in jinja templated queries'), + }, + }, + { + name: 'time_range_endpoints', + config: { + type: 'HiddenControl', + label: t('Time range endpoints'), + hidden: true, + description: t('Time range endpoints (SIP-15)'), + }, + }, + ], + ], +}; + +export const colorScheme = { + label: t('Color Scheme'), + controlSetRows: [['color_scheme', 'label_colors']], +}; + +export const sqlaTimeSeries = { + label: t('Time'), + description: t('Time related form attributes'), + expanded: true, + controlSetRows: [['granularity_sqla'], ['time_range']], +}; + +export const annotations = { + label: t('Annotations and Layers'), + tabOverride: 'data', + expanded: true, + controlSetRows: [ + [ + { + name: 'annotation_layers', + config: { + type: 'AnnotationLayerControl', + label: '', + default: [], + description: 'Annotation Layers', + renderTrigger: true, + tabOverride: 'data', + }, + }, + ], + ], +}; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/selectOptions.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/selectOptions.ts new file mode 100644 index 000000000000..a65e95cf1520 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/controls/selectOptions.ts @@ -0,0 +1,42 @@ +/** + * 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. + */ + +// helper functions for select controls + +export type Formattable = string | number; + +export type Formatted = [Formattable, string]; + +/** Turns an array of string/number options into options for a select input */ +export function formatSelectOptions(options: Formattable[]): Formatted[] { + return options.map(opt => [opt, opt.toString()]); +} + +/** + * outputs array of arrays + * formatSelectOptionsForRange(1, 5) + * returns [[1,'1'], [2,'2'], [3,'3'], [4,'4'], [5,'5']] + */ +export function formatSelectOptionsForRange(start: number, end: number) { + const options: Formatted[] = []; + for (let i = start; i <= end; i++) { + options.push([i, i.toString()]); + } + return options; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/index.ts index a19e7bbfc00c..71d139dad8b8 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/index.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/src/index.ts @@ -15,5 +15,7 @@ export { default as getChartTransformPropsRegistry } from './registries/ChartTra export { default as ChartDataProvider } from './components/ChartDataProvider'; +export * from './controls/index'; + export * from './types/TransformFunction'; export * from './types/QueryResponse'; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/test/controls/selectOptions.test.ts b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/test/controls/selectOptions.test.ts new file mode 100644 index 000000000000..b3fec3078cea --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/packages/superset-ui-chart/test/controls/selectOptions.test.ts @@ -0,0 +1,26 @@ +import { formatSelectOptions, formatSelectOptionsForRange } from '../../src'; + +describe('formatSelectOptions', () => { + it('formats an array of options', () => { + expect(formatSelectOptions([1, 5, 10, 25, 50, 'unlimited'])).toEqual([ + [1, '1'], + [5, '5'], + [10, '10'], + [25, '25'], + [50, '50'], + ['unlimited', 'unlimited'], + ]); + }); +}); + +describe('formatSelectOptionsForRange', () => { + it('generates select options from a range', () => { + expect(formatSelectOptionsForRange(1, 5)).toEqual([ + [1, '1'], + [2, '2'], + [3, '3'], + [4, '4'], + [5, '5'], + ]); + }); +}); diff --git a/superset-frontend/temporary_superset_ui/superset-ui/temporary-plugins/superset-ui-legacy-preset-chart-nvd3/src/NVD3Controls.tsx b/superset-frontend/temporary_superset_ui/superset-ui/temporary-plugins/superset-ui-legacy-preset-chart-nvd3/src/NVD3Controls.tsx new file mode 100644 index 000000000000..72400996705c --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/temporary-plugins/superset-ui-legacy-preset-chart-nvd3/src/NVD3Controls.tsx @@ -0,0 +1,457 @@ +/** + * 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 { t } from '@superset-ui/translation'; +import { controls } from '@superset-ui/chart'; + +const { formatSelectOptions, D3_TIME_FORMAT_OPTIONS, D3_FORMAT_DOCS, D3_FORMAT_OPTIONS } = controls; + +/* + Plugins in question: + + AreaChartPlugin, + BarChartPlugin, + BubbleChartPlugin, + BulletChartPlugin, + CompareChartPlugin, + DistBarChartPlugin, + DualLineChartPlugin, + LineChartPlugin, + LineMultiChartPlugin, + PieChartPlugin, + TimePivotChartPlugin, +*/ + +export const yAxis2Format = { + name: 'y_axis_2_format', + config: { + type: 'SelectControl', + freeForm: true, + label: t('Right Axis Format'), + default: 'SMART_NUMBER', + choices: D3_FORMAT_OPTIONS, + description: D3_FORMAT_DOCS, + }, +}; + +export const showMarkers = { + name: 'show_markers', + config: { + type: 'CheckboxControl', + label: t('Show Markers'), + renderTrigger: true, + default: false, + description: t('Show data points as circle markers on the lines'), + }, +}; + +export const leftMargin = { + name: 'left_margin', + config: { + type: 'SelectControl', + freeForm: true, + clearable: false, + label: t('Left Margin'), + choices: formatSelectOptions(['auto', 50, 75, 100, 125, 150, 200]), + default: 'auto', + renderTrigger: true, + description: t('Left margin, in pixels, allowing for more room for axis labels'), + }, +}; + +export const yAxisShowMinmax = { + name: 'y_axis_showminmax', + config: { + type: 'CheckboxControl', + label: t('Y bounds'), + renderTrigger: true, + default: false, + description: t('Whether to display the min and max values of the Y-axis'), + }, +}; + +export const lineInterpolation = { + name: 'line_interpolation', + config: { + type: 'SelectControl', + label: t('Line Style'), + renderTrigger: true, + choices: formatSelectOptions([ + 'linear', + 'basis', + 'cardinal', + 'monotone', + 'step-before', + 'step-after', + ]), + default: 'linear', + description: t('Line interpolation as defined by d3.js'), + }, +}; + +export const showBrush = { + name: 'show_brush', + config: { + type: 'SelectControl', + label: t('Show Range Filter'), + renderTrigger: true, + clearable: false, + default: 'auto', + choices: [ + ['yes', 'Yes'], + ['no', 'No'], + ['auto', 'Auto'], + ], + description: t('Whether to display the time range interactive selector'), + }, +}; + +export const showLegend = { + name: 'show_legend', + config: { + type: 'CheckboxControl', + label: t('Legend'), + renderTrigger: true, + default: true, + description: t('Whether to display the legend (toggles)'), + }, +}; + +export const showControls = { + name: 'show_controls', + config: { + type: 'CheckboxControl', + label: t('Extra Controls'), + renderTrigger: true, + default: false, + description: t( + 'Whether to show extra controls or not. Extra controls ' + + 'include things like making mulitBar charts stacked ' + + 'or side by side.', + ), + }, +}; + +export const xAxisLabel = { + name: 'x_axis_label', + config: { + type: 'TextControl', + label: t('X Axis Label'), + renderTrigger: true, + default: '', + }, +}; + +export const bottomMargin = { + name: 'bottom_margin', + config: { + type: 'SelectControl', + clearable: false, + freeForm: true, + label: t('Bottom Margin'), + choices: formatSelectOptions(['auto', 50, 75, 100, 125, 150, 200]), + default: 'auto', + renderTrigger: true, + description: t('Bottom margin, in pixels, allowing for more room for axis labels'), + }, +}; + +export const xTicksLayout = { + name: 'x_ticks_layout', + config: { + type: 'SelectControl', + label: t('X Tick Layout'), + choices: formatSelectOptions(['auto', 'flat', '45°', 'staggered']), + default: 'auto', + clearable: false, + renderTrigger: true, + description: t('The way the ticks are laid out on the X-axis'), + }, +}; + +export const xAxisFormat = { + name: 'x_axis_format', + config: { + type: 'SelectControl', + freeForm: true, + label: t('X Axis Format'), + renderTrigger: true, + choices: D3_TIME_FORMAT_OPTIONS, + default: 'smart_date', + description: D3_FORMAT_DOCS, + }, +}; + +export const yLogScale = { + name: 'y_log_scale', + config: { + type: 'CheckboxControl', + label: t('Y Log Scale'), + default: false, + renderTrigger: true, + description: t('Use a log scale for the Y-axis'), + }, +}; + +export const yAxisBounds = { + name: 'y_axis_bounds', + config: { + type: 'BoundsControl', + label: t('Y Axis Bounds'), + renderTrigger: true, + default: [null, null], + description: t( + 'Bounds for the Y-axis. When left empty, the bounds are ' + + 'dynamically defined based on the min/max of the data. Note that ' + + "this feature will only expand the axis range. It won't " + + "narrow the data's extent.", + ), + }, +}; + +export const xAxisShowMinmax = { + name: 'x_axis_showminmax', + config: { + type: 'CheckboxControl', + label: t('X bounds'), + renderTrigger: true, + default: false, + description: t('Whether to display the min and max values of the X-axis'), + }, +}; + +export const richTooltip = { + name: 'rich_tooltip', + config: { + type: 'CheckboxControl', + label: t('Rich Tooltip'), + renderTrigger: true, + default: true, + description: t('The rich tooltip shows a list of all series for that point in time'), + }, +}; + +export const showBarValue = { + name: 'show_bar_value', + config: { + type: 'CheckboxControl', + label: t('Bar Values'), + default: false, + renderTrigger: true, + description: t('Show the value on top of the bar'), + }, +}; + +export const barStacked = { + name: 'bar_stacked', + config: { + type: 'CheckboxControl', + label: t('Stacked Bars'), + renderTrigger: true, + default: false, + description: null, + }, +}; + +export const reduceXTicks = { + name: 'reduce_x_ticks', + config: { + type: 'CheckboxControl', + label: t('Reduce X ticks'), + renderTrigger: true, + default: false, + description: t( + 'Reduces the number of X-axis ticks to be rendered. ' + + 'If true, the x-axis will not overflow and labels may be ' + + 'missing. If false, a minimum width will be applied ' + + 'to columns and the width may overflow into an ' + + 'horizontal scroll.', + ), + }, +}; + +export const yAxisLabel = { + name: 'y_axis_label', + config: { + type: 'TextControl', + label: t('Y Axis Label'), + renderTrigger: true, + default: '', + }, +}; + +export const timeSeriesSection = [ + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['metrics'], + ['adhoc_filters'], + ['groupby'], + ['limit', 'timeseries_limit_metric'], + [ + { + name: 'order_desc', + config: { + type: 'CheckboxControl', + label: t('Sort Descending'), + default: true, + description: t('Whether to sort descending or ascending'), + }, + }, + { + name: 'contribution', + config: { + type: 'CheckboxControl', + label: t('Contribution'), + default: false, + description: t('Compute the contribution to the total'), + }, + }, + ], + ['row_limit', null], + ], + }, + { + label: t('Advanced Analytics'), + tabOverride: 'data', + description: t( + 'This section contains options ' + + 'that allow for advanced analytical post processing ' + + 'of query results', + ), + controlSetRows: [ + [

{t('Rolling Window')}

], + [ + { + name: 'rolling_type', + config: { + type: 'SelectControl', + label: t('Rolling Function'), + default: 'None', + choices: formatSelectOptions(['None', 'mean', 'sum', 'std', 'cumsum']), + description: t( + 'Defines a rolling window function to apply, works along ' + + 'with the [Periods] text box', + ), + }, + }, + { + name: 'rolling_periods', + config: { + type: 'TextControl', + label: t('Periods'), + isInt: true, + description: t( + 'Defines the size of the rolling window function, ' + + 'relative to the time granularity selected', + ), + }, + }, + { + name: 'min_periods', + config: { + type: 'TextControl', + label: t('Min Periods'), + isInt: true, + description: t( + 'The minimum number of rolling periods required to show ' + + 'a value. For instance if you do a cumulative sum on 7 days ' + + 'you may want your "Min Period" to be 7, so that all data points ' + + 'shown are the total of 7 periods. This will hide the "ramp up" ' + + 'taking place over the first 7 periods', + ), + }, + }, + ], + [

{t('Time Comparison')}

], + [ + { + name: 'time_compare', + config: { + type: 'SelectControl', + multi: true, + freeForm: true, + label: t('Time Shift'), + choices: formatSelectOptions([ + '1 day', + '1 week', + '28 days', + '30 days', + '52 weeks', + '1 year', + ]), + description: t( + 'Overlay one or more timeseries from a ' + + 'relative time period. Expects relative time deltas ' + + 'in natural language (example: 24 hours, 7 days, ' + + '56 weeks, 365 days)', + ), + }, + }, + { + name: 'comparison_type', + config: { + type: 'SelectControl', + label: t('Calculation type'), + default: 'values', + choices: [ + ['values', 'Actual Values'], + ['absolute', 'Absolute difference'], + ['percentage', 'Percentage change'], + ['ratio', 'Ratio'], + ], + description: t( + 'How to display time shifts: as individual lines; as the ' + + 'absolute difference between the main time series and each time shift; ' + + 'as the percentage change; or as the ratio between series and time shifts.', + ), + }, + }, + ], + [

{t('Python Functions')}

], + [

pandas.resample

], + [ + { + name: 'resample_rule', + config: { + type: 'SelectControl', + freeForm: true, + label: t('Rule'), + default: null, + choices: formatSelectOptions(['1T', '1H', '1D', '7D', '1M', '1AS']), + description: t('Pandas resample rule'), + }, + }, + { + name: 'resample_method', + config: { + type: 'SelectControl', + freeForm: true, + label: t('Method'), + default: null, + choices: formatSelectOptions(['asfreq', 'bfill', 'ffill', 'median', 'mean', 'sum']), + description: t('Pandas resample method'), + }, + }, + ], + ], + }, +];