mirror of
https://github.com/apache/superset.git
synced 2026-04-23 10:04:45 +00:00
refactor: Removes the Filter Box code (#26328)
Co-authored-by: John Bodley <john.bodley@gmail.com>
This commit is contained in:
committed by
GitHub
parent
591f266543
commit
d9a3c3e1dd
@@ -1,480 +0,0 @@
|
||||
/**
|
||||
* 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 PropTypes from 'prop-types';
|
||||
import { debounce } from 'lodash';
|
||||
import { max as d3Max } from 'd3-array';
|
||||
import {
|
||||
AsyncCreatableSelect,
|
||||
CreatableSelect,
|
||||
} from 'src/components/DeprecatedSelect';
|
||||
import Button from 'src/components/Button';
|
||||
import {
|
||||
css,
|
||||
styled,
|
||||
t,
|
||||
SupersetClient,
|
||||
ensureIsArray,
|
||||
withTheme,
|
||||
} from '@superset-ui/core';
|
||||
import { Global } from '@emotion/react';
|
||||
|
||||
import {
|
||||
BOOL_FALSE_DISPLAY,
|
||||
BOOL_TRUE_DISPLAY,
|
||||
SLOW_DEBOUNCE,
|
||||
} from 'src/constants';
|
||||
import { FormLabel } from 'src/components/Form';
|
||||
import DateFilterControl from 'src/explore/components/controls/DateFilterControl';
|
||||
import ControlRow from 'src/explore/components/ControlRow';
|
||||
import Control from 'src/explore/components/Control';
|
||||
import { controls } from 'src/explore/controls';
|
||||
import { getExploreUrl } from 'src/explore/exploreUtils';
|
||||
import OnPasteSelect from 'src/components/DeprecatedSelect/OnPasteSelect';
|
||||
import {
|
||||
FILTER_CONFIG_ATTRIBUTES,
|
||||
FILTER_OPTIONS_LIMIT,
|
||||
TIME_FILTER_LABELS,
|
||||
TIME_FILTER_MAP,
|
||||
} from 'src/explore/constants';
|
||||
|
||||
// a shortcut to a map key, used by many components
|
||||
export const TIME_RANGE = TIME_FILTER_MAP.time_range;
|
||||
|
||||
const propTypes = {
|
||||
chartId: PropTypes.number.isRequired,
|
||||
origSelectedValues: PropTypes.object,
|
||||
datasource: PropTypes.object.isRequired,
|
||||
instantFiltering: PropTypes.bool,
|
||||
filtersFields: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
field: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
}),
|
||||
),
|
||||
filtersChoices: PropTypes.objectOf(
|
||||
PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
text: PropTypes.string,
|
||||
filter: PropTypes.string,
|
||||
metric: PropTypes.number,
|
||||
}),
|
||||
),
|
||||
),
|
||||
onChange: PropTypes.func,
|
||||
onFilterMenuOpen: PropTypes.func,
|
||||
onFilterMenuClose: PropTypes.func,
|
||||
showDateFilter: PropTypes.bool,
|
||||
showSqlaTimeGrain: PropTypes.bool,
|
||||
showSqlaTimeColumn: PropTypes.bool,
|
||||
};
|
||||
const defaultProps = {
|
||||
origSelectedValues: {},
|
||||
onChange: () => {},
|
||||
onFilterMenuOpen: () => {},
|
||||
onFilterMenuClose: () => {},
|
||||
showDateFilter: false,
|
||||
showSqlaTimeGrain: false,
|
||||
showSqlaTimeColumn: false,
|
||||
instantFiltering: false,
|
||||
};
|
||||
|
||||
const StyledFilterContainer = styled.div`
|
||||
${({ theme }) => `
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: ${theme.gridUnit * 2 + 2}px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
}
|
||||
|
||||
.filter-badge-container {
|
||||
width: 30px;
|
||||
padding-right: ${theme.gridUnit * 2 + 2}px;
|
||||
}
|
||||
|
||||
.filter-badge-container + div {
|
||||
width: 100%;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
/**
|
||||
* @deprecated in version 3.0.
|
||||
*/
|
||||
class FilterBox extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedValues: props.origSelectedValues,
|
||||
// this flag is used by non-instant filter, to make the apply button enabled/disabled
|
||||
hasChanged: false,
|
||||
};
|
||||
this.debouncerCache = {};
|
||||
this.maxValueCache = {};
|
||||
this.changeFilter = this.changeFilter.bind(this);
|
||||
this.onFilterMenuOpen = this.onFilterMenuOpen.bind(this);
|
||||
this.onOpenDateFilterControl = this.onOpenDateFilterControl.bind(this);
|
||||
this.onFilterMenuClose = this.onFilterMenuClose.bind(this);
|
||||
}
|
||||
|
||||
onFilterMenuOpen(column) {
|
||||
return this.props.onFilterMenuOpen(this.props.chartId, column);
|
||||
}
|
||||
|
||||
onFilterMenuClose(column) {
|
||||
return this.props.onFilterMenuClose(this.props.chartId, column);
|
||||
}
|
||||
|
||||
onOpenDateFilterControl() {
|
||||
return this.onFilterMenuOpen(TIME_RANGE);
|
||||
}
|
||||
|
||||
onCloseDateFilterControl = () => this.onFilterMenuClose(TIME_RANGE);
|
||||
|
||||
getControlData(controlName) {
|
||||
const { selectedValues } = this.state;
|
||||
const control = {
|
||||
...controls[controlName], // TODO: make these controls ('granularity_sqla', 'time_grain_sqla') accessible from getControlsForVizType.
|
||||
name: controlName,
|
||||
key: `control-${controlName}`,
|
||||
value: selectedValues[TIME_FILTER_MAP[controlName]],
|
||||
actions: { setControlValue: this.changeFilter },
|
||||
};
|
||||
const mapFunc = control.mapStateToProps;
|
||||
return mapFunc ? { ...control, ...mapFunc(this.props) } : control;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get known max value of a column
|
||||
*/
|
||||
getKnownMax(key, choices) {
|
||||
this.maxValueCache[key] = Math.max(
|
||||
this.maxValueCache[key] || 0,
|
||||
d3Max(choices || this.props.filtersChoices[key] || [], x => x.metric),
|
||||
);
|
||||
return this.maxValueCache[key];
|
||||
}
|
||||
|
||||
clickApply() {
|
||||
const { selectedValues } = this.state;
|
||||
this.setState({ hasChanged: false }, () => {
|
||||
this.props.onChange(selectedValues, false);
|
||||
});
|
||||
}
|
||||
|
||||
changeFilter(filter, options) {
|
||||
const fltr = TIME_FILTER_MAP[filter] || filter;
|
||||
let vals = null;
|
||||
if (options !== null) {
|
||||
if (Array.isArray(options)) {
|
||||
vals = options.map(opt => (typeof opt === 'string' ? opt : opt.value));
|
||||
} else if (Object.values(TIME_FILTER_MAP).includes(fltr)) {
|
||||
vals = options.value ?? options;
|
||||
} else {
|
||||
// must use array member for legacy extra_filters's value
|
||||
vals = ensureIsArray(options.value ?? options);
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(
|
||||
prevState => ({
|
||||
selectedValues: {
|
||||
...prevState.selectedValues,
|
||||
[fltr]: vals,
|
||||
},
|
||||
hasChanged: true,
|
||||
}),
|
||||
() => {
|
||||
if (this.props.instantFiltering) {
|
||||
this.props.onChange({ [fltr]: vals }, false);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a debounce function that loads options for a specific column
|
||||
*/
|
||||
debounceLoadOptions(key) {
|
||||
if (!(key in this.debouncerCache)) {
|
||||
this.debouncerCache[key] = debounce((input, callback) => {
|
||||
this.loadOptions(key, input).then(callback);
|
||||
}, SLOW_DEBOUNCE);
|
||||
}
|
||||
return this.debouncerCache[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform select options, add bar background
|
||||
*/
|
||||
transformOptions(options, max) {
|
||||
const maxValue = max === undefined ? d3Max(options, x => x.metric) : max;
|
||||
return options.map(opt => {
|
||||
const perc = Math.round((opt.metric / maxValue) * 100);
|
||||
const color = 'lightgrey';
|
||||
const backgroundImage = `linear-gradient(to right, ${color}, ${color} ${perc}%, rgba(0,0,0,0) ${perc}%`;
|
||||
const style = { backgroundImage };
|
||||
let label = opt.id;
|
||||
if (label === true) {
|
||||
label = BOOL_TRUE_DISPLAY;
|
||||
} else if (label === false) {
|
||||
label = BOOL_FALSE_DISPLAY;
|
||||
}
|
||||
return { value: opt.id, label, style };
|
||||
});
|
||||
}
|
||||
|
||||
async loadOptions(key, inputValue = '') {
|
||||
const input = inputValue.toLowerCase();
|
||||
const sortAsc = this.props.filtersFields.find(x => x.key === key).asc;
|
||||
const formData = {
|
||||
...this.props.rawFormData,
|
||||
adhoc_filters: inputValue
|
||||
? [
|
||||
{
|
||||
clause: 'WHERE',
|
||||
expressionType: 'SIMPLE',
|
||||
subject: key,
|
||||
operator: 'ILIKE',
|
||||
comparator: `%${input}%`,
|
||||
},
|
||||
]
|
||||
: null,
|
||||
};
|
||||
|
||||
const { json } = await SupersetClient.get({
|
||||
url: getExploreUrl({
|
||||
formData,
|
||||
endpointType: 'json',
|
||||
method: 'GET',
|
||||
}),
|
||||
});
|
||||
const options = (json?.data?.[key] || []).filter(x => x.id);
|
||||
if (!options || options.length === 0) {
|
||||
return [];
|
||||
}
|
||||
if (input) {
|
||||
// sort those starts with search query to front
|
||||
options.sort((a, b) => {
|
||||
const labelA = a.id.toLowerCase();
|
||||
const labelB = b.id.toLowerCase();
|
||||
const textOrder = labelB.startsWith(input) - labelA.startsWith(input);
|
||||
return textOrder === 0
|
||||
? (a.metric - b.metric) * (sortAsc ? 1 : -1)
|
||||
: textOrder;
|
||||
});
|
||||
}
|
||||
return this.transformOptions(options, this.getKnownMax(key, options));
|
||||
}
|
||||
|
||||
renderDateFilter() {
|
||||
const { showDateFilter } = this.props;
|
||||
const label = TIME_FILTER_LABELS.time_range;
|
||||
if (showDateFilter) {
|
||||
return (
|
||||
<div className="row space-1">
|
||||
<div
|
||||
className="col-lg-12 col-xs-12"
|
||||
data-test="date-filter-container"
|
||||
>
|
||||
<DateFilterControl
|
||||
name={TIME_RANGE}
|
||||
label={label}
|
||||
description={t('Select start and end date')}
|
||||
onChange={newValue => {
|
||||
this.changeFilter(TIME_RANGE, newValue);
|
||||
}}
|
||||
onOpenDateFilterControl={this.onOpenDateFilterControl}
|
||||
onCloseDateFilterControl={this.onCloseDateFilterControl}
|
||||
value={this.state.selectedValues[TIME_RANGE] || 'No filter'}
|
||||
endpoints={['inclusive', 'exclusive']}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderDatasourceFilters() {
|
||||
const { showSqlaTimeGrain, showSqlaTimeColumn } = this.props;
|
||||
const datasourceFilters = [];
|
||||
const sqlaFilters = [];
|
||||
if (showSqlaTimeGrain) sqlaFilters.push('time_grain_sqla');
|
||||
if (showSqlaTimeColumn) sqlaFilters.push('granularity_sqla');
|
||||
if (sqlaFilters.length) {
|
||||
datasourceFilters.push(
|
||||
<ControlRow
|
||||
key="sqla-filters"
|
||||
controls={sqlaFilters.map(control => (
|
||||
<Control {...this.getControlData(control)} />
|
||||
))}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
return datasourceFilters;
|
||||
}
|
||||
|
||||
renderSelect(filterConfig) {
|
||||
const { filtersChoices } = this.props;
|
||||
const { selectedValues } = this.state;
|
||||
this.debouncerCache = {};
|
||||
this.maxValueCache = {};
|
||||
|
||||
// Add created options to filtersChoices, even though it doesn't exist,
|
||||
// or these options will exist in query sql but invisible to end user.
|
||||
Object.keys(selectedValues)
|
||||
.filter(key => key in filtersChoices)
|
||||
.forEach(key => {
|
||||
// empty values are ignored
|
||||
if (!selectedValues[key]) {
|
||||
return;
|
||||
}
|
||||
const choices = filtersChoices[key] || (filtersChoices[key] = []);
|
||||
const choiceIds = new Set(choices.map(f => f.id));
|
||||
const selectedValuesForKey = Array.isArray(selectedValues[key])
|
||||
? selectedValues[key]
|
||||
: [selectedValues[key]];
|
||||
selectedValuesForKey
|
||||
.filter(value => value !== null && !choiceIds.has(value))
|
||||
.forEach(value => {
|
||||
choices.unshift({
|
||||
filter: key,
|
||||
id: value,
|
||||
text: value,
|
||||
metric: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
const {
|
||||
key,
|
||||
label,
|
||||
[FILTER_CONFIG_ATTRIBUTES.MULTIPLE]: isMultiple,
|
||||
[FILTER_CONFIG_ATTRIBUTES.DEFAULT_VALUE]: defaultValue,
|
||||
[FILTER_CONFIG_ATTRIBUTES.CLEARABLE]: isClearable,
|
||||
[FILTER_CONFIG_ATTRIBUTES.SEARCH_ALL_OPTIONS]: searchAllOptions,
|
||||
} = filterConfig;
|
||||
const data = filtersChoices[key] || [];
|
||||
let value = selectedValues[key] || null;
|
||||
|
||||
// Assign default value if required
|
||||
if (value === undefined && defaultValue) {
|
||||
// multiple values are separated by semicolons
|
||||
value = isMultiple ? defaultValue.split(';') : defaultValue;
|
||||
}
|
||||
|
||||
return (
|
||||
<OnPasteSelect
|
||||
cacheOptions
|
||||
loadOptions={this.debounceLoadOptions(key)}
|
||||
defaultOptions={this.transformOptions(data)}
|
||||
key={key}
|
||||
placeholder={t('Type or Select [%s]', label)}
|
||||
isMulti={isMultiple}
|
||||
isClearable={isClearable}
|
||||
value={value}
|
||||
options={this.transformOptions(data)}
|
||||
onChange={newValue => {
|
||||
// avoid excessive re-renders
|
||||
if (newValue !== value) {
|
||||
this.changeFilter(key, newValue);
|
||||
}
|
||||
}}
|
||||
// TODO try putting this back once react-select is upgraded
|
||||
// onFocus={() => this.onFilterMenuOpen(key)}
|
||||
onMenuOpen={() => this.onFilterMenuOpen(key)}
|
||||
onBlur={() => this.onFilterMenuClose(key)}
|
||||
onMenuClose={() => this.onFilterMenuClose(key)}
|
||||
selectWrap={
|
||||
searchAllOptions && data.length >= FILTER_OPTIONS_LIMIT
|
||||
? AsyncCreatableSelect
|
||||
: CreatableSelect
|
||||
}
|
||||
noResultsText={t('No results found')}
|
||||
forceOverflow
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderFilters() {
|
||||
const { filtersFields = [] } = this.props;
|
||||
return filtersFields.map(filterConfig => {
|
||||
const { label, key } = filterConfig;
|
||||
return (
|
||||
<StyledFilterContainer key={key} className="filter-container">
|
||||
<FormLabel htmlFor={`LABEL-${key}`}>{label}</FormLabel>
|
||||
{this.renderSelect(filterConfig)}
|
||||
</StyledFilterContainer>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { instantFiltering, width, height } = this.props;
|
||||
const { zIndex, gridUnit } = this.props.theme;
|
||||
return (
|
||||
<>
|
||||
<Global
|
||||
styles={css`
|
||||
.dashboard .filter_box .slice_container > div:not(.alert) {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.filter_box {
|
||||
padding: ${gridUnit * 2 + 2}px 0;
|
||||
overflow: visible !important;
|
||||
|
||||
&:hover {
|
||||
z-index: ${zIndex.max};
|
||||
}
|
||||
}
|
||||
`}
|
||||
/>
|
||||
<div style={{ width, height, overflow: 'auto' }}>
|
||||
{this.renderDateFilter()}
|
||||
{this.renderDatasourceFilters()}
|
||||
{this.renderFilters()}
|
||||
{!instantFiltering && (
|
||||
<Button
|
||||
buttonSize="small"
|
||||
buttonStyle="primary"
|
||||
onClick={this.clickApply.bind(this)}
|
||||
disabled={!this.state.hasChanged}
|
||||
>
|
||||
{t('Apply')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FilterBox.propTypes = propTypes;
|
||||
FilterBox.defaultProps = defaultProps;
|
||||
|
||||
export default withTheme(FilterBox);
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* 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 { styledMount as mount } from 'spec/helpers/theming';
|
||||
import FilterBox from 'src/visualizations/FilterBox/FilterBox';
|
||||
import SelectControl from 'src/explore/components/controls/SelectControl';
|
||||
|
||||
describe('FilterBox', () => {
|
||||
it('should only add defined non-predefined options to filtersChoices', () => {
|
||||
const wrapper = mount(
|
||||
<FilterBox
|
||||
chartId={1001}
|
||||
datasource={{ id: 1 }}
|
||||
filtersChoices={{
|
||||
name: [
|
||||
{ id: 'John', text: 'John', metric: 1234 },
|
||||
{ id: 'Jane', text: 'Jane', metric: 345678 },
|
||||
],
|
||||
}}
|
||||
filtersFields={[
|
||||
{
|
||||
asc: false,
|
||||
clearable: true,
|
||||
column: 'name',
|
||||
key: 'name',
|
||||
label: 'name',
|
||||
metric: 'sum__COUNT',
|
||||
multiple: true,
|
||||
},
|
||||
]}
|
||||
origSelectedValues={{}}
|
||||
/>,
|
||||
);
|
||||
const inst = wrapper.find('FilterBox').instance();
|
||||
// choose a predefined value
|
||||
inst.setState({ selectedValues: { name: ['John'] } });
|
||||
expect(inst.props.filtersChoices.name.length).toEqual(2);
|
||||
// reset selection
|
||||
inst.setState({ selectedValues: { name: null } });
|
||||
expect(inst.props.filtersChoices.name.length).toEqual(2);
|
||||
// Add a new name
|
||||
inst.setState({ selectedValues: { name: 'James' } });
|
||||
expect(inst.props.filtersChoices.name.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('should support granularity_sqla options', () => {
|
||||
const wrapper = mount(
|
||||
<FilterBox
|
||||
chartId={1001}
|
||||
datasource={{
|
||||
id: 1,
|
||||
columns: [],
|
||||
databases: {},
|
||||
granularity_sqla: [
|
||||
['created_on', 'created_on'],
|
||||
['changed_on', 'changed_on'],
|
||||
],
|
||||
}}
|
||||
showSqlaTimeColumn
|
||||
instantFiltering
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find(SelectControl).props().choices).toEqual(
|
||||
expect.arrayContaining([
|
||||
['created_on', 'created_on'],
|
||||
['changed_on', 'changed_on'],
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* 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, ChartMetadata, ChartPlugin, ChartLabel } from '@superset-ui/core';
|
||||
import transformProps from './transformProps';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
import example1 from './images/example1.jpg';
|
||||
import example2 from './images/example2.jpg';
|
||||
import controlPanel from './controlPanel';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
category: t('Tools'),
|
||||
label: ChartLabel.DEPRECATED,
|
||||
name: t('Filter box (legacy)'),
|
||||
description:
|
||||
t(`Chart component that lets you add a custom filter UI in your dashboard. When added to dashboard, a filter box lets users specify specific values or ranges to filter charts by. The charts that each filter box is applied to can be fine tuned as well in the dashboard view.
|
||||
|
||||
Note that this plugin is being replaced with the new Filters feature that lives in the dashboard view itself. It's easier to use and has more capabilities!`),
|
||||
exampleGallery: [{ url: example1 }, { url: example2 }],
|
||||
thumbnail,
|
||||
useLegacyApi: true,
|
||||
tags: [t('Legacy'), t('Deprecated')],
|
||||
});
|
||||
|
||||
/**
|
||||
* @deprecated in version 3.0.
|
||||
*/
|
||||
export default class FilterBoxChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
controlPanel,
|
||||
metadata,
|
||||
transformProps,
|
||||
loadChart: () => import('./FilterBox'),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/**
|
||||
* 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/core';
|
||||
import { sections } from '@superset-ui/chart-controls';
|
||||
|
||||
export default {
|
||||
controlPanelSections: [
|
||||
sections.legacyTimeseriesTime,
|
||||
{
|
||||
label: t('Filters configuration'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'filter_configs',
|
||||
config: {
|
||||
type: 'CollectionControl',
|
||||
label: t('Filters'),
|
||||
description: t('Filter configuration for the filter box'),
|
||||
validators: [],
|
||||
controlName: 'FilterBoxItemControl',
|
||||
mapStateToProps: ({ datasource }) => ({ datasource }),
|
||||
},
|
||||
},
|
||||
],
|
||||
[<hr />],
|
||||
[
|
||||
{
|
||||
name: 'date_filter',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Date filter'),
|
||||
default: true,
|
||||
description: t('Whether to include a time filter'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'instant_filtering',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Instant filtering'),
|
||||
renderTrigger: true,
|
||||
default: false,
|
||||
description: t(
|
||||
'Check to apply filters instantly as they change instead of displaying [Apply] button',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_sqla_time_granularity',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show time grain dropdown'),
|
||||
default: false,
|
||||
description: t('Check to include time grain dropdown'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_sqla_time_column',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show time column'),
|
||||
default: false,
|
||||
description: t('Check to include time column dropdown'),
|
||||
},
|
||||
},
|
||||
],
|
||||
['adhoc_filters'],
|
||||
],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
adhoc_filters: {
|
||||
label: t('Limit selector values'),
|
||||
description: t(
|
||||
'These filters apply to the values available in the dropdowns',
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* 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 { FilterBoxChartProps } from './types';
|
||||
|
||||
const NOOP = () => {};
|
||||
|
||||
export default function transformProps(chartProps: FilterBoxChartProps) {
|
||||
const {
|
||||
datasource,
|
||||
formData,
|
||||
hooks,
|
||||
initialValues,
|
||||
queriesData,
|
||||
rawDatasource = {},
|
||||
rawFormData,
|
||||
width,
|
||||
height,
|
||||
} = chartProps;
|
||||
const {
|
||||
onAddFilter = NOOP,
|
||||
onFilterMenuOpen = NOOP,
|
||||
onFilterMenuClose = NOOP,
|
||||
} = hooks;
|
||||
const {
|
||||
sliceId,
|
||||
dateFilter,
|
||||
instantFiltering,
|
||||
showSqlaTimeColumn,
|
||||
showSqlaTimeGranularity,
|
||||
} = formData;
|
||||
const { verboseMap = {} } = datasource;
|
||||
const filterConfigs = formData.filterConfigs || [];
|
||||
|
||||
const filtersFields = filterConfigs.map(flt => ({
|
||||
...flt,
|
||||
key: flt.column,
|
||||
label: flt.label || verboseMap[flt.column] || flt.column,
|
||||
}));
|
||||
|
||||
return {
|
||||
chartId: sliceId,
|
||||
width,
|
||||
height,
|
||||
datasource: rawDatasource,
|
||||
filtersChoices: queriesData[0].data,
|
||||
filtersFields,
|
||||
instantFiltering,
|
||||
onChange: onAddFilter,
|
||||
onFilterMenuOpen,
|
||||
onFilterMenuClose,
|
||||
origSelectedValues: initialValues || {},
|
||||
showDateFilter: dateFilter,
|
||||
showSqlaTimeColumn,
|
||||
showSqlaTimeGrain: showSqlaTimeGranularity,
|
||||
// the original form data, needed for async select options
|
||||
rawFormData,
|
||||
};
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 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, Datasource } from '@superset-ui/core';
|
||||
|
||||
export interface FilterConfig {
|
||||
column: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export type FilterBoxChartProps = ChartProps & {
|
||||
datasource?: Datasource;
|
||||
formData: ChartProps['formData'] & { filterConfigs: FilterConfig[] };
|
||||
};
|
||||
@@ -76,7 +76,6 @@ import {
|
||||
} from 'src/filters/components';
|
||||
import { PivotTableChartPlugin as PivotTableChartPluginV2 } from '@superset-ui/plugin-chart-pivot-table';
|
||||
import { HandlebarsChartPlugin } from '@superset-ui/plugin-chart-handlebars';
|
||||
import FilterBoxChartPlugin from '../FilterBox/FilterBoxChartPlugin';
|
||||
import TimeTableChartPlugin from '../TimeTable';
|
||||
|
||||
export default class MainPreset extends Preset {
|
||||
@@ -98,7 +97,6 @@ export default class MainPreset extends Preset {
|
||||
new CountryMapChartPlugin().configure({ key: 'country_map' }),
|
||||
new DistBarChartPlugin().configure({ key: 'dist_bar' }),
|
||||
new EventFlowChartPlugin().configure({ key: 'event_flow' }),
|
||||
new FilterBoxChartPlugin().configure({ key: 'filter_box' }),
|
||||
new EchartsFunnelChartPlugin().configure({ key: 'funnel' }),
|
||||
new EchartsTreemapChartPlugin().configure({ key: 'treemap_v2' }),
|
||||
new EchartsGaugeChartPlugin().configure({ key: 'gauge_chart' }),
|
||||
|
||||
Reference in New Issue
Block a user