Skip to content

Commit 8dbd8f7

Browse files
[Maps] Update style when metrics change (#83586)
1 parent 9fd020e commit 8dbd8f7

File tree

9 files changed

+489
-170
lines changed

9 files changed

+489
-170
lines changed

x-pack/plugins/maps/common/descriptor_types/style_property_descriptor_types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,8 @@ export type DynamicStylePropertyOptions =
254254
| LabelDynamicOptions
255255
| OrientationDynamicOptions
256256
| SizeDynamicOptions;
257+
258+
export type DynamicStyleProperties = {
259+
type: STYLE_TYPE.DYNAMIC;
260+
options: DynamicStylePropertyOptions;
261+
};

x-pack/plugins/maps/public/actions/layer_actions.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import { IVectorLayer } from '../classes/layers/vector_layer/vector_layer';
4545
import { LAYER_STYLE_TYPE, LAYER_TYPE } from '../../common/constants';
4646
import { IVectorStyle } from '../classes/styles/vector/vector_style';
4747
import { notifyLicensedFeatureUsage } from '../licensed_features';
48+
import { IESAggField } from '../classes/fields/agg';
49+
import { IField } from '../classes/fields/field';
4850

4951
export function trackCurrentLayerState(layerId: string) {
5052
return {
@@ -274,13 +276,37 @@ export function updateLayerOrder(newLayerOrder: number[]) {
274276
};
275277
}
276278

279+
function updateMetricsProp(layerId: string, value: unknown) {
280+
return async (
281+
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
282+
getState: () => MapStoreState
283+
) => {
284+
const layer = getLayerById(layerId, getState());
285+
const previousFields = await (layer as IVectorLayer).getFields();
286+
await dispatch({
287+
type: UPDATE_SOURCE_PROP,
288+
layerId,
289+
propName: 'metrics',
290+
value,
291+
});
292+
await dispatch(updateStyleProperties(layerId, previousFields as IESAggField[]));
293+
dispatch(syncDataForLayerId(layerId));
294+
};
295+
}
296+
277297
export function updateSourceProp(
278298
layerId: string,
279299
propName: string,
280300
value: unknown,
281301
newLayerType?: LAYER_TYPE
282302
) {
283303
return async (dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) => {
304+
if (propName === 'metrics') {
305+
if (newLayerType) {
306+
throw new Error('May not change layer-type when modifying metrics source-property');
307+
}
308+
return await dispatch(updateMetricsProp(layerId, value));
309+
}
284310
dispatch({
285311
type: UPDATE_SOURCE_PROP,
286312
layerId,
@@ -290,7 +316,6 @@ export function updateSourceProp(
290316
if (newLayerType) {
291317
dispatch(updateLayerType(layerId, newLayerType));
292318
}
293-
await dispatch(clearMissingStyleProperties(layerId));
294319
dispatch(syncDataForLayerId(layerId));
295320
};
296321
}
@@ -422,7 +447,7 @@ function removeLayerFromLayerList(layerId: string) {
422447
};
423448
}
424449

425-
export function clearMissingStyleProperties(layerId: string) {
450+
function updateStyleProperties(layerId: string, previousFields: IField[]) {
426451
return async (
427452
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
428453
getState: () => MapStoreState
@@ -441,8 +466,9 @@ export function clearMissingStyleProperties(layerId: string) {
441466
const {
442467
hasChanges,
443468
nextStyleDescriptor,
444-
} = await (style as IVectorStyle).getDescriptorWithMissingStylePropsRemoved(
469+
} = await (style as IVectorStyle).getDescriptorWithUpdatedStyleProps(
445470
nextFields,
471+
previousFields,
446472
getMapColors(getState())
447473
);
448474
if (hasChanges && nextStyleDescriptor) {
@@ -485,13 +511,13 @@ export function updateLayerStyleForSelectedLayer(styleDescriptor: StyleDescripto
485511

486512
export function setJoinsForLayer(layer: ILayer, joins: JoinDescriptor[]) {
487513
return async (dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) => {
514+
const previousFields = await (layer as IVectorLayer).getFields();
488515
await dispatch({
489516
type: SET_JOINS,
490517
layer,
491518
joins,
492519
});
493-
494-
await dispatch(clearMissingStyleProperties(layer.getId()));
520+
await dispatch(updateStyleProperties(layer.getId(), previousFields));
495521
dispatch(syncDataForLayerId(layer.getId()));
496522
};
497523
}

x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,8 @@ export class CountAggField implements IESAggField {
9797
canReadFromGeoJson(): boolean {
9898
return this._canReadFromGeoJson;
9999
}
100+
101+
isEqual(field: IESAggField) {
102+
return field.getName() === this.getName();
103+
}
100104
}

x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,8 @@ export class TopTermPercentageField implements IESAggField {
8383
canReadFromGeoJson(): boolean {
8484
return this._canReadFromGeoJson;
8585
}
86+
87+
isEqual(field: IESAggField) {
88+
return field.getName() === this.getName();
89+
}
8690
}

x-pack/plugins/maps/public/classes/fields/field.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export interface IField {
3232
supportsFieldMeta(): boolean;
3333

3434
canReadFromGeoJson(): boolean;
35+
isEqual(field: IField): boolean;
3536
}
3637

3738
export class AbstractField implements IField {
@@ -99,4 +100,8 @@ export class AbstractField implements IField {
99100
canReadFromGeoJson(): boolean {
100101
return true;
101102
}
103+
104+
isEqual(field: IField) {
105+
return this._origin === field.getOrigin() && this._fieldName === field.getName();
106+
}
102107
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { FIELD_ORIGIN, VECTOR_STYLES } from '../../../../common/constants';
8+
import { createStyleFieldsHelper, StyleFieldsHelper } from './style_fields_helper';
9+
import { AbstractField, IField } from '../../fields/field';
10+
11+
class MockField extends AbstractField {
12+
private readonly _dataType: string;
13+
private readonly _supportsAutoDomain: boolean;
14+
constructor({ dataType, supportsAutoDomain }: { dataType: string; supportsAutoDomain: boolean }) {
15+
super({ fieldName: 'foobar_' + dataType, origin: FIELD_ORIGIN.SOURCE });
16+
this._dataType = dataType;
17+
this._supportsAutoDomain = supportsAutoDomain;
18+
}
19+
async getDataType() {
20+
return this._dataType;
21+
}
22+
23+
supportsAutoDomain(): boolean {
24+
return this._supportsAutoDomain;
25+
}
26+
}
27+
28+
describe('StyleFieldHelper', () => {
29+
describe('isFieldDataTypeCompatibleWithStyleType', () => {
30+
async function createHelper(
31+
supportsAutoDomain: boolean
32+
): Promise<{
33+
styleFieldHelper: StyleFieldsHelper;
34+
stringField: IField;
35+
numberField: IField;
36+
dateField: IField;
37+
}> {
38+
const stringField = new MockField({
39+
dataType: 'string',
40+
supportsAutoDomain,
41+
});
42+
const numberField = new MockField({
43+
dataType: 'number',
44+
supportsAutoDomain,
45+
});
46+
const dateField = new MockField({
47+
dataType: 'date',
48+
supportsAutoDomain,
49+
});
50+
return {
51+
styleFieldHelper: await createStyleFieldsHelper([stringField, numberField, dateField]),
52+
stringField,
53+
numberField,
54+
dateField,
55+
};
56+
}
57+
58+
test('Should validate colors for all data types', async () => {
59+
const { styleFieldHelper, stringField, numberField, dateField } = await createHelper(true);
60+
61+
[
62+
VECTOR_STYLES.FILL_COLOR,
63+
VECTOR_STYLES.LINE_COLOR,
64+
VECTOR_STYLES.LABEL_COLOR,
65+
VECTOR_STYLES.LABEL_BORDER_COLOR,
66+
].forEach((styleType) => {
67+
expect(styleFieldHelper.hasFieldForStyle(stringField, styleType)).toEqual(true);
68+
expect(styleFieldHelper.hasFieldForStyle(numberField, styleType)).toEqual(true);
69+
expect(styleFieldHelper.hasFieldForStyle(dateField, styleType)).toEqual(true);
70+
});
71+
});
72+
73+
test('Should validate sizes for all number types', async () => {
74+
const { styleFieldHelper, stringField, numberField, dateField } = await createHelper(true);
75+
76+
[VECTOR_STYLES.LINE_WIDTH, VECTOR_STYLES.LABEL_SIZE, VECTOR_STYLES.ICON_SIZE].forEach(
77+
(styleType) => {
78+
expect(styleFieldHelper.hasFieldForStyle(stringField, styleType)).toEqual(false);
79+
expect(styleFieldHelper.hasFieldForStyle(numberField, styleType)).toEqual(true);
80+
expect(styleFieldHelper.hasFieldForStyle(dateField, styleType)).toEqual(true);
81+
}
82+
);
83+
});
84+
85+
test('Should not validate sizes if autodomain is not enabled', async () => {
86+
const { styleFieldHelper, stringField, numberField, dateField } = await createHelper(false);
87+
88+
[VECTOR_STYLES.LINE_WIDTH, VECTOR_STYLES.LABEL_SIZE, VECTOR_STYLES.ICON_SIZE].forEach(
89+
(styleType) => {
90+
expect(styleFieldHelper.hasFieldForStyle(stringField, styleType)).toEqual(false);
91+
expect(styleFieldHelper.hasFieldForStyle(numberField, styleType)).toEqual(false);
92+
expect(styleFieldHelper.hasFieldForStyle(dateField, styleType)).toEqual(false);
93+
}
94+
);
95+
});
96+
97+
test('Should validate orientation only number types', async () => {
98+
const { styleFieldHelper, stringField, numberField, dateField } = await createHelper(true);
99+
100+
[VECTOR_STYLES.ICON_ORIENTATION].forEach((styleType) => {
101+
expect(styleFieldHelper.hasFieldForStyle(stringField, styleType)).toEqual(false);
102+
expect(styleFieldHelper.hasFieldForStyle(numberField, styleType)).toEqual(true);
103+
expect(styleFieldHelper.hasFieldForStyle(dateField, styleType)).toEqual(false);
104+
});
105+
});
106+
107+
test('Should not validate label_border_size', async () => {
108+
const { styleFieldHelper, stringField, numberField, dateField } = await createHelper(true);
109+
110+
[VECTOR_STYLES.LABEL_BORDER_SIZE].forEach((styleType) => {
111+
expect(styleFieldHelper.hasFieldForStyle(stringField, styleType)).toEqual(false);
112+
expect(styleFieldHelper.hasFieldForStyle(numberField, styleType)).toEqual(false);
113+
expect(styleFieldHelper.hasFieldForStyle(dateField, styleType)).toEqual(false);
114+
});
115+
});
116+
});
117+
});

x-pack/plugins/maps/public/classes/styles/vector/style_fields_helper.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ export class StyleFieldsHelper {
6969
this._ordinalFields = ordinalFields;
7070
}
7171

72+
hasFieldForStyle(field: IField, styleName: VECTOR_STYLES): boolean {
73+
const fieldList = this.getFieldsForStyle(styleName);
74+
return fieldList.some((styleField) => field.getName() === styleField.name);
75+
}
76+
7277
getFieldsForStyle(styleName: VECTOR_STYLES): StyleField[] {
7378
switch (styleName) {
7479
case VECTOR_STYLES.ICON_ORIENTATION:

0 commit comments

Comments
 (0)