mirror of
https://github.com/apache/superset.git
synced 2026-04-18 15:44:57 +00:00
feat: Adds plugin-chart-handlebars (#17903)
* adds: plugin chart handlebars * adds: handlebars plugin to main presets * update: npm install * chore: lint * adds: dateFormat handlebars helper * deletes: unused props * chore: linting plugin-chart-handlebars * docs: chart-plugin-handlebars * adds: moment to peer deps * update: use error handling * update: inline config, adds renderTrigger * update: inline config, adds renderTrigger * camelCase controls * (plugins-chart-handlebars) adds: missing props Adds missing propeties in test formData * (plugin-chart-handlebars) fixes test * (plugin-handlebars-chart) use numbers for size * (feature-handlebars-chart) fix viz_type * (plugin-handlebars-chart) revert revert the viz_type change. it was in the wrong place. * fix test and add license headers Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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, QueryFormData } from '@superset-ui/core';
|
||||
|
||||
export default function buildQuery(formData: QueryFormData) {
|
||||
const { metric, sort_by_metric, groupby } = formData;
|
||||
|
||||
return buildQueryContext(formData, baseQueryObject => [
|
||||
{
|
||||
...baseQueryObject,
|
||||
...(sort_by_metric && { orderby: [[metric, false]] }),
|
||||
...(groupby && { groupby }),
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* 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 {
|
||||
ControlPanelConfig,
|
||||
emitFilterControl,
|
||||
sections,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { addLocaleData, t } from '@superset-ui/core';
|
||||
import i18n from '../i18n';
|
||||
import { allColumnsControlSetItem } from './controls/columns';
|
||||
import { groupByControlSetItem } from './controls/groupBy';
|
||||
import { handlebarsTemplateControlSetItem } from './controls/handlebarTemplate';
|
||||
import { includeTimeControlSetItem } from './controls/includeTime';
|
||||
import {
|
||||
rowLimitControlSetItem,
|
||||
timeSeriesLimitMetricControlSetItem,
|
||||
} from './controls/limits';
|
||||
import {
|
||||
metricsControlSetItem,
|
||||
percentMetricsControlSetItem,
|
||||
showTotalsControlSetItem,
|
||||
} from './controls/metrics';
|
||||
import {
|
||||
orderByControlSetItem,
|
||||
orderDescendingControlSetItem,
|
||||
} from './controls/orderBy';
|
||||
import {
|
||||
serverPageLengthControlSetItem,
|
||||
serverPaginationControlSetRow,
|
||||
} from './controls/pagination';
|
||||
import { queryModeControlSetItem } from './controls/queryMode';
|
||||
import { styleControlSetItem } from './controls/style';
|
||||
|
||||
addLocaleData(i18n);
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
/**
|
||||
* The control panel is split into two tabs: "Query" and
|
||||
* "Chart Options". The controls that define the inputs to
|
||||
* the chart data request, such as columns and metrics, usually
|
||||
* reside within "Query", while controls that affect the visual
|
||||
* appearance or functionality of the chart are under the
|
||||
* "Chart Options" section.
|
||||
*
|
||||
* There are several predefined controls that can be used.
|
||||
* Some examples:
|
||||
* - groupby: columns to group by (tranlated to GROUP BY statement)
|
||||
* - series: same as groupby, but single selection.
|
||||
* - metrics: multiple metrics (translated to aggregate expression)
|
||||
* - metric: sane as metrics, but single selection
|
||||
* - adhoc_filters: filters (translated to WHERE or HAVING
|
||||
* depending on filter type)
|
||||
* - row_limit: maximum number of rows (translated to LIMIT statement)
|
||||
*
|
||||
* If a control panel has both a `series` and `groupby` control, and
|
||||
* the user has chosen `col1` as the value for the `series` control,
|
||||
* and `col2` and `col3` as values for the `groupby` control,
|
||||
* the resulting query will contain three `groupby` columns. This is because
|
||||
* we considered `series` control a `groupby` query field and its value
|
||||
* will automatically append the `groupby` field when the query is generated.
|
||||
*
|
||||
* It is also possible to define custom controls by importing the
|
||||
* necessary dependencies and overriding the default parameters, which
|
||||
* can then be placed in the `controlSetRows` section
|
||||
* of the `Query` section instead of a predefined control.
|
||||
*
|
||||
* import { validateNonEmpty } from '@superset-ui/core';
|
||||
* import {
|
||||
* sharedControls,
|
||||
* ControlConfig,
|
||||
* ControlPanelConfig,
|
||||
* } from '@superset-ui/chart-controls';
|
||||
*
|
||||
* const myControl: ControlConfig<'SelectControl'> = {
|
||||
* name: 'secondary_entity',
|
||||
* config: {
|
||||
* ...sharedControls.entity,
|
||||
* type: 'SelectControl',
|
||||
* label: t('Secondary Entity'),
|
||||
* mapStateToProps: state => ({
|
||||
* sharedControls.columnChoices(state.datasource)
|
||||
* .columns.filter(c => c.groupby)
|
||||
* })
|
||||
* validators: [validateNonEmpty],
|
||||
* },
|
||||
* }
|
||||
*
|
||||
* In addition to the basic drop down control, there are several predefined
|
||||
* control types (can be set via the `type` property) that can be used. Some
|
||||
* commonly used examples:
|
||||
* - SelectControl: Dropdown to select single or multiple values,
|
||||
usually columns
|
||||
* - MetricsControl: Dropdown to select metrics, triggering a modal
|
||||
to define Metric details
|
||||
* - AdhocFilterControl: Control to choose filters
|
||||
* - CheckboxControl: A checkbox for choosing true/false values
|
||||
* - SliderControl: A slider with min/max values
|
||||
* - TextControl: Control for text data
|
||||
*
|
||||
* For more control input types, check out the `incubator-superset` repo
|
||||
* and open this file: superset-frontend/src/explore/components/controls/index.js
|
||||
*
|
||||
* To ensure all controls have been filled out correctly, the following
|
||||
* validators are provided
|
||||
* by the `@superset-ui/core/lib/validator`:
|
||||
* - validateNonEmpty: must have at least one value
|
||||
* - validateInteger: must be an integer value
|
||||
* - validateNumber: must be an intger or decimal value
|
||||
*/
|
||||
|
||||
// For control input types, see: superset-frontend/src/explore/components/controls/index.js
|
||||
controlPanelSections: [
|
||||
sections.legacyTimeseriesTime,
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[queryModeControlSetItem],
|
||||
[groupByControlSetItem],
|
||||
[metricsControlSetItem, allColumnsControlSetItem],
|
||||
[percentMetricsControlSetItem],
|
||||
[timeSeriesLimitMetricControlSetItem, orderByControlSetItem],
|
||||
serverPaginationControlSetRow,
|
||||
[rowLimitControlSetItem, serverPageLengthControlSetItem],
|
||||
[includeTimeControlSetItem, orderDescendingControlSetItem],
|
||||
[showTotalsControlSetItem],
|
||||
['adhoc_filters'],
|
||||
emitFilterControl,
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Options'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[handlebarsTemplateControlSetItem],
|
||||
[styleControlSetItem],
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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 {
|
||||
ColumnOption,
|
||||
ControlSetItem,
|
||||
ExtraControlProps,
|
||||
sharedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import {
|
||||
ensureIsArray,
|
||||
FeatureFlag,
|
||||
isFeatureEnabled,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import React from 'react';
|
||||
import { getQueryMode, isRawMode } from './shared';
|
||||
|
||||
export const allColumns: typeof sharedControls.groupby = {
|
||||
type: 'SelectControl',
|
||||
label: t('Columns'),
|
||||
description: t('Columns to display'),
|
||||
multi: true,
|
||||
freeForm: true,
|
||||
allowAll: true,
|
||||
commaChoosesOption: false,
|
||||
default: [],
|
||||
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,
|
||||
};
|
||||
|
||||
const dndAllColumns: typeof sharedControls.groupby = {
|
||||
type: 'DndColumnSelect',
|
||||
label: t('Columns'),
|
||||
description: t('Columns to display'),
|
||||
default: [],
|
||||
mapStateToProps({ datasource, controls }, controlState) {
|
||||
const newState: ExtraControlProps = {};
|
||||
if (datasource) {
|
||||
const options = datasource.columns;
|
||||
newState.options = Object.fromEntries(
|
||||
options.map(option => [option.column_name, option]),
|
||||
);
|
||||
}
|
||||
newState.queryMode = getQueryMode(controls);
|
||||
newState.externalValidationErrors =
|
||||
isRawMode({ controls }) && ensureIsArray(controlState.value).length === 0
|
||||
? [t('must have a value')]
|
||||
: [];
|
||||
return newState;
|
||||
},
|
||||
visibility: isRawMode,
|
||||
};
|
||||
|
||||
export const allColumnsControlSetItem: ControlSetItem = {
|
||||
name: 'all_columns',
|
||||
config: isFeatureEnabled(FeatureFlag.ENABLE_EXPLORE_DRAG_AND_DROP)
|
||||
? dndAllColumns
|
||||
: allColumns,
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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 {
|
||||
ControlPanelState,
|
||||
ControlSetItem,
|
||||
ControlState,
|
||||
sharedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { isAggMode, validateAggControlValues } from './shared';
|
||||
|
||||
export const groupByControlSetItem: ControlSetItem = {
|
||||
name: 'groupby',
|
||||
override: {
|
||||
visibility: isAggMode,
|
||||
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,
|
||||
controls.percent_metrics?.value,
|
||||
controlState.value,
|
||||
]);
|
||||
|
||||
return newState;
|
||||
},
|
||||
rerender: ['metrics', 'percent_metrics'],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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 {
|
||||
ControlSetItem,
|
||||
CustomControlConfig,
|
||||
sharedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { t, validateNonEmpty } from '@superset-ui/core';
|
||||
import React from 'react';
|
||||
import { CodeEditor } from '../../components/CodeEditor/CodeEditor';
|
||||
import { ControlHeader } from '../../components/ControlHeader/controlHeader';
|
||||
|
||||
interface HandlebarsCustomControlProps {
|
||||
value: string;
|
||||
}
|
||||
|
||||
const HandlebarsTemplateControl = (
|
||||
props: CustomControlConfig<HandlebarsCustomControlProps>,
|
||||
) => {
|
||||
const val = String(
|
||||
props?.value ? props?.value : props?.default ? props?.default : '',
|
||||
);
|
||||
|
||||
const updateConfig = (source: string) => {
|
||||
props.onChange(source);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<ControlHeader>{props.label}</ControlHeader>
|
||||
<CodeEditor
|
||||
theme="dark"
|
||||
value={val}
|
||||
onChange={source => {
|
||||
updateConfig(source || '');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const handlebarsTemplateControlSetItem: ControlSetItem = {
|
||||
name: 'handlebarsTemplate',
|
||||
config: {
|
||||
...sharedControls.entity,
|
||||
type: HandlebarsTemplateControl,
|
||||
label: t('Handlebars Template'),
|
||||
description: t('A handlebars template that is applied to the data'),
|
||||
default: `<ul class="data_list">
|
||||
{{#each data}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>`,
|
||||
isInt: false,
|
||||
renderTrigger: true,
|
||||
|
||||
validators: [validateNonEmpty],
|
||||
mapStateToProps: ({ controls }) => ({
|
||||
value: controls?.handlebars_template?.value,
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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 { ControlSetItem } from '@superset-ui/chart-controls';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { isAggMode } from './shared';
|
||||
|
||||
export const includeTimeControlSetItem: ControlSetItem = {
|
||||
name: 'include_time',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Include time'),
|
||||
description: t(
|
||||
'Whether to include the time granularity as defined in the time section',
|
||||
),
|
||||
default: false,
|
||||
visibility: isAggMode,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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 {
|
||||
ControlPanelsContainerProps,
|
||||
ControlSetItem,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { isAggMode } from './shared';
|
||||
|
||||
export const rowLimitControlSetItem: ControlSetItem = {
|
||||
name: 'row_limit',
|
||||
override: {
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
!controls?.server_pagination?.value,
|
||||
},
|
||||
};
|
||||
|
||||
export const timeSeriesLimitMetricControlSetItem: ControlSetItem = {
|
||||
name: 'timeseries_limit_metric',
|
||||
override: {
|
||||
visibility: isAggMode,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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 {
|
||||
ControlPanelState,
|
||||
ControlSetItem,
|
||||
ControlState,
|
||||
sharedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
|
||||
import { getQueryMode, isAggMode, validateAggControlValues } from './shared';
|
||||
|
||||
const percentMetrics: typeof sharedControls.metrics = {
|
||||
type: 'MetricsControl',
|
||||
label: t('Percentage metrics'),
|
||||
description: t(
|
||||
'Metrics for which percentage of total are to be displayed. Calculated from only data within the row limit.',
|
||||
),
|
||||
multi: true,
|
||||
visibility: isAggMode,
|
||||
mapStateToProps: ({ datasource, controls }, controlState) => ({
|
||||
columns: datasource?.columns || [],
|
||||
savedMetrics: datasource?.metrics || [],
|
||||
datasource,
|
||||
datasourceType: datasource?.type,
|
||||
queryMode: getQueryMode(controls),
|
||||
externalValidationErrors: validateAggControlValues(controls, [
|
||||
controls.groupby?.value,
|
||||
controls.metrics?.value,
|
||||
controlState.value,
|
||||
]),
|
||||
}),
|
||||
rerender: ['groupby', 'metrics'],
|
||||
default: [],
|
||||
validators: [],
|
||||
};
|
||||
|
||||
const dndPercentMetrics = {
|
||||
...percentMetrics,
|
||||
type: 'DndMetricSelect',
|
||||
};
|
||||
|
||||
export const percentMetricsControlSetItem: ControlSetItem = {
|
||||
name: 'percent_metrics',
|
||||
config: {
|
||||
...(isFeatureEnabled(FeatureFlag.ENABLE_EXPLORE_DRAG_AND_DROP)
|
||||
? dndPercentMetrics
|
||||
: percentMetrics),
|
||||
},
|
||||
};
|
||||
|
||||
export const metricsControlSetItem: ControlSetItem = {
|
||||
name: 'metrics',
|
||||
override: {
|
||||
validators: [],
|
||||
visibility: isAggMode,
|
||||
mapStateToProps: (
|
||||
{ controls, datasource, form_data }: ControlPanelState,
|
||||
controlState: ControlState,
|
||||
) => ({
|
||||
columns: datasource?.columns.filter(c => c.filterable) || [],
|
||||
savedMetrics: datasource?.metrics || [],
|
||||
// current active adhoc metrics
|
||||
selectedMetrics:
|
||||
form_data.metrics || (form_data.metric ? [form_data.metric] : []),
|
||||
datasource,
|
||||
externalValidationErrors: validateAggControlValues(controls, [
|
||||
controls.groupby?.value,
|
||||
controls.percent_metrics?.value,
|
||||
controlState.value,
|
||||
]),
|
||||
}),
|
||||
rerender: ['groupby', 'percent_metrics'],
|
||||
},
|
||||
};
|
||||
|
||||
export const showTotalsControlSetItem: ControlSetItem = {
|
||||
name: 'show_totals',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show totals'),
|
||||
default: false,
|
||||
description: t(
|
||||
'Show total aggregations of selected metrics. Note that row limit does not apply to the result.',
|
||||
),
|
||||
visibility: isAggMode,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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 { ControlSetItem } from '@superset-ui/chart-controls';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { isAggMode, isRawMode } from './shared';
|
||||
|
||||
export const orderByControlSetItem: ControlSetItem = {
|
||||
name: 'order_by_cols',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Ordering'),
|
||||
description: t('Order results by selected columns'),
|
||||
multi: true,
|
||||
default: [],
|
||||
mapStateToProps: ({ datasource }) => ({
|
||||
choices: datasource?.order_by_choices || [],
|
||||
}),
|
||||
visibility: isRawMode,
|
||||
},
|
||||
};
|
||||
|
||||
export const orderDescendingControlSetItem: ControlSetItem = {
|
||||
name: 'order_desc',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Sort descending'),
|
||||
default: true,
|
||||
description: t('Whether to sort descending or ascending'),
|
||||
visibility: isAggMode,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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 {
|
||||
ControlPanelsContainerProps,
|
||||
ControlSetItem,
|
||||
ControlSetRow,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
|
||||
import { PAGE_SIZE_OPTIONS } from '../../consts';
|
||||
|
||||
export const serverPaginationControlSetRow: ControlSetRow =
|
||||
isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) ||
|
||||
isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS)
|
||||
? [
|
||||
{
|
||||
name: 'server_pagination',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Server pagination'),
|
||||
description: t(
|
||||
'Enable server side pagination of results (experimental feature)',
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
export const serverPageLengthControlSetItem: ControlSetItem = {
|
||||
name: 'server_page_length',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Server Page Length'),
|
||||
default: 10,
|
||||
choices: PAGE_SIZE_OPTIONS,
|
||||
description: t('Rows per page, 0 means no pagination'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
Boolean(controls?.server_pagination?.value),
|
||||
},
|
||||
};
|
||||
@@ -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.
|
||||
*/
|
||||
import {
|
||||
ControlConfig,
|
||||
ControlSetItem,
|
||||
QueryModeLabel,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { QueryMode, t } from '@superset-ui/core';
|
||||
import { getQueryMode } from './shared';
|
||||
|
||||
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'],
|
||||
};
|
||||
|
||||
export const queryModeControlSetItem: ControlSetItem = {
|
||||
name: 'query_mode',
|
||||
config: queryMode,
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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 {
|
||||
ControlPanelsContainerProps,
|
||||
ControlStateMapping,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import {
|
||||
ensureIsArray,
|
||||
QueryFormColumn,
|
||||
QueryMode,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visibility check
|
||||
*/
|
||||
export function isQueryMode(mode: QueryMode) {
|
||||
return ({ controls }: Pick<ControlPanelsContainerProps, 'controls'>) =>
|
||||
getQueryMode(controls) === mode;
|
||||
}
|
||||
|
||||
export const isAggMode = isQueryMode(QueryMode.aggregate);
|
||||
export const isRawMode = isQueryMode(QueryMode.raw);
|
||||
|
||||
export 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')]
|
||||
: [];
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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 {
|
||||
ControlSetItem,
|
||||
CustomControlConfig,
|
||||
sharedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import { t } from '@superset-ui/core';
|
||||
import React from 'react';
|
||||
import { CodeEditor } from '../../components/CodeEditor/CodeEditor';
|
||||
import { ControlHeader } from '../../components/ControlHeader/controlHeader';
|
||||
|
||||
interface StyleCustomControlProps {
|
||||
value: string;
|
||||
}
|
||||
|
||||
const StyleControl = (props: CustomControlConfig<StyleCustomControlProps>) => {
|
||||
const val = String(
|
||||
props?.value ? props?.value : props?.default ? props?.default : '',
|
||||
);
|
||||
|
||||
const updateConfig = (source: string) => {
|
||||
props.onChange(source);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<ControlHeader>{props.label}</ControlHeader>
|
||||
<CodeEditor
|
||||
theme="dark"
|
||||
mode="css"
|
||||
value={val}
|
||||
onChange={source => {
|
||||
updateConfig(source || '');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const styleControlSetItem: ControlSetItem = {
|
||||
name: 'styleTemplate',
|
||||
config: {
|
||||
...sharedControls.entity,
|
||||
type: StyleControl,
|
||||
label: t('CSS Styles'),
|
||||
description: t('CSS applied to the chart'),
|
||||
default: '',
|
||||
isInt: false,
|
||||
renderTrigger: true,
|
||||
|
||||
validators: [],
|
||||
mapStateToProps: ({ controls }) => ({
|
||||
value: controls?.handlebars_template?.value,
|
||||
}),
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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 { ChartMetadata, ChartPlugin, t } from '@superset-ui/core';
|
||||
import thumbnail from '../images/thumbnail.png';
|
||||
import buildQuery from './buildQuery';
|
||||
import controlPanel from './controlPanel';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default class HandlebarsChartPlugin extends ChartPlugin {
|
||||
/**
|
||||
* The constructor is used to pass relevant metadata and callbacks that get
|
||||
* registered in respective registries that are used throughout the library
|
||||
* and application. A more thorough description of each property is given in
|
||||
* the respective imported file.
|
||||
*
|
||||
* It is worth noting that `buildQuery` and is optional, and only needed for
|
||||
* advanced visualizations that require either post processing operations
|
||||
* (pivoting, rolling aggregations, sorting etc) or submitting multiple queries.
|
||||
*/
|
||||
constructor() {
|
||||
const metadata = new ChartMetadata({
|
||||
description: 'Write a handlebars template to render the data',
|
||||
name: t('Handlebars'),
|
||||
thumbnail,
|
||||
});
|
||||
|
||||
super({
|
||||
buildQuery,
|
||||
controlPanel,
|
||||
loadChart: () => import('../Handlebars'),
|
||||
metadata,
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* 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, TimeseriesDataRecord } from '@superset-ui/core';
|
||||
|
||||
export default function transformProps(chartProps: ChartProps) {
|
||||
/**
|
||||
* This function is called after a successful response has been
|
||||
* received from the chart data endpoint, and is used to transform
|
||||
* the incoming data prior to being sent to the Visualization.
|
||||
*
|
||||
* The transformProps function is also quite useful to return
|
||||
* additional/modified props to your data viz component. The formData
|
||||
* can also be accessed from your Handlebars.tsx file, but
|
||||
* doing supplying custom props here is often handy for integrating third
|
||||
* party libraries that rely on specific props.
|
||||
*
|
||||
* A description of properties in `chartProps`:
|
||||
* - `height`, `width`: the height/width of the DOM element in which
|
||||
* the chart is located
|
||||
* - `formData`: the chart data request payload that was sent to the
|
||||
* backend.
|
||||
* - `queriesData`: the chart data response payload that was received
|
||||
* from the backend. Some notable properties of `queriesData`:
|
||||
* - `data`: an array with data, each row with an object mapping
|
||||
* the column/alias to its value. Example:
|
||||
* `[{ col1: 'abc', metric1: 10 }, { col1: 'xyz', metric1: 20 }]`
|
||||
* - `rowcount`: the number of rows in `data`
|
||||
* - `query`: the query that was issued.
|
||||
*
|
||||
* Please note: the transformProps function gets cached when the
|
||||
* application loads. When making changes to the `transformProps`
|
||||
* function during development with hot reloading, changes won't
|
||||
* be seen until restarting the development server.
|
||||
*/
|
||||
const { width, height, formData, queriesData } = chartProps;
|
||||
const data = queriesData[0].data as TimeseriesDataRecord[];
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
|
||||
data: data.map(item => ({
|
||||
...item,
|
||||
// convert epoch to native Date
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
__timestamp: new Date(item.__timestamp as number),
|
||||
})),
|
||||
// and now your control data, manipulated as needed, and passed through as props!
|
||||
formData,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user