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

[Feat] add supportedFilterTypes to dataset #1594

Merged
merged 3 commits into from
Aug 31, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/actions/actions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export type ProtoDataset = {

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

export type AddDataToMapOptions = {
Expand Down
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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