From d10153bc35970cccecc6434c3a0a9b2b5cb03607 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 3 May 2021 10:42:07 -0600 Subject: [PATCH] [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 --- .../layer_descriptor_types.ts | 6 + .../source_descriptor_types.ts | 14 +- .../migrations/move_attribution.test.ts | 67 ++++++ .../common/migrations/move_attribution.ts | 42 ++++ .../maps/public/actions/layer_actions.ts | 25 +- .../public/actions/map_action_constants.ts | 1 + .../maps/public/classes/layers/layer.tsx | 20 +- .../ems_file_source/ems_file_source.tsx | 10 +- .../sources/ems_tms_source/ems_tms_source.js | 16 +- .../ems_tms_source/ems_tms_source.test.js | 3 +- .../kibana_tilemap_source.js | 12 +- .../maps/public/classes/sources/source.ts | 13 +- .../sources/tms_source/tms_source.d.ts | 3 +- .../wms_source/wms_create_source_editor.js | 60 +---- .../classes/sources/wms_source/wms_source.js | 18 +- .../xyz_tms_editor.test.tsx.snap | 217 ------------------ .../xyz_tms_source/xyz_tms_editor.test.tsx | 21 -- .../sources/xyz_tms_source/xyz_tms_editor.tsx | 52 +---- .../sources/xyz_tms_source/xyz_tms_source.ts | 23 +- .../layer_panel/_index.scss | 1 + .../attribution_form_row.test.tsx.snap | 91 ++++++++ .../layer_settings/_attribution_form_row.scss | 27 +++ .../layer_settings/_attribution_popover.scss | 3 + .../layer_panel/layer_settings/_index.scss | 2 + .../attribution_form_row.test.tsx | 78 +++++++ .../layer_settings/attribution_form_row.tsx | 100 ++++++++ .../layer_settings/attribution_popover.tsx | 154 +++++++++++++ .../layer_panel/layer_settings/index.tsx | 6 + .../layer_settings/layer_settings.tsx | 13 ++ .../attribution_control.tsx | 3 +- .../maps/public/reducers/map/layer_utils.ts | 27 +++ .../plugins/maps/public/reducers/map/map.ts | 4 + .../maps/server/saved_objects/migrations.js | 9 + .../translations/translations/ja-JP.json | 6 - .../translations/translations/zh-CN.json | 6 - .../api_integration/apis/maps/migrations.js | 2 +- 36 files changed, 712 insertions(+), 443 deletions(-) create mode 100644 x-pack/plugins/maps/common/migrations/move_attribution.test.ts create mode 100644 x-pack/plugins/maps/common/migrations/move_attribution.ts create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_attribution_form_row.scss create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_attribution_popover.scss create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_index.scss create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_form_row.test.tsx create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_form_row.tsx create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_popover.tsx diff --git a/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts index 7c4746fd2ccb3..5c7c0251800a8 100644 --- a/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/layer_descriptor_types.ts @@ -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; @@ -29,6 +34,7 @@ export type LayerDescriptor = { __trackedLayerDescriptor?: LayerDescriptor; __areTilesLoaded?: boolean; alpha?: number; + attribution?: Attribution; id: string; joins?: JoinDescriptor[]; label?: string | null; diff --git a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts index 9c4ef3fde6d16..06afe34bb73fe 100644 --- a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts @@ -19,11 +19,6 @@ import { SOURCE_TYPES, } from '../constants'; -export type AttributionDescriptor = { - attributionText?: string; - attributionUrl?: string; -}; - export type AbstractSourceDescriptor = { id?: string; type: string; @@ -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; diff --git a/x-pack/plugins/maps/common/migrations/move_attribution.test.ts b/x-pack/plugins/maps/common/migrations/move_attribution.test.ts new file mode 100644 index 0000000000000..250361a3b6bed --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/move_attribution.test.ts @@ -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' }, + }, + ]); +}); diff --git a/x-pack/plugins/maps/common/migrations/move_attribution.ts b/x-pack/plugins/maps/common/migrations/move_attribution.ts new file mode 100644 index 0000000000000..74258e815439e --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/move_attribution.ts @@ -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), + }; +} diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index fe62e9fe9da51..b58595d32b4b2 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -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, @@ -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'; @@ -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, diff --git a/x-pack/plugins/maps/public/actions/map_action_constants.ts b/x-pack/plugins/maps/public/actions/map_action_constants.ts index b9951af71154d..497a5b7c29065 100644 --- a/x-pack/plugins/maps/public/actions/map_action_constants.ts +++ b/x-pack/plugins/maps/public/actions/map_action_constants.ts @@ -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'; diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 59edaa8ed1b95..dee1a26efef42 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -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'; @@ -99,6 +100,7 @@ export interface ILayer { isFittable(): Promise; getLicensedFeatures(): Promise; getCustomIconAndTooltipContent(): CustomIconAndTooltipContent; + getDescriptor(): LayerDescriptor; } export type CustomIconAndTooltipContent = { @@ -156,6 +158,10 @@ export class AbstractLayer implements ILayer { return mbStyle.sources[sourceId].data; } + getDescriptor(): LayerDescriptor { + return this._descriptor; + } + async cloneDescriptor(): Promise { const clonedDescriptor = copyPersistentState(this._descriptor); // layer id is uuid used to track styles/layers in mapbox @@ -259,10 +265,16 @@ export class AbstractLayer implements ILayer { } async getAttributions(): Promise { - 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 { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx index 22b873a94d1f7..a61ae85c89ac6 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.tsx @@ -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'; @@ -183,9 +183,11 @@ export class EMSFileSource extends AbstractVectorSource implements IEmsFileSourc } } - async getAttributions(): Promise { - const emsFileLayer = await this.getEMSFileLayer(); - return emsFileLayer.getAttributions(); + getAttributionProvider() { + return async () => { + const emsFileLayer = await this.getEMSFileLayer(); + return emsFileLayer.getAttributions(); + }; } async getLeftJoinFields() { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js index 97fb20b795bf6..fec1050120960 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js @@ -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() { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js index db5191e62fc04..8998e895f6541 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js @@ -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', diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_tilemap_source.js b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_tilemap_source.js index 94d082d8744e8..cceb3ddd1fcc0 100644 --- a/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_tilemap_source.js +++ b/x-pack/plugins/maps/public/classes/sources/kibana_tilemap_source/kibana_tilemap_source.js @@ -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() { diff --git a/x-pack/plugins/maps/public/classes/sources/source.ts b/x-pack/plugins/maps/public/classes/sources/source.ts index 25e3595d6dffa..28a68dda64cb6 100644 --- a/x-pack/plugins/maps/public/classes/sources/source.ts +++ b/x-pack/plugins/maps/public/classes/sources/source.ts @@ -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'; @@ -31,11 +31,6 @@ export type ImmutableSourceProperty = { link?: string; }; -export type Attribution = { - url: string; - label: string; -}; - export interface ISource { destroy(): void; getDisplayName(): Promise; @@ -47,7 +42,7 @@ export interface ISource { isRefreshTimerAware(): boolean; isTimeAware(): Promise; getImmutableProperties(): Promise; - getAttributions(): Promise; + getAttributionProvider(): (() => Promise) | null; isESSource(): boolean; renderSourceSettingsEditor(sourceEditorArgs: SourceEditorArgs): ReactElement | null; supportsFitToBounds(): Promise; @@ -103,8 +98,8 @@ export class AbstractSource implements ISource { return ''; } - async getAttributions(): Promise { - return []; + getAttributionProvider(): (() => Promise) | null { + return null; } isFieldAware(): boolean { diff --git a/x-pack/plugins/maps/public/classes/sources/tms_source/tms_source.d.ts b/x-pack/plugins/maps/public/classes/sources/tms_source/tms_source.d.ts index d38bcbc3b9a52..edd335fd5507b 100644 --- a/x-pack/plugins/maps/public/classes/sources/tms_source/tms_source.d.ts +++ b/x-pack/plugins/maps/public/classes/sources/tms_source/tms_source.d.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AbstractSource, Attribution, ISource } from '../source'; +import { AbstractSource, ISource } from '../source'; export interface ITMSSource extends ISource { getUrlTemplate(): Promise; @@ -13,5 +13,4 @@ export interface ITMSSource extends ISource { export class AbstractTMSSource extends AbstractSource implements ITMSSource { getUrlTemplate(): Promise; - getAttributions(): Promise; } diff --git a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_create_source_editor.js index b333122384af9..b13c15d19073d 100644 --- a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_create_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_create_source_editor.js @@ -39,8 +39,6 @@ export class WMSCreateSourceEditor extends Component { styleOptions: [], selectedLayerOptions: [], selectedStyleOptions: [], - attributionText: '', - attributionUrl: '', }; componentDidMount() { this._isMounted = true; @@ -51,7 +49,7 @@ export class WMSCreateSourceEditor extends Component { } _previewIfPossible = _.debounce(() => { - const { serviceUrl, layers, styles, attributionText, attributionUrl } = this.state; + const { serviceUrl, layers, styles } = this.state; const sourceConfig = serviceUrl && layers @@ -59,8 +57,6 @@ export class WMSCreateSourceEditor extends Component { serviceUrl, layers, styles, - attributionText, - attributionUrl, } : null; this.props.onSourceConfigChange(sourceConfig); @@ -155,15 +151,6 @@ export class WMSCreateSourceEditor extends Component { ); }; - _handleWMSAttributionChange(attributionUpdate) { - const { attributionText, attributionUrl } = this.state; - this.setState(attributionUpdate, () => { - if (attributionText && attributionUrl) { - this._previewIfPossible(); - } - }); - } - _renderLayerAndStyleInputs() { if (!this.state.hasAttemptedToLoadCapabilities || this.state.isLoadingCapabilities) { return null; @@ -245,49 +232,6 @@ export class WMSCreateSourceEditor extends Component { ); } - _renderAttributionInputs() { - if (!this.state.layers) { - return; - } - - const { attributionText, attributionUrl } = this.state; - - return ( - - - - this._handleWMSAttributionChange({ attributionText: target.value }) - } - /> - - - - this._handleWMSAttributionChange({ attributionUrl: target.value }) - } - /> - - - ); - } - render() { return ( @@ -302,8 +246,6 @@ export class WMSCreateSourceEditor extends Component { {this._renderGetCapabilitiesButton()} {this._renderLayerAndStyleInputs()} - - {this._renderAttributionInputs()} ); } diff --git a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_source.js b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_source.js index 97686e2d81b5a..0077602c9e00b 100644 --- a/x-pack/plugins/maps/public/classes/sources/wms_source/wms_source.js +++ b/x-pack/plugins/maps/public/classes/sources/wms_source/wms_source.js @@ -19,14 +19,12 @@ export const sourceTitle = i18n.translate('xpack.maps.source.wmsTitle', { export class WMSSource extends AbstractTMSSource { static type = SOURCE_TYPES.WMS; - static createDescriptor({ serviceUrl, layers, styles, attributionText, attributionUrl }) { + static createDescriptor({ serviceUrl, layers, styles }) { return { type: WMSSource.type, serviceUrl, layers, styles, - attributionText, - attributionUrl, }; } @@ -53,20 +51,6 @@ export class WMSSource extends AbstractTMSSource { return this._descriptor.serviceUrl; } - getAttributions() { - const { attributionText, attributionUrl } = this._descriptor; - const attributionComplete = !!attributionText && !!attributionUrl; - - return attributionComplete - ? [ - { - url: attributionUrl, - label: attributionText, - }, - ] - : []; - } - getUrlTemplate() { const client = new WmsClient({ serviceUrl: this._descriptor.serviceUrl }); return client.getUrlTemplate(this._descriptor.layers, this._descriptor.styles || ''); diff --git a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/__snapshots__/xyz_tms_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/__snapshots__/xyz_tms_editor.test.tsx.snap index b8ed4a727fad0..6dfa7afabc71b 100644 --- a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/__snapshots__/xyz_tms_editor.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/__snapshots__/xyz_tms_editor.test.tsx.snap @@ -1,182 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`attribution validation should provide no validation errors when attribution text and attribution url are provided 1`] = ` - - - - - - - - - - - -`; - -exports[`attribution validation should provide validation error when attribution text is provided without attribution url 1`] = ` - - - - - - - - - - - -`; - -exports[`attribution validation should provide validation error when attribution url is provided without attribution text 1`] = ` - - - - - - - - - - - -`; - exports[`should render 1`] = ` - - - - - - `; diff --git a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.test.tsx b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.test.tsx index 3cabad761ebc5..7a724b3e86709 100644 --- a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.test.tsx +++ b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.test.tsx @@ -15,24 +15,3 @@ test('should render', () => { const component = shallow(); expect(component).toMatchSnapshot(); }); - -describe('attribution validation', () => { - test('should provide validation error when attribution text is provided without attribution url', () => { - const component = shallow(); - component.setState({ attributionText: 'myAttribtionLabel' }); - expect(component).toMatchSnapshot(); - }); - - test('should provide validation error when attribution url is provided without attribution text', () => { - const component = shallow(); - component.setState({ attributionUrl: 'http://mySource' }); - expect(component).toMatchSnapshot(); - }); - - test('should provide no validation errors when attribution text and attribution url are provided', () => { - const component = shallow(); - component.setState({ attributionText: 'myAttribtionLabel' }); - component.setState({ attributionUrl: 'http://mySource' }); - expect(component).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.tsx b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.tsx index e0b6201711793..0cd3512ab5adc 100644 --- a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_editor.tsx @@ -10,12 +10,9 @@ import React, { Component, ChangeEvent } from 'react'; import _ from 'lodash'; import { EuiFormRow, EuiFieldText, EuiPanel } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; export type XYZTMSSourceConfig = { urlTemplate: string; - attributionText: string; - attributionUrl: string; }; interface Props { @@ -24,29 +21,19 @@ interface Props { interface State { url: string; - attributionText: string; - attributionUrl: string; } export class XYZTMSEditor extends Component { state = { url: '', - attributionText: '', - attributionUrl: '', }; _previewLayer = _.debounce(() => { - const { url, attributionText, attributionUrl } = this.state; + const { url } = this.state; const isUrlValid = url.indexOf('{x}') >= 0 && url.indexOf('{y}') >= 0 && url.indexOf('{z}') >= 0; - const sourceConfig = isUrlValid - ? { - urlTemplate: url, - attributionText, - attributionUrl, - } - : null; + const sourceConfig = isUrlValid ? { urlTemplate: url } : null; this.props.onSourceConfigChange(sourceConfig); }, 500); @@ -54,16 +41,7 @@ export class XYZTMSEditor extends Component { this.setState({ url: event.target.value }, this._previewLayer); }; - _onAttributionTextChange = (event: ChangeEvent) => { - this.setState({ attributionText: event.target.value }, this._previewLayer); - }; - - _onAttributionUrlChange = (event: ChangeEvent) => { - this.setState({ attributionUrl: event.target.value }, this._previewLayer); - }; - render() { - const { attributionText, attributionUrl } = this.state; return ( @@ -72,32 +50,6 @@ export class XYZTMSEditor extends Component { onChange={this._onUrlChange} /> - - - - - - ); } diff --git a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_source.ts b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_source.ts index 71e722581816b..4dd81b77668f4 100644 --- a/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/xyz_tms_source/xyz_tms_source.ts @@ -11,7 +11,7 @@ import { SOURCE_TYPES } from '../../../../common/constants'; import { registerSource } from '../source_registry'; import { AbstractTMSSource } from '../tms_source'; import { XYZTMSSourceDescriptor } from '../../../../common/descriptor_types'; -import { Attribution, ImmutableSourceProperty } from '../source'; +import { ImmutableSourceProperty } from '../source'; import { XYZTMSSourceConfig } from './xyz_tms_editor'; export const sourceTitle = i18n.translate('xpack.maps.source.ems_xyzTitle', { @@ -23,16 +23,10 @@ export class XYZTMSSource extends AbstractTMSSource { readonly _descriptor: XYZTMSSourceDescriptor; - static createDescriptor({ - urlTemplate, - attributionText, - attributionUrl, - }: XYZTMSSourceConfig): XYZTMSSourceDescriptor { + static createDescriptor({ urlTemplate }: XYZTMSSourceConfig): XYZTMSSourceDescriptor { return { type: XYZTMSSource.type, urlTemplate, - attributionText, - attributionUrl, }; } @@ -52,19 +46,6 @@ export class XYZTMSSource extends AbstractTMSSource { return this._descriptor.urlTemplate; } - async getAttributions(): Promise { - const { attributionText, attributionUrl } = this._descriptor; - const attributionComplete = !!attributionText && !!attributionUrl; - return attributionComplete - ? [ - { - url: attributionUrl as string, - label: attributionText as string, - }, - ] - : []; - } - async getUrlTemplate(): Promise { return this._descriptor.urlTemplate; } diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/_index.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/_index.scss index 41b4826a02c67..0f4f71a003478 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/_index.scss +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/_index.scss @@ -1,4 +1,5 @@ @import 'layer_panel'; +@import 'layer_settings/index'; @import 'filter_editor/filter_editor'; @import 'join_editor/resources/join'; @import 'style_settings/style_settings'; diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap new file mode 100644 index 0000000000000..cb496311b3d1c --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should render add form row when attribution not provided 1`] = ` +
+
+ + Attribution + +
+ +
+
+
+`; + +exports[`Should render edit form row when attribution not provided 1`] = ` +
+
+ + Attribution + +
+ + + label1 + + +
+ + + + +
+
+
+
+`; + +exports[`Should render null when layer source has attribution provider 1`] = `""`; diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_attribution_form_row.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_attribution_form_row.scss new file mode 100644 index 0000000000000..a8a6264c951ce --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_attribution_form_row.scss @@ -0,0 +1,27 @@ +.mapAttributionFormRow { + display: flex; + margin: $euiSizeS 0; +} + +.mapAttributionFormRow__legend { + @include euiFormLabel; + display: inline-flex; + width: calc(33% - #{$euiSizeS}); + height: $euiSizeXL; + align-items: center; + margin-right: $euiSizeS; +} + +.mapAttributionFormRow__field { + width: 67%; +} + +.mapAttributionFormRow__addButton { + margin-top: $euiSizeXS; +} + +.mapAttributionFormRow__buttons { + display: flex; + justify-content: flex-end; + margin-top: $euiSizeXS; +} \ No newline at end of file diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_attribution_popover.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_attribution_popover.scss new file mode 100644 index 0000000000000..ae4208f7de9ae --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_attribution_popover.scss @@ -0,0 +1,3 @@ +.mapAttributionPopover { + width: $euiSizeXXL * 12; +} \ No newline at end of file diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_index.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_index.scss new file mode 100644 index 0000000000000..6c457be428f95 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/_index.scss @@ -0,0 +1,2 @@ +@import 'attribution_form_row'; +@import 'attribution_popover'; \ No newline at end of file diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_form_row.test.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_form_row.test.tsx new file mode 100644 index 0000000000000..bd8be0ab37b51 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_form_row.test.tsx @@ -0,0 +1,78 @@ +/* + * 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 React from 'react'; +import { shallow } from 'enzyme'; +import { LayerDescriptor } from '../../../../common/descriptor_types'; +import { ILayer } from '../../../classes/layers/layer'; +import { ISource } from '../../../classes/sources/source'; +import { AttributionFormRow } from './attribution_form_row'; + +const defaultProps = { + onChange: () => {}, +}; + +test('Should render null when layer source has attribution provider', () => { + const sourceMock = ({ + getAttributionProvider: () => { + return async () => { + return [{ url: 'url1', label: 'label1' }]; + }; + }, + } as unknown) as ISource; + const layerMock = ({ + getSource: () => { + return sourceMock; + }, + } as unknown) as ILayer; + const component = shallow(); + + expect(component).toMatchSnapshot(); +}); + +test('Should render add form row when attribution not provided', () => { + const sourceMock = ({ + getAttributionProvider: () => { + return null; + }, + } as unknown) as ISource; + const layerMock = ({ + getSource: () => { + return sourceMock; + }, + getDescriptor: () => { + return ({} as unknown) as LayerDescriptor; + }, + } as unknown) as ILayer; + const component = shallow(); + + expect(component).toMatchSnapshot(); +}); + +test('Should render edit form row when attribution not provided', () => { + const sourceMock = ({ + getAttributionProvider: () => { + return null; + }, + } as unknown) as ISource; + const layerMock = ({ + getSource: () => { + return sourceMock; + }, + getDescriptor: () => { + return ({ + attribution: { + url: 'url1', + label: 'label1', + }, + } as unknown) as LayerDescriptor; + }, + } as unknown) as ILayer; + const component = shallow(); + + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_form_row.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_form_row.tsx new file mode 100644 index 0000000000000..10ad495d59cb7 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_form_row.tsx @@ -0,0 +1,100 @@ +/* + * 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 React from 'react'; +import { EuiButtonEmpty, EuiLink, EuiPanel } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Attribution } from '../../../../common/descriptor_types'; +import { ILayer } from '../../../classes/layers/layer'; +import { AttributionPopover } from './attribution_popover'; + +interface Props { + layer: ILayer; + onChange: (attribution?: Attribution) => void; +} + +export function AttributionFormRow(props: Props) { + function renderAttribution() { + const layerDescriptor = props.layer.getDescriptor(); + + return ( +
+
+ + {i18n.translate('xpack.maps.layerSettings.attributionLegend', { + defaultMessage: 'Attribution', + })} + + + {layerDescriptor.attribution === undefined ? ( +
+ +
+ ) : ( +
+ + + {layerDescriptor.attribution.label} + + + +
+ + + { + props.onChange(); + }} + size="xs" + iconType="trash" + color="danger" + aria-label={i18n.translate('xpack.maps.attribution.clearBtnAriaLabel', { + defaultMessage: 'Clear attribution', + })} + > + + +
+
+ )} +
+
+ ); + } + + return props.layer.getSource().getAttributionProvider() ? null : renderAttribution(); +} diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_popover.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_popover.tsx new file mode 100644 index 0000000000000..90b657a50ed7b --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/attribution_popover.tsx @@ -0,0 +1,154 @@ +/* + * 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 React, { ChangeEvent, Component } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiFieldText, + EuiFormRow, + EuiPopover, + EuiPopoverFooter, + EuiPopoverTitle, + EuiSpacer, + EuiTextAlign, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Attribution } from '../../../../common/descriptor_types'; + +interface Props { + onChange: (attribution: Attribution) => void; + popoverButtonLabel: string; + popoverButtonAriaLabel: string; + popoverButtonIcon: string; + popoverButtonClassName?: string; + label: string; + url: string; +} + +interface State { + isPopoverOpen: boolean; + label: string; + url: string; +} + +export class AttributionPopover extends Component { + state: State = { + isPopoverOpen: false, + label: this.props.label, + url: this.props.url, + }; + + _togglePopover = () => { + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + + _closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + _onApply = () => { + this.props.onChange({ + label: this.state.label, + url: this.state.url, + }); + this._closePopover(); + }; + + _onLabelChange = (event: ChangeEvent) => { + this.setState({ label: event.target.value }); + }; + + _onUrlChange = (event: ChangeEvent) => { + this.setState({ url: event.target.value }); + }; + + _renderPopoverButton() { + return ( + + {this.props.popoverButtonLabel} + + ); + } + + _renderContent() { + const isComplete = this.state.label.length !== 0 && this.state.url.length !== 0; + const hasChanges = this.state.label !== this.props.label || this.state.url !== this.props.url; + return ( +
+ + + + + + + + + + + + + + + + + +
+ ); + } + + render() { + return ( + + {this._renderContent()} + + ); + } +} diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx index 2d81b434b3855..5e61812e866ff 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.tsx @@ -9,15 +9,21 @@ import { AnyAction, Dispatch } from 'redux'; import { connect } from 'react-redux'; import { LayerSettings } from './layer_settings'; import { + clearLayerAttribution, + setLayerAttribution, updateLayerLabel, updateLayerMaxZoom, updateLayerMinZoom, updateLayerAlpha, updateLabelsOnTop, } from '../../../actions'; +import { Attribution } from '../../../../common/descriptor_types'; function mapDispatchToProps(dispatch: Dispatch) { return { + clearLayerAttribution: (id: string) => dispatch(clearLayerAttribution(id)), + setLayerAttribution: (id: string, attribution: Attribution) => + dispatch(setLayerAttribution(id, attribution)), updateLabel: (id: string, label: string) => dispatch(updateLayerLabel(id, label)), updateMinZoom: (id: string, minZoom: number) => dispatch(updateLayerMinZoom(id, minZoom)), updateMaxZoom: (id: string, maxZoom: number) => dispatch(updateLayerMaxZoom(id, maxZoom)), diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx index 0b6ac34ec8b53..0bcf6547daddf 100644 --- a/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.tsx @@ -17,13 +17,17 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { Attribution } from '../../../../common/descriptor_types'; import { MAX_ZOOM } from '../../../../common/constants'; import { AlphaSlider } from '../../../components/alpha_slider'; import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_react/public'; import { ILayer } from '../../../classes/layers/layer'; +import { AttributionFormRow } from './attribution_form_row'; export interface Props { layer: ILayer; + clearLayerAttribution: (layerId: string) => void; + setLayerAttribution: (id: string, attribution: Attribution) => void; updateLabel: (layerId: string, label: string) => void; updateMinZoom: (layerId: string, minZoom: number) => void; updateMaxZoom: (layerId: string, maxZoom: number) => void; @@ -54,6 +58,14 @@ export function LayerSettings(props: Props) { props.updateLabelsOnTop(layerId, event.target.checked); }; + const onAttributionChange = (attribution?: Attribution) => { + if (attribution) { + props.setLayerAttribution(layerId, attribution); + } else { + props.clearLayerAttribution(layerId); + } + }; + const renderZoomSliders = () => { return ( {renderShowLabelsOnTop()} + diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/attribution_control/attribution_control.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/attribution_control/attribution_control.tsx index 3d36f62944636..3955db278777a 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/attribution_control/attribution_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/attribution_control/attribution_control.tsx @@ -9,7 +9,7 @@ import React, { Component, Fragment } from 'react'; import _ from 'lodash'; import { EuiText, EuiLink } from '@elastic/eui'; import classNames from 'classnames'; -import { Attribution } from '../../../classes/sources/source'; +import { Attribution } from '../../../../common/descriptor_types'; import { ILayer } from '../../../classes/layers/layer'; export interface Props { @@ -65,6 +65,7 @@ export class AttributionControl extends Component { } } } + // Reflect top-to-bottom layer order as left-to-right in attribs uniqueAttributions.reverse(); if (!_.isEqual(this.state.uniqueAttributions, uniqueAttributions)) { diff --git a/x-pack/plugins/maps/public/reducers/map/layer_utils.ts b/x-pack/plugins/maps/public/reducers/map/layer_utils.ts index dfc3bf670867d..206cc4a740192 100644 --- a/x-pack/plugins/maps/public/reducers/map/layer_utils.ts +++ b/x-pack/plugins/maps/public/reducers/map/layer_utils.ts @@ -17,6 +17,33 @@ export function findLayerById(state: MapState, layerId: string): LayerDescriptor return state.layerList.find(({ id }) => layerId === id); } +export function clearLayerProp( + state: MapState, + layerId: string, + propName: keyof LayerDescriptor +): MapState { + if (!layerId) { + return state; + } + + const { layerList } = state; + const layerIdx = getLayerIndex(layerList, layerId); + if (layerIdx === -1) { + return state; + } + + const updatedLayer = { + ...layerList[layerIdx], + }; + delete updatedLayer[propName]; + const updatedList = [ + ...layerList.slice(0, layerIdx), + updatedLayer, + ...layerList.slice(layerIdx + 1), + ]; + return { ...state, layerList: updatedList }; +} + export function updateLayerInList( state: MapState, layerId: string, diff --git a/x-pack/plugins/maps/public/reducers/map/map.ts b/x-pack/plugins/maps/public/reducers/map/map.ts index 9573f1ac80c6b..daeba8e9982b0 100644 --- a/x-pack/plugins/maps/public/reducers/map/map.ts +++ b/x-pack/plugins/maps/public/reducers/map/map.ts @@ -14,6 +14,7 @@ import { ADD_LAYER, SET_LAYER_ERROR_STATUS, ADD_WAITING_FOR_MAP_READY_LAYER, + CLEAR_LAYER_PROP, CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST, REMOVE_LAYER, SET_LAYER_VISIBILITY, @@ -49,6 +50,7 @@ import { import { getDefaultMapSettings } from './default_map_settings'; import { + clearLayerProp, getLayerIndex, removeTrackedLayerState, rollbackTrackedLayerState, @@ -258,6 +260,8 @@ export function map(state: MapState = DEFAULT_MAP_STATE, action: any) { }; case UPDATE_LAYER_PROP: return updateLayerInList(state, action.id, action.propName, action.newValue); + case CLEAR_LAYER_PROP: + return clearLayerProp(state, action.id, action.propName); case UPDATE_SOURCE_PROP: return updateLayerSourceDescriptorProp(state, action.layerId, action.propName, action.value); case SET_JOINS: diff --git a/x-pack/plugins/maps/server/saved_objects/migrations.js b/x-pack/plugins/maps/server/saved_objects/migrations.js index 95a2e7ff84810..d10e22722970a 100644 --- a/x-pack/plugins/maps/server/saved_objects/migrations.js +++ b/x-pack/plugins/maps/server/saved_objects/migrations.js @@ -16,6 +16,7 @@ import { migrateJoinAggKey } from '../../common/migrations/join_agg_key'; import { removeBoundsFromSavedObject } from '../../common/migrations/remove_bounds'; import { setDefaultAutoFitToBounds } from '../../common/migrations/set_default_auto_fit_to_bounds'; import { addTypeToTermJoin } from '../../common/migrations/add_type_to_termjoin'; +import { moveAttribution } from '../../common/migrations/move_attribution'; export const migrations = { map: { @@ -89,6 +90,14 @@ export const migrations = { '7.12.0': (doc) => { const attributes = addTypeToTermJoin(doc); + return { + ...doc, + attributes, + }; + }, + '7.14.0': (doc) => { + const attributes = moveAttribution(doc); + return { ...doc, attributes, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index d03016841600f..f44f4cc727d3d 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -12704,8 +12704,6 @@ "xpack.maps.source.pewPewDescription": "ソースとデスティネーションの間の集約データパスです。", "xpack.maps.source.pewPewTitle": "ソースとデスティネーションの接続", "xpack.maps.source.urlLabel": "Url", - "xpack.maps.source.wms.attributionLink": "属性テキストにはリンクが必要です", - "xpack.maps.source.wms.attributionText": "属性 URL にはテキストが必要です", "xpack.maps.source.wms.getCapabilitiesButtonText": "負荷容量", "xpack.maps.source.wms.getCapabilitiesErrorCalloutTitle": "サービスメタデータを読み込めません", "xpack.maps.source.wms.layersHelpText": "レイヤー名のコンマ区切りのリストを使用します", @@ -12851,10 +12849,6 @@ "xpack.maps.viewControl.zoomLabel": "ズーム:", "xpack.maps.visTypeAlias.description": "マップを作成し、複数のレイヤーとインデックスを使用して、スタイルを設定します。", "xpack.maps.visTypeAlias.title": "マップ", - "xpack.maps.xyztmssource.attributionLink": "属性テキストにはリンクが必要です", - "xpack.maps.xyztmssource.attributionLinkLabel": "属性リンク", - "xpack.maps.xyztmssource.attributionText": "属性 URL にはテキストが必要です", - "xpack.maps.xyztmssource.attributionTextLabel": "属性テキスト", "xpack.ml.accessDenied.description": "ML プラグインへのアクセスパーミッションがありません", "xpack.ml.accessDenied.label": "パーミッションがありません", "xpack.ml.accessDeniedLabel": "アクセスが拒否されました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 0d61bc4a3f50b..c8bf6ed8f4eb4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -12872,8 +12872,6 @@ "xpack.maps.source.pewPewDescription": "源和目标之间的聚合数据路径", "xpack.maps.source.pewPewTitle": "源-目标连接", "xpack.maps.source.urlLabel": "URL", - "xpack.maps.source.wms.attributionLink": "属性文本必须附带链接", - "xpack.maps.source.wms.attributionText": "属性 url 必须附带文本", "xpack.maps.source.wms.getCapabilitiesButtonText": "加载功能", "xpack.maps.source.wms.getCapabilitiesErrorCalloutTitle": "无法加载服务元数据", "xpack.maps.source.wms.layersHelpText": "使用图层名称逗号分隔列表", @@ -13019,10 +13017,6 @@ "xpack.maps.viewControl.zoomLabel": "缩放:", "xpack.maps.visTypeAlias.description": "使用多个图层和索引创建地图并提供样式。", "xpack.maps.visTypeAlias.title": "Maps", - "xpack.maps.xyztmssource.attributionLink": "属性文本必须附带链接", - "xpack.maps.xyztmssource.attributionLinkLabel": "属性链接", - "xpack.maps.xyztmssource.attributionText": "属性 url 必须附带文本", - "xpack.maps.xyztmssource.attributionTextLabel": "属性文本", "xpack.ml.accessDenied.description": "您无权访问 ML 插件", "xpack.ml.accessDenied.label": "权限不足", "xpack.ml.accessDeniedLabel": "访问被拒绝", diff --git a/x-pack/test/api_integration/apis/maps/migrations.js b/x-pack/test/api_integration/apis/maps/migrations.js index 029d6bd670d01..109579e867cb0 100644 --- a/x-pack/test/api_integration/apis/maps/migrations.js +++ b/x-pack/test/api_integration/apis/maps/migrations.js @@ -42,7 +42,7 @@ export default function ({ getService }) { type: 'index-pattern', }, ]); - expect(resp.body.migrationVersion).to.eql({ map: '7.12.0' }); + expect(resp.body.migrationVersion).to.eql({ map: '7.14.0' }); expect(resp.body.attributes.layerListJSON.includes('indexPatternRefName')).to.be(true); }); });