mirror of
https://github.com/apache/superset.git
synced 2026-04-20 00:24:38 +00:00
feat(viz-type): Ag grid table plugin Integration (#33517)
Signed-off-by: hainenber <dotronghai96@gmail.com> Co-authored-by: Amaan Nawab <nelsondrew07@gmail.com> Co-authored-by: Levis Mbote <111055098+LevisNgigi@users.noreply.github.com> Co-authored-by: Enzo Martellucci <52219496+EnxDev@users.noreply.github.com> Co-authored-by: Paul Rhodes <withnale@users.noreply.github.com> Co-authored-by: Vitor Avila <96086495+Vitor-Avila@users.noreply.github.com> Co-authored-by: Đỗ Trọng Hải <41283691+hainenber@users.noreply.github.com> Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com> Co-authored-by: Sam Firke <sfirke@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,753 @@
|
||||
/* eslint-disable camelcase */
|
||||
/**
|
||||
* 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 {
|
||||
ColumnMeta,
|
||||
ColumnOption,
|
||||
ControlConfig,
|
||||
ControlPanelConfig,
|
||||
ControlPanelsContainerProps,
|
||||
ControlPanelState,
|
||||
ControlState,
|
||||
ControlStateMapping,
|
||||
D3_TIME_FORMAT_OPTIONS,
|
||||
Dataset,
|
||||
DEFAULT_MAX_ROW,
|
||||
DEFAULT_MAX_ROW_TABLE_SERVER,
|
||||
defineSavedMetrics,
|
||||
formatSelectOptions,
|
||||
getStandardizedControls,
|
||||
QueryModeLabel,
|
||||
sections,
|
||||
sharedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import {
|
||||
ensureIsArray,
|
||||
FeatureFlag,
|
||||
GenericDataType,
|
||||
getMetricLabel,
|
||||
isAdhocColumn,
|
||||
isFeatureEnabled,
|
||||
isPhysicalColumn,
|
||||
legacyValidateInteger,
|
||||
QueryFormColumn,
|
||||
QueryFormMetric,
|
||||
QueryMode,
|
||||
SMART_DATE_ID,
|
||||
t,
|
||||
validateMaxValue,
|
||||
validateServerPagination,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
import { isEmpty, last } from 'lodash';
|
||||
import { PAGE_SIZE_OPTIONS, SERVER_PAGE_SIZE_OPTIONS } from './consts';
|
||||
import { ColorSchemeEnum } from './types';
|
||||
|
||||
/**
|
||||
* Generate comparison column names for a given column.
|
||||
*/
|
||||
const generateComparisonColumns = (colname: string) => [
|
||||
`${t('Main')} ${colname}`,
|
||||
`# ${colname}`,
|
||||
`△ ${colname}`,
|
||||
`% ${colname}`,
|
||||
];
|
||||
|
||||
/**
|
||||
* Generate column types for the comparison columns.
|
||||
*/
|
||||
const generateComparisonColumnTypes = (count: number) =>
|
||||
Array(count).fill(GenericDataType.Numeric);
|
||||
|
||||
function getQueryMode(controls: ControlStateMapping): QueryMode {
|
||||
const mode = controls?.query_mode?.value;
|
||||
if (mode === QueryMode.Aggregate || mode === QueryMode.Raw) {
|
||||
return mode as QueryMode;
|
||||
}
|
||||
const rawColumns = controls?.all_columns?.value as
|
||||
| QueryFormColumn[]
|
||||
| undefined;
|
||||
const hasRawColumns = rawColumns && rawColumns.length > 0;
|
||||
return hasRawColumns ? QueryMode.Raw : QueryMode.Aggregate;
|
||||
}
|
||||
|
||||
const processComparisonColumns = (columns: any[], suffix: string) =>
|
||||
columns
|
||||
.map(col => {
|
||||
if (!col.label.includes(suffix)) {
|
||||
return [
|
||||
{
|
||||
label: `${t('Main')} ${col.label}`,
|
||||
value: `${t('Main')} ${col.value}`,
|
||||
},
|
||||
{
|
||||
label: `# ${col.label}`,
|
||||
value: `# ${col.value}`,
|
||||
},
|
||||
{
|
||||
label: `△ ${col.label}`,
|
||||
value: `△ ${col.value}`,
|
||||
},
|
||||
{
|
||||
label: `% ${col.label}`,
|
||||
value: `% ${col.value}`,
|
||||
},
|
||||
];
|
||||
}
|
||||
return [];
|
||||
})
|
||||
.flat();
|
||||
|
||||
/**
|
||||
* Visibility check
|
||||
*/
|
||||
function isQueryMode(mode: QueryMode) {
|
||||
return ({ controls }: Pick<ControlPanelsContainerProps, 'controls'>) =>
|
||||
getQueryMode(controls) === mode;
|
||||
}
|
||||
|
||||
const isAggMode = isQueryMode(QueryMode.Aggregate);
|
||||
const isRawMode = isQueryMode(QueryMode.Raw);
|
||||
|
||||
const validateAggControlValues = (
|
||||
controls: ControlStateMapping,
|
||||
values: any[],
|
||||
) => {
|
||||
const areControlsEmpty = values.every(val => ensureIsArray(val).length === 0);
|
||||
return areControlsEmpty && isAggMode({ controls })
|
||||
? [t('Group By, Metrics or Percentage Metrics must have a value')]
|
||||
: [];
|
||||
};
|
||||
|
||||
const queryMode: ControlConfig<'RadioButtonControl'> = {
|
||||
type: 'RadioButtonControl',
|
||||
label: t('Query mode'),
|
||||
default: null,
|
||||
options: [
|
||||
[QueryMode.Aggregate, QueryModeLabel[QueryMode.Aggregate]],
|
||||
[QueryMode.Raw, QueryModeLabel[QueryMode.Raw]],
|
||||
],
|
||||
mapStateToProps: ({ controls }) => ({ value: getQueryMode(controls) }),
|
||||
rerender: ['all_columns', 'groupby', 'metrics', 'percent_metrics'],
|
||||
};
|
||||
|
||||
const allColumnsControl: typeof sharedControls.groupby = {
|
||||
...sharedControls.groupby,
|
||||
label: t('Columns'),
|
||||
description: t('Columns to display'),
|
||||
multi: true,
|
||||
freeForm: true,
|
||||
allowAll: true,
|
||||
commaChoosesOption: false,
|
||||
optionRenderer: c => <ColumnOption showType column={c} />,
|
||||
valueRenderer: c => <ColumnOption column={c} />,
|
||||
valueKey: 'column_name',
|
||||
mapStateToProps: ({ datasource, controls }, controlState) => ({
|
||||
options: datasource?.columns || [],
|
||||
queryMode: getQueryMode(controls),
|
||||
externalValidationErrors:
|
||||
isRawMode({ controls }) && ensureIsArray(controlState?.value).length === 0
|
||||
? [t('must have a value')]
|
||||
: [],
|
||||
}),
|
||||
visibility: isRawMode,
|
||||
resetOnHide: false,
|
||||
};
|
||||
|
||||
const percentMetricsControl: typeof sharedControls.metrics = {
|
||||
...sharedControls.metrics,
|
||||
label: t('Percentage metrics'),
|
||||
description: t(
|
||||
'Select one or many metrics to display, that will be displayed in the percentages of total. ' +
|
||||
'Percentage metrics will be calculated only from data within the row limit. ' +
|
||||
'You can use an aggregation function on a column or write custom SQL to create a percentage metric.',
|
||||
),
|
||||
visibility: isAggMode,
|
||||
resetOnHide: false,
|
||||
mapStateToProps: ({ datasource, controls }, controlState) => ({
|
||||
columns: datasource?.columns || [],
|
||||
savedMetrics: defineSavedMetrics(datasource),
|
||||
datasource,
|
||||
datasourceType: datasource?.type,
|
||||
queryMode: getQueryMode(controls),
|
||||
externalValidationErrors: validateAggControlValues(controls, [
|
||||
controls.groupby?.value,
|
||||
controls.metrics?.value,
|
||||
controlState?.value,
|
||||
]),
|
||||
}),
|
||||
rerender: ['groupby', 'metrics'],
|
||||
default: [],
|
||||
validators: [],
|
||||
};
|
||||
|
||||
/*
|
||||
Options for row limit control
|
||||
*/
|
||||
|
||||
export const ROW_LIMIT_OPTIONS_TABLE = [
|
||||
10, 50, 100, 250, 500, 1000, 5000, 10000, 50000, 100000, 150000, 200000,
|
||||
250000, 300000, 350000, 400000, 450000, 500000,
|
||||
];
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'query_mode',
|
||||
config: queryMode,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'groupby',
|
||||
override: {
|
||||
visibility: isAggMode,
|
||||
resetOnHide: false,
|
||||
mapStateToProps: (
|
||||
state: ControlPanelState,
|
||||
controlState: ControlState,
|
||||
) => {
|
||||
const { controls } = state;
|
||||
const originalMapStateToProps =
|
||||
sharedControls?.groupby?.mapStateToProps;
|
||||
const newState =
|
||||
originalMapStateToProps?.(state, controlState) ?? {};
|
||||
newState.externalValidationErrors = validateAggControlValues(
|
||||
controls,
|
||||
[controls.metrics?.value, controlState.value],
|
||||
);
|
||||
|
||||
return newState;
|
||||
},
|
||||
rerender: ['metrics', 'percent_metrics'],
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'time_grain_sqla',
|
||||
config: {
|
||||
...sharedControls.time_grain_sqla,
|
||||
visibility: ({ controls }) => {
|
||||
const dttmLookup = Object.fromEntries(
|
||||
ensureIsArray(controls?.groupby?.options).map(option => [
|
||||
option.column_name,
|
||||
option.is_dttm,
|
||||
]),
|
||||
);
|
||||
|
||||
return ensureIsArray(controls?.groupby.value)
|
||||
.map(selection => {
|
||||
if (isAdhocColumn(selection)) {
|
||||
return true;
|
||||
}
|
||||
if (isPhysicalColumn(selection)) {
|
||||
return !!dttmLookup[selection];
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.some(Boolean);
|
||||
},
|
||||
},
|
||||
},
|
||||
'temporal_columns_lookup',
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'metrics',
|
||||
override: {
|
||||
validators: [],
|
||||
visibility: isAggMode,
|
||||
resetOnHide: false,
|
||||
mapStateToProps: (
|
||||
{ controls, datasource, form_data }: ControlPanelState,
|
||||
controlState: ControlState,
|
||||
) => ({
|
||||
columns: datasource?.columns[0]?.hasOwnProperty('filterable')
|
||||
? (datasource as Dataset)?.columns?.filter(
|
||||
(c: ColumnMeta) => c.filterable,
|
||||
)
|
||||
: datasource?.columns,
|
||||
savedMetrics: defineSavedMetrics(datasource),
|
||||
// current active adhoc metrics
|
||||
selectedMetrics:
|
||||
form_data.metrics ||
|
||||
(form_data.metric ? [form_data.metric] : []),
|
||||
datasource,
|
||||
externalValidationErrors: validateAggControlValues(controls, [
|
||||
controls.groupby?.value,
|
||||
controlState.value,
|
||||
]),
|
||||
}),
|
||||
rerender: ['groupby'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'all_columns',
|
||||
config: allColumnsControl,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'percent_metrics',
|
||||
config: percentMetricsControl,
|
||||
},
|
||||
],
|
||||
['adhoc_filters'],
|
||||
[
|
||||
{
|
||||
name: 'timeseries_limit_metric',
|
||||
override: {
|
||||
visibility: isAggMode,
|
||||
resetOnHide: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'order_by_cols',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Ordering'),
|
||||
description: t('Order results by selected columns'),
|
||||
multi: true,
|
||||
default: [],
|
||||
mapStateToProps: ({ datasource }) => ({
|
||||
choices: datasource?.hasOwnProperty('order_by_choices')
|
||||
? (datasource as Dataset)?.order_by_choices
|
||||
: datasource?.columns || [],
|
||||
}),
|
||||
visibility: isRawMode,
|
||||
resetOnHide: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'order_desc',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Sort descending'),
|
||||
default: true,
|
||||
description: t(
|
||||
'If enabled, this control sorts the results/values descending, otherwise it sorts the results ascending.',
|
||||
),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) => {
|
||||
const hasSortMetric = Boolean(
|
||||
controls?.timeseries_limit_metric?.value,
|
||||
);
|
||||
return hasSortMetric && isAggMode({ controls });
|
||||
},
|
||||
resetOnHide: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'server_pagination',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Server pagination'),
|
||||
description: t(
|
||||
'Enable server side pagination of results (experimental feature)',
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'server_page_length',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Server Page Length'),
|
||||
default: 10,
|
||||
choices: SERVER_PAGE_SIZE_OPTIONS,
|
||||
description: t('Rows per page, 0 means no pagination'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
Boolean(controls?.server_pagination?.value),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'row_limit',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Row limit'),
|
||||
clearable: false,
|
||||
mapStateToProps: state => ({
|
||||
maxValue: state?.common?.conf?.TABLE_VIZ_MAX_ROW_SERVER,
|
||||
server_pagination: state?.form_data?.server_pagination,
|
||||
maxValueWithoutServerPagination:
|
||||
state?.common?.conf?.SQL_MAX_ROW,
|
||||
}),
|
||||
validators: [
|
||||
legacyValidateInteger,
|
||||
(v, state) =>
|
||||
validateMaxValue(
|
||||
v,
|
||||
state?.maxValue || DEFAULT_MAX_ROW_TABLE_SERVER,
|
||||
),
|
||||
(v, state) =>
|
||||
validateServerPagination(
|
||||
v,
|
||||
state?.server_pagination,
|
||||
state?.maxValueWithoutServerPagination || DEFAULT_MAX_ROW,
|
||||
state?.maxValue || DEFAULT_MAX_ROW_TABLE_SERVER,
|
||||
),
|
||||
],
|
||||
// Re run the validations when this control value
|
||||
validationDependancies: ['server_pagination'],
|
||||
default: 10000,
|
||||
choices: formatSelectOptions(ROW_LIMIT_OPTIONS_TABLE),
|
||||
description: t(
|
||||
'Limits the number of the rows that are computed in the query that is the source of the data used for this chart.',
|
||||
),
|
||||
},
|
||||
override: {
|
||||
default: 1000,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_totals',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show summary'),
|
||||
default: false,
|
||||
description: t(
|
||||
'Show total aggregations of selected metrics. Note that row limit does not apply to the result.',
|
||||
),
|
||||
visibility: isAggMode,
|
||||
resetOnHide: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Options'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'table_timestamp_format',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Timestamp format'),
|
||||
default: SMART_DATE_ID,
|
||||
renderTrigger: true,
|
||||
clearable: false,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: t('D3 time format for datetime columns'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'page_length',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
renderTrigger: true,
|
||||
label: t('Page length'),
|
||||
default: null,
|
||||
choices: PAGE_SIZE_OPTIONS,
|
||||
description: t('Rows per page, 0 means no pagination'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
!controls?.server_pagination?.value,
|
||||
},
|
||||
},
|
||||
null,
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'include_search',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Search box'),
|
||||
renderTrigger: true,
|
||||
default: false,
|
||||
description: t('Whether to include a client-side search box'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'column_config',
|
||||
config: {
|
||||
type: 'ColumnConfigControl',
|
||||
label: t('Customize columns'),
|
||||
description: t('Further customize how to display each column'),
|
||||
width: 400,
|
||||
height: 320,
|
||||
renderTrigger: true,
|
||||
shouldMapStateToProps() {
|
||||
return true;
|
||||
},
|
||||
mapStateToProps(explore, _, chart) {
|
||||
const timeComparisonValue =
|
||||
explore?.controls?.time_compare?.value;
|
||||
const { colnames: _colnames, coltypes: _coltypes } =
|
||||
chart?.queriesResponse?.[0] ?? {};
|
||||
let colnames: string[] = _colnames || [];
|
||||
let coltypes: GenericDataType[] = _coltypes || [];
|
||||
const childColumnMap: Record<string, boolean> = {};
|
||||
const timeComparisonColumnMap: Record<string, boolean> = {};
|
||||
|
||||
if (!isEmpty(timeComparisonValue)) {
|
||||
/**
|
||||
* Replace numeric columns with sets of comparison columns.
|
||||
*/
|
||||
const updatedColnames: string[] = [];
|
||||
const updatedColtypes: GenericDataType[] = [];
|
||||
|
||||
colnames
|
||||
.filter(
|
||||
colname =>
|
||||
last(colname.split('__')) !== timeComparisonValue,
|
||||
)
|
||||
.forEach((colname, index) => {
|
||||
if (
|
||||
explore.form_data.metrics?.some(
|
||||
metric => getMetricLabel(metric) === colname,
|
||||
) ||
|
||||
explore.form_data.percent_metrics?.some(
|
||||
(metric: QueryFormMetric) =>
|
||||
getMetricLabel(metric) === colname,
|
||||
)
|
||||
) {
|
||||
const comparisonColumns =
|
||||
generateComparisonColumns(colname);
|
||||
comparisonColumns.forEach((name, idx) => {
|
||||
updatedColnames.push(name);
|
||||
updatedColtypes.push(
|
||||
...generateComparisonColumnTypes(4),
|
||||
);
|
||||
timeComparisonColumnMap[name] = true;
|
||||
if (idx === 0 && name.startsWith('Main ')) {
|
||||
childColumnMap[name] = false;
|
||||
} else {
|
||||
childColumnMap[name] = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updatedColnames.push(colname);
|
||||
updatedColtypes.push(coltypes[index]);
|
||||
childColumnMap[colname] = false;
|
||||
timeComparisonColumnMap[colname] = false;
|
||||
}
|
||||
});
|
||||
|
||||
colnames = updatedColnames;
|
||||
coltypes = updatedColtypes;
|
||||
}
|
||||
return {
|
||||
columnsPropsObject: {
|
||||
colnames,
|
||||
coltypes,
|
||||
childColumnMap,
|
||||
timeComparisonColumnMap,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Visual formatting'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'show_cell_bars',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show Cell bars'),
|
||||
renderTrigger: true,
|
||||
default: true,
|
||||
description: t(
|
||||
'Whether to display a bar chart background in table columns',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'align_pn',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Align +/-'),
|
||||
renderTrigger: true,
|
||||
default: false,
|
||||
description: t(
|
||||
'Whether to align background charts with both positive and negative values at 0',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'color_pn',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('add colors to cell bars for +/-'),
|
||||
renderTrigger: true,
|
||||
default: true,
|
||||
description: t(
|
||||
'Whether to colorize numeric values by whether they are positive or negative',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'comparison_color_enabled',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('basic conditional formatting'),
|
||||
renderTrigger: true,
|
||||
visibility: ({ controls }) =>
|
||||
!isEmpty(controls?.time_compare?.value),
|
||||
default: false,
|
||||
description: t(
|
||||
'This will be applied to the whole table. Arrows (↑ and ↓) will be added to ' +
|
||||
'main columns for increase and decrease. Basic conditional formatting can be ' +
|
||||
'overwritten by conditional formatting below.',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'comparison_color_scheme',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('color type'),
|
||||
default: ColorSchemeEnum.Green,
|
||||
renderTrigger: true,
|
||||
choices: [
|
||||
[ColorSchemeEnum.Green, 'Green for increase, red for decrease'],
|
||||
[ColorSchemeEnum.Red, 'Red for increase, green for decrease'],
|
||||
],
|
||||
visibility: ({ controls }) =>
|
||||
!isEmpty(controls?.time_compare?.value) &&
|
||||
Boolean(controls?.comparison_color_enabled?.value),
|
||||
description: t(
|
||||
'Adds color to the chart symbols based on the positive or ' +
|
||||
'negative change from the comparison value.',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'conditional_formatting',
|
||||
config: {
|
||||
type: 'ConditionalFormattingControl',
|
||||
renderTrigger: true,
|
||||
label: t('Custom Conditional Formatting'),
|
||||
extraColorChoices: [
|
||||
{
|
||||
value: ColorSchemeEnum.Green,
|
||||
label: t('Green for increase, red for decrease'),
|
||||
},
|
||||
{
|
||||
value: ColorSchemeEnum.Red,
|
||||
label: t('Red for increase, green for decrease'),
|
||||
},
|
||||
],
|
||||
description: t(
|
||||
'Apply conditional color formatting to numeric columns',
|
||||
),
|
||||
shouldMapStateToProps() {
|
||||
return true;
|
||||
},
|
||||
mapStateToProps(explore, _, chart) {
|
||||
const verboseMap = explore?.datasource?.hasOwnProperty(
|
||||
'verbose_map',
|
||||
)
|
||||
? (explore?.datasource as Dataset)?.verbose_map
|
||||
: (explore?.datasource?.columns ?? {});
|
||||
const chartStatus = chart?.chartStatus;
|
||||
const { colnames, coltypes } =
|
||||
chart?.queriesResponse?.[0] ?? {};
|
||||
const numericColumns =
|
||||
Array.isArray(colnames) && Array.isArray(coltypes)
|
||||
? colnames
|
||||
.filter(
|
||||
(colname: string, index: number) =>
|
||||
coltypes[index] === GenericDataType.Numeric,
|
||||
)
|
||||
.map((colname: string) => ({
|
||||
value: colname,
|
||||
label: Array.isArray(verboseMap)
|
||||
? colname
|
||||
: (verboseMap[colname] ?? colname),
|
||||
}))
|
||||
: [];
|
||||
const columnOptions = explore?.controls?.time_compare?.value
|
||||
? processComparisonColumns(
|
||||
numericColumns || [],
|
||||
ensureIsArray(
|
||||
explore?.controls?.time_compare?.value,
|
||||
)[0]?.toString() || '',
|
||||
)
|
||||
: numericColumns;
|
||||
|
||||
return {
|
||||
removeIrrelevantConditions: chartStatus === 'success',
|
||||
columnOptions,
|
||||
verboseMap,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
...sections.timeComparisonControls({
|
||||
multi: false,
|
||||
showCalculationType: false,
|
||||
showFullChoices: false,
|
||||
}),
|
||||
visibility: ({ controls }) =>
|
||||
isAggMode({ controls }) &&
|
||||
isFeatureEnabled(FeatureFlag.TableV2TimeComparisonEnabled),
|
||||
},
|
||||
],
|
||||
formDataOverrides: formData => ({
|
||||
...formData,
|
||||
metrics: getStandardizedControls().popAllMetrics(),
|
||||
groupby: getStandardizedControls().popAllColumns(),
|
||||
}),
|
||||
};
|
||||
|
||||
export default config;
|
||||
Reference in New Issue
Block a user