Skip to content

Commit 595bd90

Browse files
bdjulbicCopilot
andauthored
feat: add override for vis config (#3379)
* add override for vis config Signed-off-by: bdjulbic <bdjulbic@foursquare.com> * address PR comments Signed-off-by: bdjulbic <bdjulbic@foursquare.com> * Update src/reducers/src/layer-utils.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: bdjulbic <bdjulbic@foursquare.com> * address PR comments Signed-off-by: bdjulbic <bdjulbic@foursquare.com> --------- Signed-off-by: bdjulbic <bdjulbic@foursquare.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 992f501 commit 595bd90

4 files changed

Lines changed: 155 additions & 1 deletion

File tree

src/reducers/src/layer-utils.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {Layer as DeckLayer} from '@deck.gl/core';
88
type DeckLayerProps = any;
99
import {
1010
Field,
11+
LayerVisConfig,
1112
TooltipField,
1213
CompareType,
1314
SplitMapLayers,
@@ -101,6 +102,55 @@ export function findDefaultLayer(dataset: KeplerTable, layerClasses: LayerClasse
101102
});
102103
}
103104

105+
/**
106+
* Applies `patch` to `config.visConfig` only for layers whose `dataId` is in `datasetIdsToPatch`.
107+
* Layers for datasets that were already on the map are left unchanged — `addDataToMap`
108+
* Keys whose patch value is `undefined` are skipped so existing visConfig is not cleared.
109+
*/
110+
export function mergeLayerVisConfigForNewDatasets(
111+
state: VisState,
112+
patch: Partial<LayerVisConfig> | undefined,
113+
datasetIdsToPatch: string[]
114+
): VisState {
115+
if (!patch || !Object.keys(patch).length) {
116+
return state;
117+
}
118+
119+
const hasDefinedPatchValue = (Object.keys(patch) as Array<keyof LayerVisConfig>).some(
120+
key => patch[key] !== undefined
121+
);
122+
if (!hasDefinedPatchValue) {
123+
return state;
124+
}
125+
126+
const datasetIdSet = new Set(datasetIdsToPatch);
127+
128+
return {
129+
...state,
130+
layers: state.layers.map(layer => {
131+
if (!layer.config.dataId || !datasetIdSet.has(layer.config.dataId)) {
132+
return layer;
133+
}
134+
const settings = layer.visConfigSettings;
135+
if (!settings) {
136+
return layer;
137+
}
138+
const allowedKeys = (Object.keys(patch) as Array<keyof LayerVisConfig>).filter(
139+
key =>
140+
Object.prototype.hasOwnProperty.call(settings, key) && patch[key] !== undefined
141+
);
142+
if (!allowedKeys.length) {
143+
return layer;
144+
}
145+
const partial = allowedKeys.reduce<Partial<LayerVisConfig>>((acc, key) => {
146+
acc[key] = patch[key];
147+
return acc;
148+
}, {});
149+
return layer.updateLayerVisConfig(partial);
150+
})
151+
};
152+
}
153+
104154
type MinVisStateForLayerData = {
105155
datasets: VisState['datasets'];
106156
animationConfig: VisState['animationConfig'];

src/reducers/src/vis-state-updaters.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ import {
129129
FilterAnimationConfig,
130130
Editor,
131131
Field,
132+
LayerVisConfig,
132133
TimeRangeFilter
133134
} from '@kepler.gl/types';
134135
import {Loader} from '@loaders.gl/loader-utils';
@@ -143,7 +144,12 @@ import {
143144
sortDatasetByColumn
144145
} from '@kepler.gl/table';
145146
import {findFieldsToShow} from './interaction-utils';
146-
import {calculateLayerData, findDefaultLayer, getLayerOrderFromLayers} from './layer-utils';
147+
import {
148+
calculateLayerData,
149+
findDefaultLayer,
150+
getLayerOrderFromLayers,
151+
mergeLayerVisConfigForNewDatasets
152+
} from './layer-utils';
147153
import {getPropValueToMerger, hasPropsToMerge} from './merger-handler';
148154
import {mergeDatasetsByOrder} from './vis-state-merger';
149155
import {
@@ -2508,6 +2514,20 @@ export function applyMergersUpdater(
25082514
: mergeStateResult.mergedState;
25092515
}
25102516

2517+
/** Reads `options.layerVisConfig` from add-data-to-map (see `AddDataToMapOptions`). */
2518+
function layerVisConfigFromAddDataOptions(
2519+
options: PostMergerPayload['options'] | undefined
2520+
): Partial<LayerVisConfig> | undefined {
2521+
if (!options || !('layerVisConfig' in options)) {
2522+
return undefined;
2523+
}
2524+
const patch = (options as {layerVisConfig?: unknown}).layerVisConfig;
2525+
if (patch == null || typeof patch !== 'object' || Array.isArray(patch)) {
2526+
return undefined;
2527+
}
2528+
return patch as Partial<LayerVisConfig>;
2529+
}
2530+
25112531
/**
25122532
* Add new dataset to `visState`, with option to load a map config along with the datasets
25132533
*/
@@ -2551,6 +2571,12 @@ function postMergeUpdater(mergedState: VisState, postMergerPayload: PostMergerPa
25512571
};
25522572
}
25532573

2574+
mergedState = mergeLayerVisConfigForNewDatasets(
2575+
mergedState,
2576+
layerVisConfigFromAddDataOptions(options),
2577+
newDataIds
2578+
);
2579+
25542580
// if no tooltips merged add default tooltips
25552581
newDataIds.forEach(dataId => {
25562582
const tooltipFields = mergedState.interactionConfig.tooltip.config.fieldsToShow[dataId];

src/types/actions.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright contributors to the kepler.gl project
33

44
import {SavedMap, ParsedConfig, SavedConfigV1, MinSavedConfigV1} from './schemas';
5+
import type {LayerVisConfig} from './layers';
56

67
/** EXPORT_FILE_TO_CLOUD */
78
export type MapData = {
@@ -69,6 +70,7 @@ export type AddDataToMapOptions = {
6970
keepExistingConfig?: boolean;
7071
autoCreateLayers?: boolean;
7172
autoCreateTooltips?: boolean;
73+
layerVisConfig?: Partial<LayerVisConfig>;
7274
};
7375

7476
export type AddDataToMapPayload = {

test/node/reducers/composer-state-test.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,82 @@ test('#composerStateReducer - addDataToMapUpdater: autoCreateLayers', t => {
399399
t.end();
400400
});
401401

402+
test('#composerStateReducer - addDataToMapUpdater: layerVisConfig only affects new datasets; unknown keys ignored', t => {
403+
const data = processCsvData(testCsvData);
404+
const state = keplerGlReducer({}, registerEntry({id: 'test'})).test;
405+
406+
let oldState = addDataToMapUpdater(state, {
407+
payload: {
408+
datasets: {
409+
data,
410+
info: {id: 'first-csv-dataset'}
411+
}
412+
}
413+
});
414+
oldState = {
415+
...oldState,
416+
visState: applyExistingDatasetTasks(visStateReducer, oldState.visState)
417+
};
418+
drainTasksForTesting();
419+
420+
const firstLayer = oldState.visState.layers.find(l => l.config.dataId === 'first-csv-dataset');
421+
t.ok(firstLayer, 'first dataset should have a layer');
422+
const allowHoverFirstBefore = firstLayer.config.visConfig.allowHover;
423+
424+
let nextState = addDataToMapUpdater(oldState, {
425+
payload: {
426+
datasets: {
427+
data,
428+
info: {id: 'second-csv-dataset'}
429+
},
430+
options: {
431+
keepExistingConfig: true,
432+
layerVisConfig: {
433+
allowHover: false,
434+
opacity: undefined,
435+
totallyUnknownVisKey999: 'strip-me'
436+
}
437+
}
438+
}
439+
});
440+
nextState = {
441+
...nextState,
442+
visState: applyExistingDatasetTasks(visStateReducer, nextState.visState)
443+
};
444+
drainTasksForTesting();
445+
446+
const layerFirst = nextState.visState.layers.find(l => l.config.dataId === 'first-csv-dataset');
447+
const layerSecond = nextState.visState.layers.find(l => l.config.dataId === 'second-csv-dataset');
448+
449+
t.ok(layerSecond, 'second dataset should have a layer');
450+
t.equal(
451+
layerFirst.config.visConfig.allowHover,
452+
allowHoverFirstBefore,
453+
'layerVisConfig must not change visConfig for layers tied to prior datasets'
454+
);
455+
t.equal(
456+
layerSecond.config.visConfig.allowHover,
457+
false,
458+
'layerVisConfig should merge allowHover onto layers for datasets added in this action'
459+
);
460+
t.equal(
461+
layerFirst.config.visConfig.totallyUnknownVisKey999,
462+
undefined,
463+
'unknown keys must not be applied to existing layers'
464+
);
465+
t.equal(
466+
layerSecond.config.visConfig.totallyUnknownVisKey999,
467+
undefined,
468+
'unknown keys must not be merged into visConfig when absent from visConfigSettings'
469+
);
470+
t.ok(
471+
typeof layerSecond.config.visConfig.opacity === 'number',
472+
'undefined patch values must not overwrite visConfig (e.g. opacity)'
473+
);
474+
475+
t.end();
476+
});
477+
402478
test('#composerStateReducer - replaceDataInMapUpdater', t => {
403479
const dataIdToReplace = 'dataset_to_replace';
404480
const datasets = {

0 commit comments

Comments
 (0)