Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(plugin-chart-handlebars): fix overflow, debounce and control reset #19879

Merged
merged 8 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions superset-frontend/plugins/plugin-chart-handlebars/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,20 @@
"access": "public"
},
"dependencies": {
"@superset-ui/chart-controls": "0.18.25",
"@superset-ui/core": "0.18.25",
"ace-builds": "^1.4.13",
"emotion": "^11.0.0",
"handlebars": "^4.7.7",
"react-ace": "^9.4.4"
"handlebars": "^4.7.7"
},
"peerDependencies": {
"@superset-ui/chart-controls": "*",
"@superset-ui/core": "*",
"ace-builds": "^1.4.14",
"lodash": "^4.17.11",
"moment": "^2.26.0",
"react": "^16.13.1",
"react-ace": "^9.4.4",
"react-dom": "^16.13.1"
},
"devDependencies": {
"@types/lodash": "^4.14.149",
"@types/jest": "^26.0.0",
"jest": "^26.0.1"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,19 @@
* under the License.
*/
import { styled } from '@superset-ui/core';
import React, { createRef, useEffect } from 'react';
import React, { createRef } from 'react';
import { HandlebarsViewer } from './components/Handlebars/HandlebarsViewer';
import { HandlebarsProps, HandlebarsStylesProps } from './types';

// The following Styles component is a <div> element, which has been styled using Emotion
// For docs, visit https://emotion.sh/docs/styled

// Theming variables are provided for your use via a ThemeProvider
// imported from @superset-ui/core. For variables available, please visit
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-core/src/style/index.ts

const Styles = styled.div<HandlebarsStylesProps>`
padding: ${({ theme }) => theme.gridUnit * 4}px;
border-radius: ${({ theme }) => theme.gridUnit * 2}px;
height: ${({ height }) => height};
width: ${({ width }) => width};
overflow-y: scroll;
height: ${({ height }) => height}px;
width: ${({ width }) => width}px;
overflow: auto;
`;

/**
* ******************* WHAT YOU CAN BUILD HERE *******************
* In essence, a chart is given a few key ingredients to work with:
* * Data: provided via `props.data`
* * A DOM element
* * FormData (your controls!) provided as props by transformProps.ts
*/

export default function Handlebars(props: HandlebarsProps) {
// height and width are the height and width of the DOM element as it exists in the dashboard.
// There is also a `data` prop, which is, of course, your DATA 🎉
const { data, height, width, formData } = props;
const styleTemplateSource = formData.styleTemplate
? `<style>${formData.styleTemplate}</style>`
Expand All @@ -58,13 +41,6 @@ export default function Handlebars(props: HandlebarsProps) {

const rootElem = createRef<HTMLDivElement>();

// Often, you just want to get a hold of the DOM and go nuts.
// Here, you can do that with createRef, and the useEffect hook.
useEffect(() => {
// const root = rootElem.current as HTMLElement;
// console.log('Plugin element', root);
});

return (
<Styles ref={rootElem} height={height} width={width}>
<HandlebarsViewer data={{ data }} templateSource={templateSource} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { SafeMarkdown, styled } from '@superset-ui/core';
import Handlebars from 'handlebars';
import moment from 'moment';
import React, { useMemo, useState } from 'react';
import { isPlainObject } from 'lodash';

export interface HandlebarsViewerProps {
templateSource: string;
Expand Down Expand Up @@ -64,3 +65,11 @@ Handlebars.registerHelper('dateFormat', function (context, block) {
const f = block.hash.format || 'YYYY-MM-DD';
return moment(context).format(f);
});

// usage: {{ }}
Handlebars.registerHelper('stringify', (obj: any, obj2: any) => {
// calling without an argument
if (obj2 === undefined)
throw Error('Please call with an object. Example: `stringify myObj`');
return isPlainObject(obj) ? JSON.stringify(obj) : String(obj);
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
import { debounce } from 'lodash';
import { formatSelectOptions } from '@superset-ui/chart-controls';
import { addLocaleData, t } from '@superset-ui/core';
import { addLocaleData, SLOW_DEBOUNCE, t } from '@superset-ui/core';
import i18n from './i18n';

addLocaleData(i18n);
Expand All @@ -35,3 +36,8 @@ export const PAGE_SIZE_OPTIONS = formatSelectOptions<number>([
100,
200,
]);

export const debounceFunc = debounce(
(func: (val: string) => void, source: string) => func(source),
SLOW_DEBOUNCE,
);
Original file line number Diff line number Diff line change
Expand Up @@ -50,81 +50,6 @@ 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,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import React from 'react';
import { getQueryMode, isRawMode } from './shared';

export const allColumns: typeof sharedControls.groupby = {
const allColumns: typeof sharedControls.groupby = {
type: 'SelectControl',
label: t('Columns'),
description: t('Columns to display'),
Expand All @@ -52,6 +52,7 @@ export const allColumns: typeof sharedControls.groupby = {
: [],
}),
visibility: isRawMode,
resetOnHide: false,
};

const dndAllColumns: typeof sharedControls.groupby = {
Expand All @@ -75,6 +76,7 @@ const dndAllColumns: typeof sharedControls.groupby = {
return newState;
},
visibility: isRawMode,
resetOnHide: false,
};

export const allColumnsControlSetItem: ControlSetItem = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const groupByControlSetItem: ControlSetItem = {
name: 'groupby',
override: {
visibility: isAggMode,
resetOnHide: false,
mapStateToProps: (state: ControlPanelState, controlState: ControlState) => {
const { controls } = state;
const originalMapStateToProps = sharedControls?.groupby?.mapStateToProps;
Expand All @@ -37,7 +38,6 @@ export const groupByControlSetItem: ControlSetItem = {
controls.percent_metrics?.value,
controlState.value,
]);

return newState;
},
rerender: ['metrics', 'percent_metrics'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { t, validateNonEmpty } from '@superset-ui/core';
import React from 'react';
import { CodeEditor } from '../../components/CodeEditor/CodeEditor';
import { ControlHeader } from '../../components/ControlHeader/controlHeader';
import { debounceFunc } from '../../consts';

interface HandlebarsCustomControlProps {
value: string;
Expand All @@ -37,17 +38,14 @@ const HandlebarsTemplateControl = (
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 || '');
debounceFunc(props.onChange, source || '');
}}
/>
</div>
Expand All @@ -61,11 +59,11 @@ export const handlebarsTemplateControlSetItem: ControlSetItem = {
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>`,
default: `<ul class="data-list">
{{#each data}}
<li>{{stringify this}}</li>
{{/each}}
</ul>`,
isInt: false,
renderTrigger: true,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ export const includeTimeControlSetItem: ControlSetItem = {
),
default: false,
visibility: isAggMode,
resetOnHide: false,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ export const timeSeriesLimitMetricControlSetItem: ControlSetItem = {
name: 'timeseries_limit_metric',
override: {
visibility: isAggMode,
resetOnHide: false,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const percentMetrics: typeof sharedControls.metrics = {
),
multi: true,
visibility: isAggMode,
resetOnHide: false,
mapStateToProps: ({ datasource, controls }, controlState) => ({
columns: datasource?.columns || [],
savedMetrics: datasource?.metrics || [],
Expand Down Expand Up @@ -86,6 +87,7 @@ export const metricsControlSetItem: ControlSetItem = {
]),
}),
rerender: ['groupby', 'percent_metrics'],
resetOnHide: false,
},
};

Expand All @@ -99,5 +101,6 @@ export const showTotalsControlSetItem: ControlSetItem = {
'Show total aggregations of selected metrics. Note that row limit does not apply to the result.',
),
visibility: isAggMode,
resetOnHide: false,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const orderByControlSetItem: ControlSetItem = {
choices: datasource?.order_by_choices || [],
}),
visibility: isRawMode,
resetOnHide: false,
},
};

Expand All @@ -43,5 +44,6 @@ export const orderDescendingControlSetItem: ControlSetItem = {
default: true,
description: t('Whether to sort descending or ascending'),
visibility: isAggMode,
resetOnHide: false,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { t } from '@superset-ui/core';
import React from 'react';
import { CodeEditor } from '../../components/CodeEditor/CodeEditor';
import { ControlHeader } from '../../components/ControlHeader/controlHeader';
import { debounceFunc } from '../../consts';

interface StyleCustomControlProps {
value: string;
Expand All @@ -35,9 +36,6 @@ const StyleControl = (props: CustomControlConfig<StyleCustomControlProps>) => {
props?.value ? props?.value : props?.default ? props?.default : '',
);

const updateConfig = (source: string) => {
props.onChange(source);
};
return (
<div>
<ControlHeader>{props.label}</ControlHeader>
Expand All @@ -46,7 +44,7 @@ const StyleControl = (props: CustomControlConfig<StyleCustomControlProps>) => {
mode="css"
value={val}
onChange={source => {
updateConfig(source || '');
debounceFunc(props.onChange, source || '');
}}
/>
</div>
Expand All @@ -60,7 +58,11 @@ export const styleControlSetItem: ControlSetItem = {
type: StyleControl,
label: t('CSS Styles'),
description: t('CSS applied to the chart'),
default: '',
default: `/*
.data-list {
background-color: yellow;
}
*/`,
isInt: false,
renderTrigger: true,

Expand Down
Loading