Skip to content

Commit

Permalink
[Maps] add attribution to layer editor (#98328) (#99073)
Browse files Browse the repository at this point in the history
* [Maps] add attribution to layer editor

* update sources to getAttributionProvider

* remove attribution UI from EMS_XYZ source

* remove attribution UI from WMS source editor

* clean up

* tslint

* AttributionFormRow jest test

* add migration

* tslint

* i18n fixes

* attribution

* [Maps] Improving design and a11y for attribution layer settings (#38)

* Design and a11y improvements

* Buttons aria labels

* Addressing PR review

* tslint and i18n fixes

* update jest snapshots

* remove placeholder for url

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elizabet Oliveira <elizabet.oliveira@elastic.co>

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Elizabet Oliveira <elizabet.oliveira@elastic.co>
  • Loading branch information
3 people committed May 3, 2021
1 parent 435f7fd commit 7d3e2c3
Show file tree
Hide file tree
Showing 36 changed files with 712 additions and 443 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ import {
import { DataRequestDescriptor } from './data_request_descriptor_types';
import { AbstractSourceDescriptor, TermJoinSourceDescriptor } from './source_descriptor_types';

export type Attribution = {
label: string;
url: string;
};

export type JoinDescriptor = {
leftField?: string;
right: TermJoinSourceDescriptor;
Expand All @@ -29,6 +34,7 @@ export type LayerDescriptor = {
__trackedLayerDescriptor?: LayerDescriptor;
__areTilesLoaded?: boolean;
alpha?: number;
attribution?: Attribution;
id: string;
joins?: JoinDescriptor[];
label?: string | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ import {
SOURCE_TYPES,
} from '../constants';

export type AttributionDescriptor = {
attributionText?: string;
attributionUrl?: string;
};

export type AbstractSourceDescriptor = {
id?: string;
type: string;
Expand Down Expand Up @@ -129,14 +124,11 @@ export type WMSSourceDescriptor = AbstractSourceDescriptor & {
serviceUrl: string;
layers: string;
styles: string;
attributionText: string;
attributionUrl: string;
};

export type XYZTMSSourceDescriptor = AbstractSourceDescriptor &
AttributionDescriptor & {
urlTemplate: string;
};
export type XYZTMSSourceDescriptor = AbstractSourceDescriptor & {
urlTemplate: string;
};

export type MVTFieldDescriptor = {
name: string;
Expand Down
67 changes: 67 additions & 0 deletions x-pack/plugins/maps/common/migrations/move_attribution.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { moveAttribution } from './move_attribution';
import { LayerDescriptor } from '../descriptor_types';

test('Should handle missing layerListJSON attribute', () => {
const attributes = {
title: 'my map',
};
expect(moveAttribution({ attributes })).toEqual({
title: 'my map',
});
});

test('Should migrate source attribution to layer attribution', () => {
const layerListJSON = JSON.stringify(([
{
sourceDescriptor: {
attributionText: 'myLabel',
attributionUrl: 'myUrl',
id: 'mySourceId',
},
},
] as unknown) as LayerDescriptor[]);

const attributes = {
title: 'my map',
layerListJSON,
};

const { layerListJSON: migratedLayerListJSON } = moveAttribution({ attributes });
const migratedLayerList = JSON.parse(migratedLayerListJSON!);
expect(migratedLayerList).toEqual([
{
attribution: { label: 'myLabel', url: 'myUrl' },
sourceDescriptor: { id: 'mySourceId' },
},
]);
});

test('Should not add attribution to layer when source does not provide attribution', () => {
const layerListJSON = JSON.stringify(([
{
sourceDescriptor: {
id: 'mySourceId',
},
},
] as unknown) as LayerDescriptor[]);

const attributes = {
title: 'my map',
layerListJSON,
};

const { layerListJSON: migratedLayerListJSON } = moveAttribution({ attributes });
const migratedLayerList = JSON.parse(migratedLayerListJSON!);
expect(migratedLayerList).toEqual([
{
sourceDescriptor: { id: 'mySourceId' },
},
]);
});
42 changes: 42 additions & 0 deletions x-pack/plugins/maps/common/migrations/move_attribution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { MapSavedObjectAttributes } from '../map_saved_object_type';
import { LayerDescriptor } from '../descriptor_types';

// In 7.14, attribution added to the layer_descriptor. Prior to 7.14, 2 sources, WMS and TMS, had attribution on source descriptor.
export function moveAttribution({
attributes,
}: {
attributes: MapSavedObjectAttributes;
}): MapSavedObjectAttributes {
if (!attributes || !attributes.layerListJSON) {
return attributes;
}

const layerList: LayerDescriptor[] = JSON.parse(attributes.layerListJSON);

layerList.forEach((layer: LayerDescriptor) => {
const sourceDescriptor = layer.sourceDescriptor as {
attributionText?: string;
attributionUrl?: string;
};
if (sourceDescriptor.attributionText && sourceDescriptor.attributionUrl) {
layer.attribution = {
label: sourceDescriptor.attributionText,
url: sourceDescriptor.attributionUrl,
};
delete sourceDescriptor.attributionText;
delete sourceDescriptor.attributionUrl;
}
});

return {
...attributes,
layerListJSON: JSON.stringify(layerList),
};
}
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 @@ -24,6 +24,7 @@ import { updateFlyout } from './ui_actions';
import {
ADD_LAYER,
ADD_WAITING_FOR_MAP_READY_LAYER,
CLEAR_LAYER_PROP,
CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST,
REMOVE_LAYER,
REMOVE_TRACKED_LAYER_STATE,
Expand All @@ -40,7 +41,12 @@ import {
} from './map_action_constants';
import { clearDataRequests, syncDataForLayerId, updateStyleMeta } from './data_request_actions';
import { cleanTooltipStateForLayer } from './tooltip_actions';
import { JoinDescriptor, LayerDescriptor, StyleDescriptor } from '../../common/descriptor_types';
import {
Attribution,
JoinDescriptor,
LayerDescriptor,
StyleDescriptor,
} from '../../common/descriptor_types';
import { ILayer } from '../classes/layers/layer';
import { IVectorLayer } from '../classes/layers/vector_layer';
import { LAYER_STYLE_TYPE, LAYER_TYPE } from '../../common/constants';
Expand Down Expand Up @@ -349,6 +355,23 @@ export function updateLayerLabel(id: string, newLabel: string) {
};
}

export function setLayerAttribution(id: string, attribution: Attribution) {
return {
type: UPDATE_LAYER_PROP,
id,
propName: 'attribution',
newValue: attribution,
};
}

export function clearLayerAttribution(id: string) {
return {
type: CLEAR_LAYER_PROP,
id,
propName: 'attribution',
};
}

export function updateLayerMinZoom(id: string, minZoom: number) {
return {
type: UPDATE_LAYER_PROP,
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 @@ -10,6 +10,7 @@ export const UPDATE_LAYER_ORDER = 'UPDATE_LAYER_ORDER';
export const ADD_LAYER = 'ADD_LAYER';
export const SET_LAYER_ERROR_STATUS = 'SET_LAYER_ERROR_STATUS';
export const ADD_WAITING_FOR_MAP_READY_LAYER = 'ADD_WAITING_FOR_MAP_READY_LAYER';
export const CLEAR_LAYER_PROP = 'CLEAR_LAYER_PROP';
export const CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST = 'CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST';
export const REMOVE_LAYER = 'REMOVE_LAYER';
export const SET_LAYER_VISIBILITY = 'SET_LAYER_VISIBILITY';
Expand Down
20 changes: 16 additions & 4 deletions x-pack/plugins/maps/public/classes/layers/layer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ import {
import { copyPersistentState } from '../../reducers/copy_persistent_state';
import {
AggDescriptor,
Attribution,
ESTermSourceDescriptor,
JoinDescriptor,
LayerDescriptor,
MapExtent,
StyleDescriptor,
} from '../../../common/descriptor_types';
import { Attribution, ImmutableSourceProperty, ISource, SourceEditorArgs } from '../sources/source';
import { ImmutableSourceProperty, ISource, SourceEditorArgs } from '../sources/source';
import { DataRequestContext } from '../../actions';
import { IStyle } from '../styles/style';
import { getJoinAggKey } from '../../../common/get_agg_key';
Expand Down Expand Up @@ -99,6 +100,7 @@ export interface ILayer {
isFittable(): Promise<boolean>;
getLicensedFeatures(): Promise<LICENSED_FEATURES[]>;
getCustomIconAndTooltipContent(): CustomIconAndTooltipContent;
getDescriptor(): LayerDescriptor;
}

export type CustomIconAndTooltipContent = {
Expand Down Expand Up @@ -156,6 +158,10 @@ export class AbstractLayer implements ILayer {
return mbStyle.sources[sourceId].data;
}

getDescriptor(): LayerDescriptor {
return this._descriptor;
}

async cloneDescriptor(): Promise<LayerDescriptor> {
const clonedDescriptor = copyPersistentState(this._descriptor);
// layer id is uuid used to track styles/layers in mapbox
Expand Down Expand Up @@ -259,10 +265,16 @@ export class AbstractLayer implements ILayer {
}

async getAttributions(): Promise<Attribution[]> {
if (!this.hasErrors()) {
return await this.getSource().getAttributions();
if (this.hasErrors() || !this.isVisible()) {
return [];
}
return [];

const attributionProvider = this.getSource().getAttributionProvider();
if (attributionProvider) {
return attributionProvider();
}

return this._descriptor.attribution !== undefined ? [this._descriptor.attribution] : [];
}

getStyleForEditing(): IStyle {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import { Feature } from 'geojson';
import { Adapters } from 'src/plugins/inspector/public';
import { FileLayer } from '@elastic/ems-client';
import { Attribution, ImmutableSourceProperty, SourceEditorArgs } from '../source';
import { ImmutableSourceProperty, SourceEditorArgs } from '../source';
import { AbstractVectorSource, GeoJsonWithMeta, IVectorSource } from '../vector_source';
import { SOURCE_TYPES, FIELD_ORIGIN, VECTOR_SHAPE_TYPE } from '../../../../common/constants';
import { getEmsFileLayers } from '../../../util';
Expand Down Expand Up @@ -183,9 +183,11 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc
}
}

async getAttributions(): Promise<Attribution[]> {
const emsFileLayer = await this.getEMSFileLayer();
return emsFileLayer.getAttributions();
getAttributionProvider() {
return async () => {
const emsFileLayer = await this.getEMSFileLayer();
return emsFileLayer.getAttributions();
};
}

async getLeftJoinFields() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,15 @@ export class EMSTMSSource extends AbstractTMSSource {
}
}

async getAttributions() {
const emsTMSService = await this._getEMSTMSService();
const markdown = emsTMSService.getMarkdownAttribution();
if (!markdown) {
return [];
}
return this.convertMarkdownLinkToObjectArr(markdown);
getAttributionProvider() {
return async () => {
const emsTMSService = await this._getEMSTMSService();
const markdown = emsTMSService.getMarkdownAttribution();
if (!markdown) {
return [];
}
return this.convertMarkdownLinkToObjectArr(markdown);
};
}

async getUrlTemplate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ describe('EMSTMSSource', () => {
id: 'road_map',
});

const attributions = await emsTmsSource.getAttributions();
const attributionProvider = emsTmsSource.getAttributionProvider();
const attributions = await attributionProvider();
expect(attributions).toEqual([
{
label: 'foobar',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ export class KibanaTilemapSource extends AbstractTMSSource {
return tilemap.url;
}

async getAttributions() {
const tilemap = getKibanaTileMap();
const markdown = _.get(tilemap, 'options.attribution', '');
const objArr = this.convertMarkdownLinkToObjectArr(markdown);
return objArr;
getAttributionProvider() {
return async () => {
const tilemap = getKibanaTileMap();
const markdown = _.get(tilemap, 'options.attribution', '');
const objArr = this.convertMarkdownLinkToObjectArr(markdown);
return objArr;
};
}

async getDisplayName() {
Expand Down
13 changes: 4 additions & 9 deletions x-pack/plugins/maps/public/classes/sources/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { copyPersistentState } from '../../reducers/copy_persistent_state';

import { IField } from '../fields/field';
import { FieldFormatter, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants';
import { AbstractSourceDescriptor } from '../../../common/descriptor_types';
import { AbstractSourceDescriptor, Attribution } from '../../../common/descriptor_types';
import { OnSourceChangeArgs } from '../../connected_components/layer_panel/view';
import { LICENSED_FEATURES } from '../../licensed_features';
import { PreIndexedShape } from '../../../common/elasticsearch_util';
Expand All @@ -31,11 +31,6 @@ export type ImmutableSourceProperty = {
link?: string;
};

export type Attribution = {
url: string;
label: string;
};

export interface ISource {
destroy(): void;
getDisplayName(): Promise<string>;
Expand All @@ -47,7 +42,7 @@ export interface ISource {
isRefreshTimerAware(): boolean;
isTimeAware(): Promise<boolean>;
getImmutableProperties(): Promise<ImmutableSourceProperty[]>;
getAttributions(): Promise<Attribution[]>;
getAttributionProvider(): (() => Promise<Attribution[]>) | null;
isESSource(): boolean;
renderSourceSettingsEditor(sourceEditorArgs: SourceEditorArgs): ReactElement<any> | null;
supportsFitToBounds(): Promise<boolean>;
Expand Down Expand Up @@ -103,8 +98,8 @@ export class AbstractSource implements ISource {
return '';
}

async getAttributions(): Promise<Attribution[]> {
return [];
getAttributionProvider(): (() => Promise<Attribution[]>) | null {
return null;
}

isFieldAware(): boolean {
Expand Down
Loading

0 comments on commit 7d3e2c3

Please sign in to comment.