Skip to content

Commit d10153b

Browse files
nreesekibanamachineelizabetdev
committed
[Maps] add attribution to layer editor (#98328)
* [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>
1 parent 8875fc2 commit d10153b

36 files changed

Lines changed: 712 additions & 443 deletions

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import {
1616
import { DataRequestDescriptor } from './data_request_descriptor_types';
1717
import { AbstractSourceDescriptor, TermJoinSourceDescriptor } from './source_descriptor_types';
1818

19+
export type Attribution = {
20+
label: string;
21+
url: string;
22+
};
23+
1924
export type JoinDescriptor = {
2025
leftField?: string;
2126
right: TermJoinSourceDescriptor;
@@ -29,6 +34,7 @@ export type LayerDescriptor = {
2934
__trackedLayerDescriptor?: LayerDescriptor;
3035
__areTilesLoaded?: boolean;
3136
alpha?: number;
37+
attribution?: Attribution;
3238
id: string;
3339
joins?: JoinDescriptor[];
3440
label?: string | null;

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

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@ import {
1919
SOURCE_TYPES,
2020
} from '../constants';
2121

22-
export type AttributionDescriptor = {
23-
attributionText?: string;
24-
attributionUrl?: string;
25-
};
26-
2722
export type AbstractSourceDescriptor = {
2823
id?: string;
2924
type: string;
@@ -129,14 +124,11 @@ export type WMSSourceDescriptor = AbstractSourceDescriptor & {
129124
serviceUrl: string;
130125
layers: string;
131126
styles: string;
132-
attributionText: string;
133-
attributionUrl: string;
134127
};
135128

136-
export type XYZTMSSourceDescriptor = AbstractSourceDescriptor &
137-
AttributionDescriptor & {
138-
urlTemplate: string;
139-
};
129+
export type XYZTMSSourceDescriptor = AbstractSourceDescriptor & {
130+
urlTemplate: string;
131+
};
140132

141133
export type MVTFieldDescriptor = {
142134
name: string;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { moveAttribution } from './move_attribution';
9+
import { LayerDescriptor } from '../descriptor_types';
10+
11+
test('Should handle missing layerListJSON attribute', () => {
12+
const attributes = {
13+
title: 'my map',
14+
};
15+
expect(moveAttribution({ attributes })).toEqual({
16+
title: 'my map',
17+
});
18+
});
19+
20+
test('Should migrate source attribution to layer attribution', () => {
21+
const layerListJSON = JSON.stringify(([
22+
{
23+
sourceDescriptor: {
24+
attributionText: 'myLabel',
25+
attributionUrl: 'myUrl',
26+
id: 'mySourceId',
27+
},
28+
},
29+
] as unknown) as LayerDescriptor[]);
30+
31+
const attributes = {
32+
title: 'my map',
33+
layerListJSON,
34+
};
35+
36+
const { layerListJSON: migratedLayerListJSON } = moveAttribution({ attributes });
37+
const migratedLayerList = JSON.parse(migratedLayerListJSON!);
38+
expect(migratedLayerList).toEqual([
39+
{
40+
attribution: { label: 'myLabel', url: 'myUrl' },
41+
sourceDescriptor: { id: 'mySourceId' },
42+
},
43+
]);
44+
});
45+
46+
test('Should not add attribution to layer when source does not provide attribution', () => {
47+
const layerListJSON = JSON.stringify(([
48+
{
49+
sourceDescriptor: {
50+
id: 'mySourceId',
51+
},
52+
},
53+
] as unknown) as LayerDescriptor[]);
54+
55+
const attributes = {
56+
title: 'my map',
57+
layerListJSON,
58+
};
59+
60+
const { layerListJSON: migratedLayerListJSON } = moveAttribution({ attributes });
61+
const migratedLayerList = JSON.parse(migratedLayerListJSON!);
62+
expect(migratedLayerList).toEqual([
63+
{
64+
sourceDescriptor: { id: 'mySourceId' },
65+
},
66+
]);
67+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { MapSavedObjectAttributes } from '../map_saved_object_type';
9+
import { LayerDescriptor } from '../descriptor_types';
10+
11+
// In 7.14, attribution added to the layer_descriptor. Prior to 7.14, 2 sources, WMS and TMS, had attribution on source descriptor.
12+
export function moveAttribution({
13+
attributes,
14+
}: {
15+
attributes: MapSavedObjectAttributes;
16+
}): MapSavedObjectAttributes {
17+
if (!attributes || !attributes.layerListJSON) {
18+
return attributes;
19+
}
20+
21+
const layerList: LayerDescriptor[] = JSON.parse(attributes.layerListJSON);
22+
23+
layerList.forEach((layer: LayerDescriptor) => {
24+
const sourceDescriptor = layer.sourceDescriptor as {
25+
attributionText?: string;
26+
attributionUrl?: string;
27+
};
28+
if (sourceDescriptor.attributionText && sourceDescriptor.attributionUrl) {
29+
layer.attribution = {
30+
label: sourceDescriptor.attributionText,
31+
url: sourceDescriptor.attributionUrl,
32+
};
33+
delete sourceDescriptor.attributionText;
34+
delete sourceDescriptor.attributionUrl;
35+
}
36+
});
37+
38+
return {
39+
...attributes,
40+
layerListJSON: JSON.stringify(layerList),
41+
};
42+
}

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { updateFlyout } from './ui_actions';
2424
import {
2525
ADD_LAYER,
2626
ADD_WAITING_FOR_MAP_READY_LAYER,
27+
CLEAR_LAYER_PROP,
2728
CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST,
2829
REMOVE_LAYER,
2930
REMOVE_TRACKED_LAYER_STATE,
@@ -40,7 +41,12 @@ import {
4041
} from './map_action_constants';
4142
import { clearDataRequests, syncDataForLayerId, updateStyleMeta } from './data_request_actions';
4243
import { cleanTooltipStateForLayer } from './tooltip_actions';
43-
import { JoinDescriptor, LayerDescriptor, StyleDescriptor } from '../../common/descriptor_types';
44+
import {
45+
Attribution,
46+
JoinDescriptor,
47+
LayerDescriptor,
48+
StyleDescriptor,
49+
} from '../../common/descriptor_types';
4450
import { ILayer } from '../classes/layers/layer';
4551
import { IVectorLayer } from '../classes/layers/vector_layer';
4652
import { LAYER_STYLE_TYPE, LAYER_TYPE } from '../../common/constants';
@@ -349,6 +355,23 @@ export function updateLayerLabel(id: string, newLabel: string) {
349355
};
350356
}
351357

358+
export function setLayerAttribution(id: string, attribution: Attribution) {
359+
return {
360+
type: UPDATE_LAYER_PROP,
361+
id,
362+
propName: 'attribution',
363+
newValue: attribution,
364+
};
365+
}
366+
367+
export function clearLayerAttribution(id: string) {
368+
return {
369+
type: CLEAR_LAYER_PROP,
370+
id,
371+
propName: 'attribution',
372+
};
373+
}
374+
352375
export function updateLayerMinZoom(id: string, minZoom: number) {
353376
return {
354377
type: UPDATE_LAYER_PROP,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const UPDATE_LAYER_ORDER = 'UPDATE_LAYER_ORDER';
1010
export const ADD_LAYER = 'ADD_LAYER';
1111
export const SET_LAYER_ERROR_STATUS = 'SET_LAYER_ERROR_STATUS';
1212
export const ADD_WAITING_FOR_MAP_READY_LAYER = 'ADD_WAITING_FOR_MAP_READY_LAYER';
13+
export const CLEAR_LAYER_PROP = 'CLEAR_LAYER_PROP';
1314
export const CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST = 'CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST';
1415
export const REMOVE_LAYER = 'REMOVE_LAYER';
1516
export const SET_LAYER_VISIBILITY = 'SET_LAYER_VISIBILITY';

x-pack/plugins/maps/public/classes/layers/layer.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,14 @@ import {
3030
import { copyPersistentState } from '../../reducers/copy_persistent_state';
3131
import {
3232
AggDescriptor,
33+
Attribution,
3334
ESTermSourceDescriptor,
3435
JoinDescriptor,
3536
LayerDescriptor,
3637
MapExtent,
3738
StyleDescriptor,
3839
} from '../../../common/descriptor_types';
39-
import { Attribution, ImmutableSourceProperty, ISource, SourceEditorArgs } from '../sources/source';
40+
import { ImmutableSourceProperty, ISource, SourceEditorArgs } from '../sources/source';
4041
import { DataRequestContext } from '../../actions';
4142
import { IStyle } from '../styles/style';
4243
import { getJoinAggKey } from '../../../common/get_agg_key';
@@ -99,6 +100,7 @@ export interface ILayer {
99100
isFittable(): Promise<boolean>;
100101
getLicensedFeatures(): Promise<LICENSED_FEATURES[]>;
101102
getCustomIconAndTooltipContent(): CustomIconAndTooltipContent;
103+
getDescriptor(): LayerDescriptor;
102104
}
103105

104106
export type CustomIconAndTooltipContent = {
@@ -156,6 +158,10 @@ export class AbstractLayer implements ILayer {
156158
return mbStyle.sources[sourceId].data;
157159
}
158160

161+
getDescriptor(): LayerDescriptor {
162+
return this._descriptor;
163+
}
164+
159165
async cloneDescriptor(): Promise<LayerDescriptor> {
160166
const clonedDescriptor = copyPersistentState(this._descriptor);
161167
// layer id is uuid used to track styles/layers in mapbox
@@ -259,10 +265,16 @@ export class AbstractLayer implements ILayer {
259265
}
260266

261267
async getAttributions(): Promise<Attribution[]> {
262-
if (!this.hasErrors()) {
263-
return await this.getSource().getAttributions();
268+
if (this.hasErrors() || !this.isVisible()) {
269+
return [];
264270
}
265-
return [];
271+
272+
const attributionProvider = this.getSource().getAttributionProvider();
273+
if (attributionProvider) {
274+
return attributionProvider();
275+
}
276+
277+
return this._descriptor.attribution !== undefined ? [this._descriptor.attribution] : [];
266278
}
267279

268280
getStyleForEditing(): IStyle {

x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
1010
import { Feature } from 'geojson';
1111
import { Adapters } from 'src/plugins/inspector/public';
1212
import { FileLayer } from '@elastic/ems-client';
13-
import { Attribution, ImmutableSourceProperty, SourceEditorArgs } from '../source';
13+
import { ImmutableSourceProperty, SourceEditorArgs } from '../source';
1414
import { AbstractVectorSource, GeoJsonWithMeta, IVectorSource } from '../vector_source';
1515
import { SOURCE_TYPES, FIELD_ORIGIN, VECTOR_SHAPE_TYPE } from '../../../../common/constants';
1616
import { getEmsFileLayers } from '../../../util';
@@ -183,9 +183,11 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc
183183
}
184184
}
185185

186-
async getAttributions(): Promise<Attribution[]> {
187-
const emsFileLayer = await this.getEMSFileLayer();
188-
return emsFileLayer.getAttributions();
186+
getAttributionProvider() {
187+
return async () => {
188+
const emsFileLayer = await this.getEMSFileLayer();
189+
return emsFileLayer.getAttributions();
190+
};
189191
}
190192

191193
async getLeftJoinFields() {

x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,15 @@ export class EMSTMSSource extends AbstractTMSSource {
113113
}
114114
}
115115

116-
async getAttributions() {
117-
const emsTMSService = await this._getEMSTMSService();
118-
const markdown = emsTMSService.getMarkdownAttribution();
119-
if (!markdown) {
120-
return [];
121-
}
122-
return this.convertMarkdownLinkToObjectArr(markdown);
116+
getAttributionProvider() {
117+
return async () => {
118+
const emsTMSService = await this._getEMSTMSService();
119+
const markdown = emsTMSService.getMarkdownAttribution();
120+
if (!markdown) {
121+
return [];
122+
}
123+
return this.convertMarkdownLinkToObjectArr(markdown);
124+
};
123125
}
124126

125127
async getUrlTemplate() {

x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ describe('EMSTMSSource', () => {
4242
id: 'road_map',
4343
});
4444

45-
const attributions = await emsTmsSource.getAttributions();
45+
const attributionProvider = emsTmsSource.getAttributionProvider();
46+
const attributions = await attributionProvider();
4647
expect(attributions).toEqual([
4748
{
4849
label: 'foobar',

0 commit comments

Comments
 (0)