Skip to content

Commit

Permalink
[maps] Lens choropleth chart (#126819)
Browse files Browse the repository at this point in the history
* register expression

* renderer

* register visualization

* tslint

* clean up

* refactor

* auto suggest

* update chart on change

* fix line color

* fix accessor labels

* remove console.logs

* use valueAccessor label in layer label

* region key editor

* fix suggestions

* eslint

* revert unneeded change

* fix i18n and clearLayer

* fix jest test

* tslint

* fix ems_autosuggest jest test

* lower suggestions score after first match

* functional test

* let lens render preview icon by not returning an expression

* functional test tslint

* avoid multiple suggestions with multiple layers

* suggestion cleanup

* review feedback

* rename Choropleth to Region map

* review feedback

* remove merge conflicts

* revert change to x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx

* fix functional test

* log table data

* expose common/expressions as extra public dir

* revert log table data

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
nreese and kibanamachine authored Mar 17, 2022
1 parent 2230685 commit e45209b
Show file tree
Hide file tree
Showing 39 changed files with 1,250 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ export function getSuggestions({
currentVisualizationState,
subVisualizationId,
palette,
visualizeTriggerFieldContext && 'isVisualizeAction' in visualizeTriggerFieldContext
visualizeTriggerFieldContext && 'isVisualizeAction' in visualizeTriggerFieldContext,
activeData
);
});
})
Expand Down Expand Up @@ -207,7 +208,8 @@ function getVisualizationSuggestions(
currentVisualizationState: unknown,
subVisualizationId?: string,
mainPalette?: PaletteOutput,
isFromContext?: boolean
isFromContext?: boolean,
activeData?: Record<string, Datatable>
) {
return visualization
.getSuggestions({
Expand All @@ -217,6 +219,7 @@ function getVisualizationSuggestions(
subVisualizationId,
mainPalette,
isFromContext,
activeData,
})
.map(({ state, ...visualizationSuggestion }) => ({
...visualizationSuggestion,
Expand Down
10 changes: 9 additions & 1 deletion x-pack/plugins/lens/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ export type {
TypedLensByValueInput,
} from './embeddable/embeddable_component';
export type { XYState } from './xy_visualization/types';
export type { DataType, OperationMetadata, Visualization } from './types';
export type {
DatasourcePublicAPI,
DataType,
OperationMetadata,
SuggestionRequest,
TableSuggestion,
Visualization,
VisualizationSuggestion,
} from './types';
export type {
AxesSettingsConfig,
XYLayerConfig,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/lens/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ export interface SuggestionRequest<T = unknown> {
* Different suggestions can be generated for each subtype of the visualization
*/
subVisualizationId?: string;
activeData?: Record<string, Datatable>;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export type TiledSingleLayerVectorSourceDescriptor = AbstractSourceDescriptor &

export type InlineFieldDescriptor = {
name: string;
label?: string;
type: 'string' | 'number';
};

Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export {
MAX_ZOOM,
MIN_ZOOM,
VECTOR_SHAPE_TYPE,
VECTOR_STYLES,
} from './constants';

export type { FieldFormatter } from './constants';
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/maps/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
"kibanaVersion": "kibana",
"configPath": ["xpack", "maps"],
"requiredPlugins": [
"lens",
"licensing",
"features",
"inspector",
"data",
"fieldFormats",
"fileUpload",
"uiActions",
"navigation",
Expand Down
25 changes: 24 additions & 1 deletion x-pack/plugins/maps/public/actions/layer_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getLayerListRaw,
getMapColors,
getMapReady,
getMapSettings,
getSelectedLayerId,
} from '../selectors/map_selectors';
import { FLYOUT_STATE } from '../reducers/ui';
Expand All @@ -35,12 +36,18 @@ import {
SET_SELECTED_LAYER,
SET_WAITING_FOR_READY_HIDDEN_LAYERS,
TRACK_CURRENT_LAYER_STATE,
UPDATE_LAYER,
UPDATE_LAYER_ORDER,
UPDATE_LAYER_PROP,
UPDATE_LAYER_STYLE,
UPDATE_SOURCE_PROP,
} from './map_action_constants';
import { clearDataRequests, syncDataForLayerId, updateStyleMeta } from './data_request_actions';
import {
autoFitToBounds,
clearDataRequests,
syncDataForLayerId,
updateStyleMeta,
} from './data_request_actions';
import { updateTooltipStateForLayer } from './tooltip_actions';
import {
Attribution,
Expand Down Expand Up @@ -117,6 +124,22 @@ export function replaceLayerList(newLayerList: LayerDescriptor[]) {
};
}

export function updateLayerById(layerDescriptor: LayerDescriptor) {
return async (
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
getState: () => MapStoreState
) => {
dispatch({
type: UPDATE_LAYER,
layer: layerDescriptor,
});
await dispatch(syncDataForLayerId(layerDescriptor.id, false));
if (getMapSettings(getState()).autoFitToDataBounds) {
dispatch(autoFitToBounds());
}
};
}

export function cloneLayer(layerId: string) {
return async (
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/maps/public/actions/map_action_constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const LAYER_DATA_LOAD_ERROR = 'LAYER_DATA_LOAD_ERROR';
export const UPDATE_SOURCE_DATA_REQUEST = 'UPDATE_SOURCE_DATA_REQUEST';
export const SET_JOINS = 'SET_JOINS';
export const SET_QUERY = 'SET_QUERY';
export const UPDATE_LAYER = 'UPDATE_LAYER';
export const UPDATE_LAYER_PROP = 'UPDATE_LAYER_PROP';
export const UPDATE_LAYER_STYLE = 'UPDATE_LAYER_STYLE';
export const SET_LAYER_STYLE_META = 'SET_LAYER_STYLE_META';
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/maps/public/classes/fields/inline_field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,25 @@ import { IField, AbstractField } from './field';
import { IVectorSource } from '../sources/vector_source';

export class InlineField<T extends IVectorSource> extends AbstractField implements IField {
private readonly _label?: string;
private readonly _source: T;
private readonly _dataType: string;

constructor({
fieldName,
label,
source,
origin,
dataType,
}: {
fieldName: string;
label?: string;
source: T;
origin: FIELD_ORIGIN;
dataType: string;
}) {
super({ fieldName, origin });
this._label = label;
this._source = source;
this._dataType = dataType;
}
Expand All @@ -42,7 +46,7 @@ export class InlineField<T extends IVectorSource> extends AbstractField implemen
}

async getLabel(): Promise<string> {
return this.getName();
return this._label ? this._label : this.getName();
}

async getDataType(): Promise<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer {
}

const joinStates = await this._syncJoins(syncContext, style);
performInnerJoins(
await performInnerJoins(
sourceResult,
joinStates,
syncContext.updateSourceData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,8 @@
import sinon from 'sinon';
import _ from 'lodash';
import { FeatureCollection } from 'geojson';
import { ESTermSourceDescriptor } from '../../../../../common/descriptor_types';
import {
AGG_TYPE,
FEATURE_VISIBLE_PROPERTY_NAME,
SOURCE_TYPES,
} from '../../../../../common/constants';
import { TableSourceDescriptor } from '../../../../../common/descriptor_types';
import { FEATURE_VISIBLE_PROPERTY_NAME, SOURCE_TYPES } from '../../../../../common/constants';
import { performInnerJoins } from './perform_inner_joins';
import { InnerJoin } from '../../../joins/inner_join';
import { IVectorSource } from '../../../sources/vector_source';
Expand Down Expand Up @@ -53,18 +49,21 @@ const featureCollection = {
const joinDescriptor = {
leftField: LEFT_FIELD,
right: {
applyGlobalQuery: true,
applyGlobalTime: true,
id: 'd3625663-5b34-4d50-a784-0d743f676a0c',
indexPatternId: 'myIndexPattern',
metrics: [
__rows: [],
__columns: [
{
type: AGG_TYPE.COUNT,
name: 'rightKey',
type: 'string',
},
{
name: COUNT_PROPERTY_NAME,
type: 'number',
},
],
term: 'rightKey',
type: SOURCE_TYPES.ES_TERM_SOURCE,
} as ESTermSourceDescriptor,
type: SOURCE_TYPES.TABLE_SOURCE,
} as TableSourceDescriptor,
};
const mockVectorSource = {
getInspectorAdapters: () => {
Expand All @@ -75,18 +74,21 @@ const mockVectorSource = {
getName: () => {
return LEFT_FIELD;
},
} as IField;
getLabel: () => {
return LEFT_FIELD;
},
} as unknown as IField;
},
} as unknown as IVectorSource;
const innerJoin = new InnerJoin(joinDescriptor, mockVectorSource);
const propertiesMap = new Map<string, Record<string | number, unknown>>();
propertiesMap.set('alpha', { [COUNT_PROPERTY_NAME]: 1 });

test('should skip join when no state has changed', () => {
test('should skip join when no state has changed', async () => {
const updateSourceData = sinon.spy();
const onJoinError = sinon.spy();

performInnerJoins(
await performInnerJoins(
{
refreshed: false,
featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
Expand All @@ -105,11 +107,11 @@ test('should skip join when no state has changed', () => {
expect(onJoinError.notCalled);
});

test('should perform join when features change', () => {
test('should perform join when features change', async () => {
const updateSourceData = sinon.spy();
const onJoinError = sinon.spy();

performInnerJoins(
await performInnerJoins(
{
refreshed: true,
featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
Expand All @@ -128,11 +130,11 @@ test('should perform join when features change', () => {
expect(onJoinError.notCalled);
});

test('should perform join when join state changes', () => {
test('should perform join when join state changes', async () => {
const updateSourceData = sinon.spy();
const onJoinError = sinon.spy();

performInnerJoins(
await performInnerJoins(
{
refreshed: false,
featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
Expand All @@ -151,11 +153,11 @@ test('should perform join when join state changes', () => {
expect(onJoinError.notCalled);
});

test('should call updateSourceData with feature collection with updated feature visibility and join properties', () => {
test('should call updateSourceData with feature collection with updated feature visibility and join properties', async () => {
const updateSourceData = sinon.spy();
const onJoinError = sinon.spy();

performInnerJoins(
await performInnerJoins(
{
refreshed: true,
featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
Expand Down Expand Up @@ -204,11 +206,11 @@ test('should call updateSourceData with feature collection with updated feature
expect(onJoinError.notCalled);
});

test('should call updateSourceData when no results returned from terms aggregation', () => {
test('should call updateSourceData when no results returned from terms aggregation', async () => {
const updateSourceData = sinon.spy();
const onJoinError = sinon.spy();

performInnerJoins(
await performInnerJoins(
{
refreshed: false,
featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
Expand Down Expand Up @@ -256,15 +258,15 @@ test('should call updateSourceData when no results returned from terms aggregati
expect(onJoinError.notCalled);
});

test('should call onJoinError when there are no matching features', () => {
test('should call onJoinError when there are no matching features', async () => {
const updateSourceData = sinon.spy();
const onJoinError = sinon.spy();

// instead of returning military alphabet like "alpha" or "bravo", mismatched key returns numbers, like '1'
const propertiesMapFromMismatchedKey = new Map<string, Record<string | number, unknown>>();
propertiesMapFromMismatchedKey.set('1', { [COUNT_PROPERTY_NAME]: 1 });

performInnerJoins(
await performInnerJoins(
{
refreshed: false,
featureCollection: _.cloneDeep(featureCollection) as FeatureCollection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface JoinState {
propertiesMap?: PropertiesMap;
}

export function performInnerJoins(
export async function performInnerJoins(
sourceResult: SourceResult,
joinStates: JoinState[],
updateSourceData: DataRequestContext['updateSourceData'],
Expand Down Expand Up @@ -102,7 +102,11 @@ export function performInnerJoins(
}

const joinStatus = joinStatusesWithoutAnyMatches[0];
const leftFieldName = joinStatus.joinState.join.getLeftField().getName();
const leftFieldName = await joinStatus.joinState.join.getLeftField().getLabel();
const rightFieldName = await joinStatus.joinState.join
.getRightJoinSource()
.getTermField()
.getLabel();
const reason =
joinStatus.keys.length === 0
? i18n.translate('xpack.maps.vectorLayer.joinError.noLeftFieldValuesMsg', {
Expand All @@ -114,10 +118,7 @@ export function performInnerJoins(
values: {
leftFieldName,
leftFieldValues: prettyPrintArray(joinStatus.keys),
rightFieldName: joinStatus.joinState.join
.getRightJoinSource()
.getTermField()
.getName(),
rightFieldName,
rightFieldValues: prettyPrintArray(
Array.from(joinStatus.joinState.propertiesMap!.keys())
),
Expand Down
Loading

0 comments on commit e45209b

Please sign in to comment.