Skip to content

Commit

Permalink
[Feat] add supportedFilterTypes to dataset (#1594)
Browse files Browse the repository at this point in the history
* supportedFilterTypes to addDataToMap dataset param

Signed-off-by: Ilya Boyandin <ilyabo@gmail.com>
  • Loading branch information
heshan0131 committed Aug 31, 2021
1 parent 41b364a commit 7827456
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 29 deletions.
1 change: 1 addition & 0 deletions src/actions/actions.d.ts
Expand Up @@ -48,6 +48,7 @@ export type ProtoDataset = {

// table-injected metadata
metadata?: any;
supportedFilterTypes?: string[];
};

export type AddDataToMapOptions = {
Expand Down
Expand Up @@ -24,6 +24,7 @@ import FilterPanelHeaderFactory from 'components/side-panel/filter-panel/filter-
import PanelHeaderActionFactory from 'components/side-panel/panel-header-action';
import SourceDataSelectorFactory from 'components/side-panel/common/source-data-selector';
import FieldSelectorFactory from '../../common/field-selector';
import {getSupportedFilterFields} from './new-filter-panel';

FieldPanelWithFieldSelectFactory.deps = [
FilterPanelHeaderFactory,
Expand Down Expand Up @@ -65,18 +66,23 @@ function FieldPanelWithFieldSelectFactory(
[filter.name]
);

const dataset = datasets[filter.dataId[0]];
const supportedFields = useMemo(
() => getSupportedFilterFields(dataset.supportedFilterTypes, allAvailableFields),
[dataset.supportedFilterTypes, allAvailableFields]
);
return (
<>
<FilterPanelHeader
datasets={[datasets[filter.dataId[0]]]}
allAvailableFields={allAvailableFields}
datasets={[dataset]}
allAvailableFields={supportedFields}
idx={idx}
filter={filter}
removeFilter={removeFilter}
>
<FieldSelector
inputTheme="secondary"
fields={allAvailableFields}
fields={supportedFields}
value={fieldValue}
erasable={false}
onSelect={onFieldSelector}
Expand Down
20 changes: 16 additions & 4 deletions src/components/filters/filter-panels/new-filter-panel.js
Expand Up @@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import React, {useCallback} from 'react';
import React, {useCallback, useMemo} from 'react';
import {StyledFilterContent} from 'components/common/styled-components';
import FilterPanelHeaderFactory from 'components/side-panel/filter-panel/filter-panel-header';
import SourceDataSelectorFactory from 'components/side-panel/common/source-data-selector';
Expand All @@ -30,6 +30,12 @@ NewFilterPanelFactory.deps = [
FieldSelectorFactory
];

export function getSupportedFilterFields(supportedFilterTypes, fields) {
return supportedFilterTypes
? fields.filter(field => supportedFilterTypes.includes(field.type))
: fields;
}

function NewFilterPanelFactory(FilterPanelHeader, SourceDataSelector, FieldSelector) {
/** @type {import('./filter-panel-types').FilterPanelComponent} */
const NewFilterPanel = React.memo(
Expand All @@ -44,11 +50,17 @@ function NewFilterPanelFactory(FilterPanelHeader, SourceDataSelector, FieldSelec
setFilter
]);

const dataset = datasets[filter.dataId[0]];
const supportedFields = useMemo(
() => getSupportedFilterFields(dataset.supportedFilterTypes, allAvailableFields),
[dataset.supportedFilterTypes, allAvailableFields]
);

return (
<>
<FilterPanelHeader
datasets={[datasets[filter.dataId[0]]]}
allAvailableFields={allAvailableFields}
datasets={[dataset]}
allAvailableFields={supportedFields}
idx={idx}
filter={filter}
removeFilter={removeFilter}
Expand All @@ -57,7 +69,7 @@ function NewFilterPanelFactory(FilterPanelHeader, SourceDataSelector, FieldSelec
>
<FieldSelector
inputTheme="secondary"
fields={allAvailableFields}
fields={supportedFields}
value={Array.isArray(filter.name) ? filter.name[0] : filter.name}
erasable={false}
onSelect={onFieldSelector}
Expand Down
3 changes: 0 additions & 3 deletions src/components/side-panel/filter-panel/filter-panel.js
Expand Up @@ -25,7 +25,6 @@ import styled from 'styled-components';
import get from 'lodash.get';
import {ALL_FIELD_TYPES, FILTER_TYPES} from 'constants/default-settings';

import FilterPanelHeaderFactory from 'components/side-panel/filter-panel/filter-panel-header';
import NewFilterPanelFactory from 'components/filters/filter-panels/new-filter-panel';
import TimeRangeFilterPanelFactory from 'components/filters/filter-panels/time-range-filter-panel';
import SingleSelectFilterPanelFactory from 'components/filters/filter-panels/single-select-filter-panel';
Expand All @@ -39,7 +38,6 @@ const StyledFilterPanel = styled.div`
`;

FilterPanelFactory.deps = [
FilterPanelHeaderFactory,
NewFilterPanelFactory,
TimeRangeFilterPanelFactory,
SingleSelectFilterPanelFactory,
Expand All @@ -49,7 +47,6 @@ FilterPanelFactory.deps = [
];

function FilterPanelFactory(
FilterPanelHeader,
NewFilterPanel,
TimeRangeFilterPanel,
SingleSelectFilterPanel,
Expand Down
4 changes: 2 additions & 2 deletions src/reducers/vis-state-updaters.js
Expand Up @@ -1322,9 +1322,9 @@ export const updateVisDataUpdater = (state, action) => {
const datasets = toArray(action.datasets);

const newDataEntries = datasets.reduce(
(accu, {info = {}, data, metadata} = {}) => ({
(accu, {info = {}, ...rest} = {}) => ({
...accu,
...(createNewDataEntry({info, data, metadata}, state.datasets) || {})
...(createNewDataEntry({info, ...rest}, state.datasets) || {})
}),
{}
);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/dataset-utils.js
Expand Up @@ -76,7 +76,7 @@ export function getNewDatasetColor(datasets) {
* Take datasets payload from addDataToMap, create datasets entry save to visState
* @type {typeof import('./dataset-utils').createNewDataEntry}
*/
export function createNewDataEntry({info, data, metadata}, datasets = {}) {
export function createNewDataEntry({info, data, ...opts}, datasets = {}) {
const validatedData = validateInputData(data);
if (!validatedData) {
return {};
Expand All @@ -85,7 +85,7 @@ export function createNewDataEntry({info, data, metadata}, datasets = {}) {
info = info || {};
const color = info.color || getNewDatasetColor(datasets);

const keplerTable = new KeplerTable({info, data: validatedData, color, metadata});
const keplerTable = new KeplerTable({info, data: validatedData, color, ...opts});
return {
[keplerTable.id]: keplerTable
};
Expand Down
2 changes: 2 additions & 0 deletions src/utils/filter-utils.d.ts
Expand Up @@ -133,6 +133,8 @@ export function getHistogram(

export function getNumericStepSize(diff: number): number;

export function formatNumberByStep(val: number, step: number, bound: 'floor' | 'ceil'): number;

export function mergeFilterDomainStep(filter: Filter, filterProps?: any): Filter | null;

export function getFilterProps(
Expand Down
2 changes: 1 addition & 1 deletion src/utils/gpu-filter-utils.d.ts
Expand Up @@ -23,6 +23,6 @@ import {Filter, Field, GpuFilter} from 'reducers/vis-state-updaters';
export function setFilterGpuMode(filter: Filter, filters: Filter[]): Filter;
export function assignGpuChannel(filter: Filter, filters: Filter[]): Filter;
export function assignGpuChannels(allFilters: Filter[]): Filter[];
export function resetFilterGpuMode( filters: Filter[]): Filter[];
export function resetFilterGpuMode(filters: Filter[]): Filter[];
export function getGpuFilterProps(filters: Filter[], dataId: string, fields: Field[]): GpuFilter;
export function getDatasetFieldIndexForFilter(dataId: string, filter: Filter): number;
4 changes: 3 additions & 1 deletion src/utils/index.js
Expand Up @@ -36,7 +36,9 @@ export {
applyFiltersToDatasets,
validateFilterWithData,
validateFiltersUpdateDatasets,
getIntervalBins
getIntervalBins,
getNumericStepSize,
formatNumberByStep
} from 'utils/filter-utils';
export {resetFilterGpuMode, assignGpuChannels} from 'utils/gpu-filter-utils';

Expand Down
23 changes: 13 additions & 10 deletions src/utils/table-utils/kepler-table.d.ts
Expand Up @@ -25,14 +25,8 @@ export type GpuFilter = {
dc: DataContainerInterface
) => (
getIndex?: (any) => number,
getData?: (
dc: DataContainerInterface,
d: any,
fieldIndex: number
) => any
) => (
d: any
) => number;
getData?: (dc: DataContainerInterface, d: any, fieldIndex: number) => any
) => (d: any) => number;
};

export type FieldPair = {
Expand Down Expand Up @@ -65,8 +59,16 @@ export function sortDatasetByColumn(dataset: Dataset, column: string, mode?: str
export function findPointFieldPairs(fields: Field[]): FieldPair[];

export class KeplerTable {
constructor(schema: {info?: object; data: any; color: RGBColor; metadata: any});
id: string;
readonly id: string;

constructor(schema: {
info?: ProtoDataset['info'];
data: ProtoDataset['data'];
color: RGBColor;
metadata?: ProtoDataset['metadata'];
supportedFilterTypes?: ProtoDataset['supportedFilterTypes'];
});
type?: string;
label?: string;
color: RGBColor;

Expand All @@ -93,6 +95,7 @@ export class KeplerTable {
sortOrder?: number[] | null;

pinnedColumns?: string[];
supportedFilterTypes?: string[];
// table-injected metadata
metadata?: object;

Expand Down
5 changes: 4 additions & 1 deletion src/utils/table-utils/kepler-table.js
Expand Up @@ -55,7 +55,7 @@ const FID_KEY = 'name';
* @type {KeplerTableClass}
*/
class KeplerTable {
constructor({info = {}, data, color, metadata}) {
constructor({info = {}, data, color, metadata, supportedFilterTypes}) {
// TODO - what to do if validation fails? Can kepler handle exceptions?
// const validatedData = validateInputData(data);
// if (!validatedData) {
Expand Down Expand Up @@ -104,6 +104,9 @@ class KeplerTable {
this.fieldPairs = findPointFieldPairs(fields);
this.fields = fields;
this.gpuFilter = getGpuFilterProps([], dataId, fields);
if (supportedFilterTypes) {
this.supportedFilterTypes = supportedFilterTypes;
}
}

/**
Expand Down
90 changes: 88 additions & 2 deletions test/browser/components/side-panel/filter-manager-test.js
Expand Up @@ -25,19 +25,31 @@ import {
FilterManagerFactory,
SourceDataCatalogFactory,
FilterPanelFactory,
Button
FieldSelectorFactory,
Button,
FilterPanelHeaderFactory
} from 'components';

import NewFilterPanelFactory from 'components/side-panel/filter-panel/filter-panel';

import {appInjector} from 'components/container';
import {mountWithTheme, IntlWrapper} from 'test/helpers/component-utils';
import keplerGlReducer from 'reducers/core';
import * as VisStateActions from 'actions/vis-state-actions';
import {testFields, testAllData} from 'test/fixtures/test-csv-data';
import {ALL_FIELD_TYPES} from 'constants/default-settings';
import {assertDatasetIsTable} from '../../../helpers/comparison-utils';

// components
const FilterManager = appInjector.get(FilterManagerFactory);
const SourceDataCatalog = appInjector.get(SourceDataCatalogFactory);
const FilterPanel = appInjector.get(FilterPanelFactory);
const NewFilterPanel = appInjector.get(NewFilterPanelFactory);
const FilterPanelHeader = appInjector.get(FilterPanelHeaderFactory);
const FieldSelector = appInjector.get(FieldSelectorFactory);

// mock state
import {StateWFilters} from 'test/helpers/mock-state';
import {StateWFilters, InitialState, applyActions, csvInfo} from 'test/helpers/mock-state';

const nop = () => {};
// default props from initial state
Expand Down Expand Up @@ -142,3 +154,77 @@ test('Components -> FilterManager.mount -> with prop', t => {

t.end();
});

test('Components -> FilterManager.mount -> with supportedFilterTypes', t => {
// load csv and geojson
const updatedState = applyActions(keplerGlReducer, InitialState, [
{
action: VisStateActions.updateVisData,
payload: [
[
{
info: csvInfo,
data: {fields: testFields, rows: testAllData},
supportedFilterTypes: [ALL_FIELD_TYPES.real, ALL_FIELD_TYPES.integer]
}
]
]
}
]);

const {visState} = updatedState;
// test dataset is table
assertDatasetIsTable(t, visState.datasets[csvInfo.id]);
t.deepEqual(
visState.datasets[csvInfo.id].supportedFilterTypes,
[ALL_FIELD_TYPES.real, ALL_FIELD_TYPES.integer],
'supportedFilterTypes should be correct'
);

// call addFilter
const stateWithEmptyFilter = keplerGlReducer(updatedState, VisStateActions.addFilter(csvInfo.id));
// mount filter panel
// components
const props = {
filters: stateWithEmptyFilter.visState.filters,
datasets: stateWithEmptyFilter.visState.datasets,
layers: stateWithEmptyFilter.visState.layers,
showDatasetTable: nop,
visStateActions: {
addFilter: nop,
setFilter: nop,
removeFilter: nop,
enlargeFilter: nop,
toggleAnimation: nop,
toggleFilterFeature: nop
}
};

let wrapper;
t.doesNotThrow(() => {
wrapper = mountWithTheme(
<IntlWrapper>
<FilterManager {...props} />
</IntlWrapper>
);
}, 'FilterManager should not fail when mount with dataset.supportedFilterTypes');

t.ok(wrapper.find(FilterPanel).length === 1, 'should render 1 FilterPanel');
t.ok(wrapper.find(NewFilterPanel).length === 1, 'should render 1 NewFilterPanel');
t.ok(wrapper.find(FilterPanelHeader).length === 1, 'should render 1 FilterPanelHeader');
t.ok(wrapper.find(FieldSelector).length === 1, 'should render FieldSelector');

// check field options
const fieldOptions = wrapper
.find(FieldSelector)
.at(0)
.props().fields;

t.deepEqual(
fieldOptions.map(f => f.name),
['gps_data.lat', 'gps_data.lng', 'id'],
'should only pass real / integer fields'
);

t.end();
});
4 changes: 4 additions & 0 deletions test/helpers/comparison-utils.js
Expand Up @@ -20,6 +20,7 @@

import {FILTER_TYPES} from 'constants/default-settings';
import {toArray} from '../../src/utils/utils';
import KeplerTable from '../../src/utils/table-utils/kepler-table';

export function cmpObjectKeys(t, expectedObj, actualObj, name) {
t.deepEqual(
Expand Down Expand Up @@ -222,6 +223,9 @@ export function cmpDatasets(t, expectedDatasets, actualDatasets) {
cmpDataset(t, expectedDatasets[dataId], actualDatasets[dataId]);
});
}
export function assertDatasetIsTable(t, dataset) {
t.ok(dataset instanceof KeplerTable, `${dataset.label || 'dataset'} should be a KeplerTable`);
}

export function cmpDataset(t, expectedDataset, actualDataset, opt = {}) {
cmpObjectKeys(t, expectedDataset, actualDataset, `dataset:${expectedDataset.id}`);
Expand Down

0 comments on commit 7827456

Please sign in to comment.