Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4ab10f3
feat: log scale and step size control for range filters
stevetracvc Jan 12, 2022
0bd7607
fix: prettier
stevetracvc Jan 12, 2022
308c410
fix: dupe min/max props
stevetracvc Jan 12, 2022
987e05c
fix: boolean math issue
stevetracvc Jan 12, 2022
4432e9f
fix: doubled up in controlPanel
stevetracvc Jan 12, 2022
70f6417
merged in master 1.4.1ish
stevetracvc Mar 24, 2022
9a117da
fixes to default/reset value and options for scaling
stevetracvc Mar 24, 2022
c1f9248
fix: prettier stuff
stevetracvc Mar 24, 2022
fe0334f
fix: more prettier
stevetracvc Mar 24, 2022
b284f51
fix: once more
stevetracvc Mar 24, 2022
9d4f5a0
fix: test needed scaling value
stevetracvc Mar 24, 2022
e81d710
Fixed issue with reset filters and added cube/quad roots
stevetracvc Mar 24, 2022
7c4aa69
added tests for all scaling functions
stevetracvc Mar 24, 2022
a2cd0f0
fix: prettier
stevetracvc Mar 24, 2022
209403d
fix: foreach issue
stevetracvc Mar 24, 2022
9ddd258
fix: cleanup old code
stevetracvc Mar 24, 2022
7af88c4
fix: issue with default scaling function
stevetracvc Mar 25, 2022
361ecc8
fix: cleaned up FiltersConfigModal a bit
stevetracvc Apr 2, 2022
f05ebc9
fix: clarified step size is increments of transformed scale
stevetracvc Apr 2, 2022
2b64973
fix: issue with using range filter as a dashboard filter
stevetracvc Apr 2, 2022
a6ab310
feat: added validators to FiltersConfigForm loaded from filter's cont…
stevetracvc Apr 4, 2022
495a2ef
fix: display precision now linked to stepSize
stevetracvc Apr 4, 2022
089df91
fix: remove direct dependence on rc-field-form
stevetracvc Apr 4, 2022
fef9b99
fix: max value now pushed to an integer increment of stepSize
stevetracvc Apr 6, 2022
b57ba75
fix: getControlItemsMap conditional on CheckboxControl
stevetracvc Apr 7, 2022
4997af6
fix: value type for SelectControl onChange
stevetracvc Apr 7, 2022
6369dcc
fix: prev commit requires config type to explicitly be CheckboxControl
stevetracvc Apr 7, 2022
f9f556b
added a couple tests for the new SelectControl in getControlItemsMap
stevetracvc Apr 7, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const formMock: FormInstance = {
const filterMock: Filter = {
cascadeParentIds: [],
defaultDataMask: {},
id: 'mock',
id: 'filterMockId',
name: 'mock',
scope: {
rootPath: [],
Expand All @@ -69,7 +69,7 @@ const createProps: () => ControlItemsProps = () => ({
disabled: false,
forceUpdate: jest.fn(),
form: formMock,
filterId: 'filterId',
filterId: 'filterMockId',
filterToEdit: filterMock,
filterType: 'filterType',
});
Expand All @@ -78,8 +78,25 @@ const createControlItems = () => [
null,
false,
{},
{ name: 'name_1', config: { renderTrigger: true, resetConfig: true } },
{
name: 'name_1',
config: { type: 'CheckboxControl', renderTrigger: true, resetConfig: true },
},
{ name: 'groupby', config: { multiple: true, required: false } },
{
name: 'select_1',
config: {
type: 'SelectControl',
renderTrigger: true,
freeForm: true,
default: 1,
choices: [
[0, 'choice_0'],
[1, 'choice_1'],
[2, 'choice_2'],
],
},
},
];

beforeEach(() => {
Expand Down Expand Up @@ -146,7 +163,10 @@ test('Should render render ControlItems', () => {

const controlItems = [
...createControlItems(),
{ name: 'name_2', config: { renderTrigger: true } },
{
name: 'name_2',
config: { type: 'CheckboxControl', renderTrigger: true },
},
];
(getControlItems as jest.Mock).mockReturnValue(controlItems);
const controlItemsMap = getControlItemsMap(props);
Expand All @@ -169,7 +189,14 @@ test('Clickin on checkbox', () => {
test('Clickin on checkbox when resetConfig:flase', () => {
const props = createProps();
(getControlItems as jest.Mock).mockReturnValue([
{ name: 'name_1', config: { renderTrigger: true, resetConfig: false } },
{
name: 'name_1',
config: {
type: 'CheckboxControl',
renderTrigger: true,
resetConfig: false,
},
},
]);
const controlItemsMap = getControlItemsMap(props);
renderControlItems(controlItemsMap);
Expand All @@ -179,3 +206,26 @@ test('Clickin on checkbox when resetConfig:flase', () => {
expect(props.forceUpdate).toBeCalled();
expect(setNativeFilterFieldValues).not.toBeCalled();
});

test('Should render SelectControl ControlItems', () => {
const props = createProps();
const controlItems = [...createControlItems()];
(getControlItems as jest.Mock).mockReturnValue(controlItems);
const controlItemsMap = getControlItemsMap(props);
renderControlItems(controlItemsMap);
expect(screen.getByRole('combobox')).toBeInTheDocument();
});

test('Should change selection for SelectControl', () => {
const props = createProps();
const controlItems = [...createControlItems()];
(getControlItems as jest.Mock).mockReturnValue(controlItems);
const controlItemsMap = getControlItemsMap(props);
renderControlItems(controlItemsMap);
userEvent.click(screen.getByRole('combobox'));
userEvent.click(screen.getByTitle('choice_2'));
expect(setNativeFilterFieldValues).toBeCalledWith(formMock, filterMock.id, {
select_1: 2,
defaultDataMask: null,
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import {
CustomControlItem,
InfoTooltipWithTrigger,
ControlValueValidator,
} from '@superset-ui/chart-controls';
import React from 'react';
import { AntdCheckbox, FormInstance } from 'src/components';
Expand All @@ -30,6 +31,8 @@ import {
} from '@superset-ui/core';
import { Tooltip } from 'src/components/Tooltip';
import { FormItem } from 'src/components/Form';
import SelectControl from 'src/explore/components/controls/SelectControl';
import { RuleObject } from 'antd/lib/form';
import {
doesColumnMatchFilterType,
getControlItems,
Expand Down Expand Up @@ -59,6 +62,25 @@ const CleanFormItem = styled(FormItem)`
margin-bottom: 0;
`;

// converts the controlPanel validators to an rc-field-form rule
const getControlItemRules = (
validators: ControlValueValidator[] | undefined,
) => {
if (validators) {
const v = validators.map(validator => ({
validator: (_rule: RuleObject, value: unknown) => {
const errorMessage = validator(value);
if (errorMessage !== false && errorMessage !== true) {
return Promise.reject(new Error(errorMessage));
}
return Promise.resolve();
},
}));
return v;
}
return undefined;
};

export default function getControlItemsMap({
datasetId,
disabled,
Expand Down Expand Up @@ -149,6 +171,7 @@ export default function getControlItemsMap({
(controlItem: CustomControlItem) =>
controlItem?.config?.renderTrigger &&
controlItem.name !== 'sortAscending' &&
controlItem?.config?.type === 'CheckboxControl' &&
controlItem.name !== 'enableSingleValue',
)
.forEach(controlItem => {
Expand Down Expand Up @@ -214,6 +237,68 @@ export default function getControlItemsMap({
);
mapControlItems[controlItem.name] = { element, checked: initialValue };
});
controlItems
.filter(
(controlItem: CustomControlItem) =>
controlItem?.config?.renderTrigger &&
controlItem?.config?.type === 'SelectControl',
)
.forEach(controlItem => {
const initialValue =
filterToEdit?.controlValues?.[controlItem.name] ??
controlItem?.config?.default;
const element = (
<>
<CleanFormItem
name={['filters', filterId, 'requiredFirst', controlItem.name]}
hidden
initialValue={
controlItem?.config?.requiredFirst && filterToEdit?.requiredFirst
}
/>
<Tooltip
key={controlItem.name}
placement="left"
title={
controlItem.config.affectsDataMask &&
disabled &&
t('Populate "Default value" to enable this control')
}
>
<StyledFormItem
key={controlItem.name}
name={['filters', filterId, 'controlValues', controlItem.name]}
initialValue={initialValue}
valuePropName="option"
colon={false}
label={
<StyledLabel>
{t(`${controlItem.config?.label}`) || t('Select')}
</StyledLabel>
}
rules={getControlItemRules(controlItem.config.validators)}
>
<SelectControl
name={controlItem.name}
clearable={false}
freeForm={controlItem.config.freeForm}
disabled={controlItem.config.affectsDataMask && disabled}
onChange={(value: string | number) => {
setNativeFilterFieldValues(form, filterId, {
[controlItem.name]: value,
defaultDataMask: null,
});
forceUpdate();
}}
value={controlItem.config.value || initialValue}
choices={controlItem.config.choices}
/>
</StyledFormItem>
</Tooltip>
</>
);
mapControlItems[controlItem.name] = { element, checked: false };
});
return {
controlItems: mapControlItems,
mainControlItems: mapMainControlItems,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ import { render } from 'spec/helpers/testing-library';
import RangeFilterPlugin from './RangeFilterPlugin';
import { SingleValueType } from './SingleValueType';
import transformProps from './transformProps';
import {
PluginFilterRangeScalingFunctions,
SCALING_FUNCTION_ENUM_TO_SCALING_FUNCTION,
} from './types';
import { roundDecimals } from '../../utils';

const rangeProps = {
formData: {
Expand Down Expand Up @@ -62,6 +67,7 @@ const rangeProps = {
urlParams: {},
vizType: 'filter_range',
inputRef: { current: null },
scaling: PluginFilterRangeScalingFunctions.LINEAR,
},
height: 20,
hooks: {},
Expand All @@ -82,16 +88,36 @@ const rangeProps = {
appSection: AppSection.DASHBOARD,
};

describe('ScalingFunctions', () => {
Object.keys(PluginFilterRangeScalingFunctions).forEach(scaling => {
[0, 1e-5, 1e-2, 1, 1e4, 1e6, 1e8].forEach(val =>
it(`inverse should undo transform (using ${scaling}, ${val})`, () =>
expect(
// make sure it's good to 6 decimal places
roundDecimals(
SCALING_FUNCTION_ENUM_TO_SCALING_FUNCTION[scaling].inverseScale(
SCALING_FUNCTION_ENUM_TO_SCALING_FUNCTION[scaling].transformScale(
val,
),
),
6,
),
).toEqual(val)),
);
});
});

describe('RangeFilterPlugin', () => {
const setDataMask = jest.fn();
const getWrapper = (props = {}) =>
const getWrapper = (props = {}, filterState = {}) =>
render(
// @ts-ignore
<RangeFilterPlugin
// @ts-ignore
{...transformProps({
...rangeProps,
formData: { ...rangeProps.formData, ...props },
filterState,
})}
setDataMask={setDataMask}
/>,
Expand All @@ -101,22 +127,43 @@ describe('RangeFilterPlugin', () => {
jest.clearAllMocks();
});

it('should call setDataMask with correct filter', () => {
getWrapper();
expect(setDataMask).toHaveBeenCalledWith({
extraFormData: {
filters: [
{
col: 'SP_POP_TOTL',
op: '<=',
val: 70,
},
],
},
filterState: {
label: 'x ≤ 70',
value: [10, 70],
},
Object.keys(PluginFilterRangeScalingFunctions).forEach(scaling => {
it(`should call setDataMask with correct filter (using ${scaling})`, () => {
getWrapper(
{ scaling },
{
value: [
SCALING_FUNCTION_ENUM_TO_SCALING_FUNCTION[scaling].transformScale(
10,
),
SCALING_FUNCTION_ENUM_TO_SCALING_FUNCTION[scaling].transformScale(
70,
),
],
},
);
expect(setDataMask).toHaveBeenCalledWith({
extraFormData: {
filters: [
{
col: 'SP_POP_TOTL',
op: '<=',
val: 70,
},
],
},
filterState: {
label: 'x ≤ 70',
value: [
SCALING_FUNCTION_ENUM_TO_SCALING_FUNCTION[scaling].transformScale(
10,
),
SCALING_FUNCTION_ENUM_TO_SCALING_FUNCTION[scaling].transformScale(
70,
),
],
},
});
});
});

Expand Down
Loading