[Maps] convert LayerPanel to typescript (#100481)

* [Maps] convert LayerPanel to typescript

* layer_errors

* style panel

* layer_panel component

* rename to EditLayerPanel

* clean up

* fix scss imports for rename

* one more scss path clean up

* fix EditLayerPanel errors
This commit is contained in:
Nathan Reese 2021-05-25 13:25:18 -06:00 committed by GitHub
parent a0ddca8b07
commit b30d96e7f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 216 additions and 227 deletions

View file

@ -89,6 +89,7 @@ export interface IVectorLayer extends ILayer {
getPropertiesForTooltip(properties: GeoJsonProperties): Promise<ITooltipProperty[]>;
hasJoins(): boolean;
canShowTooltip(): boolean;
getLeftJoinFields(): Promise<IField[]>;
}
export class VectorLayer extends AbstractLayer implements IVectorLayer {

View file

@ -12,7 +12,7 @@ import { TooltipSelector } from '../../../components/tooltip_selector';
import { getEmsFileLayers } from '../../../util';
import { IEmsFileSource } from './ems_file_source';
import { IField } from '../../fields/field';
import { OnSourceChangeArgs } from '../../../connected_components/layer_panel/view';
import { OnSourceChangeArgs } from '../source';
interface Props {
layerId: string;

View file

@ -18,7 +18,7 @@ import { MetricsEditor } from '../../../components/metrics_editor';
import { getIndexPatternService } from '../../../kibana_services';
import { GeoLineForm } from './geo_line_form';
import { AggDescriptor } from '../../../../common/descriptor_types';
import { OnSourceChangeArgs } from '../../../connected_components/layer_panel/view';
import { OnSourceChangeArgs } from '../source';
interface Props {
indexPatternId: string;

View file

@ -22,7 +22,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { getIndexPatternService } from '../../../kibana_services';
import { DEFAULT_MAX_RESULT_WINDOW, LAYER_TYPE, SCALING_TYPES } from '../../../../common/constants';
import { loadIndexSettings } from './load_index_settings';
import { OnSourceChangeArgs } from '../../../connected_components/layer_panel/view';
import { OnSourceChangeArgs } from '../source';
interface Props {
filterByMapBounds: boolean;

View file

@ -19,7 +19,7 @@ import {
SortDirection,
} from '../../../../../../../../src/plugins/data/common';
import { TopHitsForm } from './top_hits_form';
import { OnSourceChangeArgs } from '../../../../connected_components/layer_panel/view';
import { OnSourceChangeArgs } from '../../source';
interface Props {
onSourceConfigChange: (sourceConfig: Partial<ESSearchSourceDescriptor> | null) => void;

View file

@ -14,7 +14,7 @@ import { getIndexPatternService } from '../../../../kibana_services';
import { ValidatedRange } from '../../../../components/validated_range';
import { DEFAULT_MAX_INNER_RESULT_WINDOW } from '../../../../../common/constants';
import { loadIndexSettings } from '../load_index_settings';
import { OnSourceChangeArgs } from '../../../../connected_components/layer_panel/view';
import { OnSourceChangeArgs } from '../../source';
import { IFieldType, SortDirection } from '../../../../../../../../src/plugins/data/public';
interface Props {

View file

@ -16,7 +16,7 @@ import { getIndexPatternService } from '../../../../kibana_services';
import { getTermsFields, getSortFields, getSourceFields } from '../../../../index_pattern_util';
import { SortDirection, IFieldType } from '../../../../../../../../src/plugins/data/public';
import { ESDocField } from '../../../fields/es_doc_field';
import { OnSourceChangeArgs } from '../../../../connected_components/layer_panel/view';
import { OnSourceChangeArgs } from '../../source';
import { TopHitsForm } from './top_hits_form';
import { ESSearchSource } from '../es_search_source';
import { IField } from '../../../fields/field';

View file

@ -14,7 +14,7 @@ import { TooltipSelector } from '../../../components/tooltip_selector';
import { MVTField } from '../../fields/mvt_field';
import { MVTSingleLayerVectorSource } from './mvt_single_layer_vector_source';
import { MVTSettings, MVTSingleLayerSourceSettings } from './mvt_single_layer_source_settings';
import { OnSourceChangeArgs } from '../../../connected_components/layer_panel/view';
import { OnSourceChangeArgs } from '../source';
import { MVTFieldDescriptor } from '../../../../common/descriptor_types';
interface Props {

View file

@ -14,12 +14,17 @@ import { GeoJsonProperties } from 'geojson';
import { copyPersistentState } from '../../reducers/copy_persistent_state';
import { IField } from '../fields/field';
import { FieldFormatter, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants';
import { FieldFormatter, LAYER_TYPE, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants';
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';
export type OnSourceChangeArgs = {
propName: string;
value: unknown;
newLayerType?: LAYER_TYPE;
};
export type SourceEditorArgs = {
onChange: (...args: OnSourceChangeArgs[]) => void;
currentLayerType?: string;

View file

@ -6,10 +6,16 @@
*/
import React from 'react';
import { EuiFormRow, EuiSwitch } from '@elastic/eui';
import { EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui';
export function GlobalFilterCheckbox({ applyGlobalQuery, label, setApplyGlobalQuery }) {
const onApplyGlobalQueryChange = (event) => {
interface Props {
applyGlobalQuery: boolean;
label: string;
setApplyGlobalQuery: (applyGlobalQuery: boolean) => void;
}
export function GlobalFilterCheckbox({ applyGlobalQuery, label, setApplyGlobalQuery }: Props) {
const onApplyGlobalQueryChange = (event: EuiSwitchEvent) => {
setApplyGlobalQuery(event.target.checked);
};

View file

@ -1,5 +1,5 @@
@import 'map_container/map_container';
@import 'layer_panel/index';
@import 'edit_layer_panel/index';
@import 'right_side_controls/index';
@import 'toolbar_overlay/index';
@import 'mb_map/tooltip_control/features_tooltip/index';

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LayerPanel is rendered 1`] = `
exports[`EditLayerPanel is rendered 1`] = `
<Provider
services={
Object {
@ -90,7 +90,6 @@ exports[`LayerPanel is rendered 1`] = `
<div
className="mapLayerPanel__bodyOverflow"
>
<LayerErrors />
<LayerSettings
layer={
Object {
@ -98,6 +97,7 @@ exports[`LayerPanel is rendered 1`] = `
"getId": [Function],
"getImmutableSourceProperties": [Function],
"getLayerTypeIconName": [Function],
"hasErrors": [Function],
"renderSourceSettingsEditor": [Function],
"showJoinEditor": [Function],
"supportsElasticsearchFilters": [Function],
@ -116,6 +116,7 @@ exports[`LayerPanel is rendered 1`] = `
"getId": [Function],
"getImmutableSourceProperties": [Function],
"getLayerTypeIconName": [Function],
"hasErrors": [Function],
"renderSourceSettingsEditor": [Function],
"showJoinEditor": [Function],
"supportsElasticsearchFilters": [Function],
@ -140,4 +141,4 @@ exports[`LayerPanel is rendered 1`] = `
</Provider>
`;
exports[`LayerPanel should render empty panel when selectedLayer is null 1`] = `""`;
exports[`EditLayerPanel should render empty panel when selectedLayer is null 1`] = `""`;

View file

@ -29,12 +29,6 @@ jest.mock('./flyout_footer', () => ({
},
}));
jest.mock('./layer_errors', () => ({
LayerErrors: () => {
return <div>mockLayerErrors</div>;
},
}));
jest.mock('./layer_settings', () => ({
LayerSettings: () => {
return <div>mockLayerSettings</div>;
@ -53,11 +47,11 @@ jest.mock('../../kibana_services', () => {
});
import React from 'react';
import { shallowWithIntl } from '@kbn/test/jest';
import { shallow } from 'enzyme';
import { ILayer } from '../../classes/layers/layer';
import { EditLayerPanel } from './edit_layer_panel';
import { LayerPanel } from './view';
const mockLayer = {
const mockLayer = ({
getId: () => {
return '1';
},
@ -79,7 +73,10 @@ const mockLayer = {
renderSourceSettingsEditor: () => {
return <div>mockSourceSettings</div>;
},
};
hasErrors: () => {
return false;
},
} as unknown) as ILayer;
const defaultProps = {
selectedLayer: mockLayer,
@ -87,9 +84,9 @@ const defaultProps = {
updateSourceProp: () => {},
};
describe('LayerPanel', () => {
describe('EditLayerPanel', () => {
test('is rendered', async () => {
const component = shallowWithIntl(<LayerPanel {...defaultProps} />);
const component = shallow(<EditLayerPanel {...defaultProps} />);
// Ensure all promises resolve
await new Promise((resolve) => process.nextTick(resolve));
@ -100,7 +97,7 @@ describe('LayerPanel', () => {
});
test('should render empty panel when selectedLayer is null', async () => {
const component = shallowWithIntl(<LayerPanel {...defaultProps} selectedLayer={undefined} />);
const component = shallow(<EditLayerPanel {...defaultProps} selectedLayer={undefined} />);
// Ensure all promises resolve
await new Promise((resolve) => process.nextTick(resolve));

View file

@ -5,15 +5,10 @@
* 2.0.
*/
import React, { Fragment } from 'react';
import React, { Component, Fragment } from 'react';
import { FilterEditor } from './filter_editor';
import { JoinEditor } from './join_editor';
import { FlyoutFooter } from './flyout_footer';
import { LayerErrors } from './layer_errors';
import { LayerSettings } from './layer_settings';
import { StyleSettings } from './style_settings';
import {
EuiCallOut,
EuiIcon,
EuiFlexItem,
EuiTitle,
@ -26,20 +21,47 @@ import {
EuiText,
EuiLink,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FilterEditor } from './filter_editor';
import { JoinEditor, JoinField } from './join_editor';
import { FlyoutFooter } from './flyout_footer';
import { LayerSettings } from './layer_settings';
import { StyleSettings } from './style_settings';
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';
import { LAYER_TYPE } from '../../../common/constants';
import { getData, getCore } from '../../kibana_services';
import { ILayer } from '../../classes/layers/layer';
import { IVectorLayer } from '../../classes/layers/vector_layer';
import { ImmutableSourceProperty, OnSourceChangeArgs } from '../../classes/sources/source';
import { IField } from '../../classes/fields/field';
const localStorage = new Storage(window.localStorage);
export class LayerPanel extends React.Component {
state = {
export interface Props {
selectedLayer?: ILayer;
updateSourceProp: (
layerId: string,
propName: string,
value: unknown,
newLayerType?: LAYER_TYPE
) => void;
}
interface State {
displayName: string;
immutableSourceProps: ImmutableSourceProperty[];
leftJoinFields: JoinField[];
supportsFitToBounds: boolean;
}
export class EditLayerPanel extends Component<Props, State> {
private _isMounted = false;
state: State = {
displayName: '',
immutableSourceProps: [],
leftJoinFields: null,
leftJoinFields: [],
supportsFitToBounds: false,
};
@ -89,14 +111,19 @@ export class LayerPanel extends React.Component {
};
async _loadLeftJoinFields() {
if (!this.props.selectedLayer || !this.props.selectedLayer.showJoinEditor()) {
if (
!this.props.selectedLayer ||
!this.props.selectedLayer.showJoinEditor() ||
(this.props.selectedLayer as IVectorLayer).getLeftJoinFields === undefined
) {
return;
}
let leftJoinFields;
let leftJoinFields: JoinField[] = [];
try {
const leftFieldsInstances = await this.props.selectedLayer.getLeftJoinFields();
const leftFieldPromises = leftFieldsInstances.map(async (field) => {
const leftFieldsInstances = await (this.props
.selectedLayer as IVectorLayer).getLeftJoinFields();
const leftFieldPromises = leftFieldsInstances.map(async (field: IField) => {
return {
name: field.getName(),
label: await field.getLabel(),
@ -104,22 +131,42 @@ export class LayerPanel extends React.Component {
});
leftJoinFields = await Promise.all(leftFieldPromises);
} catch (error) {
leftJoinFields = [];
// ignore exceptions getting fields, will bubble up in layer errors panel
}
if (this._isMounted) {
this.setState({ leftJoinFields });
}
}
_onSourceChange = (...args) => {
_onSourceChange = (...args: OnSourceChangeArgs[]) => {
for (let i = 0; i < args.length; i++) {
const { propName, value, newLayerType } = args[i];
this.props.updateSourceProp(this.props.selectedLayer.getId(), propName, value, newLayerType);
this.props.updateSourceProp(this.props.selectedLayer!.getId(), propName, value, newLayerType);
}
};
_renderLayerErrors() {
if (!this.props.selectedLayer || !this.props.selectedLayer.hasErrors()) {
return null;
}
return (
<Fragment>
<EuiCallOut
color="warning"
title={i18n.translate('xpack.maps.layerPanel.settingsPanel.unableToLoadTitle', {
defaultMessage: 'Unable to load layer',
})}
>
<p data-test-subj="layerErrorMessage">{this.props.selectedLayer.getErrors()}</p>
</EuiCallOut>
<EuiSpacer size="m" />
</Fragment>
);
}
_renderFilterSection() {
if (!this.props.selectedLayer.supportsElasticsearchFilters()) {
if (!this.props.selectedLayer || !this.props.selectedLayer.supportsElasticsearchFilters()) {
return null;
}
@ -134,7 +181,7 @@ export class LayerPanel extends React.Component {
}
_renderJoinSection() {
if (!this.props.selectedLayer.showJoinEditor()) {
if (!this.props.selectedLayer || !this.props.selectedLayer.showJoinEditor()) {
return null;
}
@ -153,29 +200,29 @@ export class LayerPanel extends React.Component {
}
_renderSourceProperties() {
return this.state.immutableSourceProps.map(({ label, value, link }) => {
function renderValue() {
if (link) {
return (
<EuiLink href={link} target="_blank">
{value}
</EuiLink>
);
return this.state.immutableSourceProps.map(
({ label, value, link }: ImmutableSourceProperty) => {
function renderValue() {
if (link) {
return (
<EuiLink href={link} target="_blank">
{value}
</EuiLink>
);
}
return <span>{value}</span>;
}
return <span>{value}</span>;
return (
<p key={label} className="mapLayerPanel__sourceDetail">
<strong>{label}</strong> {renderValue()}
</p>
);
}
return (
<p key={label} className="mapLayerPanel__sourceDetail">
<strong>{label}</strong> {renderValue()}
</p>
);
});
);
}
render() {
const { selectedLayer } = this.props;
if (!selectedLayer) {
if (!this.props.selectedLayer) {
return null;
}
@ -192,7 +239,7 @@ export class LayerPanel extends React.Component {
<EuiFlyoutHeader hasBorder className="mapLayerPanel__header">
<EuiFlexGroup responsive={false} alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiIcon type={selectedLayer.getLayerTypeIconName()} />
<EuiIcon type={this.props.selectedLayer.getLayerTypeIconName()} />
</EuiFlexItem>
<EuiFlexItem>
<EuiTitle size="s">
@ -218,10 +265,10 @@ export class LayerPanel extends React.Component {
<div className="mapLayerPanel__body">
<div className="mapLayerPanel__bodyOverflow">
<LayerErrors />
{this._renderLayerErrors()}
<LayerSettings
layer={selectedLayer}
layer={this.props.selectedLayer}
supportsFitToBounds={this.state.supportsFitToBounds}
/>

View file

@ -21,12 +21,28 @@ import {
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import type { IndexPattern, Query } from 'src/plugins/data/public';
import { APP_ID } from '../../../../common/constants';
import { getIndexPatternService, getData } from '../../../kibana_services';
import { GlobalFilterCheckbox } from '../../../components/global_filter_checkbox';
import { GlobalTimeCheckbox } from '../../../components/global_time_checkbox';
import { ILayer } from '../../../classes/layers/layer';
export class FilterEditor extends Component {
state = {
export interface Props {
layer: ILayer;
setLayerQuery: (id: string, query: Query) => void;
updateSourceProp: (layerId: string, propName: string, value: unknown) => void;
}
interface State {
isPopoverOpen: boolean;
indexPatterns: IndexPattern[];
isSourceTimeAware: boolean;
}
export class FilterEditor extends Component<Props, State> {
private _isMounted = false;
state: State = {
isPopoverOpen: false,
indexPatterns: [],
isSourceTimeAware: false,
@ -45,7 +61,7 @@ export class FilterEditor extends Component {
async _loadIndexPatterns() {
// Filter only effects source so only load source indices.
const indexPatternIds = this.props.layer.getSource().getIndexPatternIds();
const indexPatterns = [];
const indexPatterns: IndexPattern[] = [];
const getIndexPatternPromises = indexPatternIds.map(async (indexPatternId) => {
try {
const indexPattern = await getIndexPatternService().get(indexPatternId);
@ -81,16 +97,19 @@ export class FilterEditor extends Component {
this.setState({ isPopoverOpen: false });
};
_onQueryChange = ({ query }) => {
_onQueryChange = ({ query }: { query?: Query }) => {
if (!query) {
return;
}
this.props.setLayerQuery(this.props.layer.getId(), query);
this._close();
};
_onApplyGlobalQueryChange = (applyGlobalQuery) => {
_onApplyGlobalQueryChange = (applyGlobalQuery: boolean) => {
this.props.updateSourceProp(this.props.layer.getId(), 'applyGlobalQuery', applyGlobalQuery);
};
_onApplyGlobalTimeChange = (applyGlobalTime) => {
_onApplyGlobalTimeChange = (applyGlobalTime: boolean) => {
this.props.updateSourceProp(this.props.layer.getId(), 'applyGlobalTime', applyGlobalTime);
};
@ -109,6 +128,7 @@ export class FilterEditor extends Component {
>
<div className="mapFilterEditor" data-test-subj="mapFilterEditor">
<SearchBar
appName={APP_ID}
showFilterBar={false}
showDatePicker={false}
showQueryInput={true}

View file

@ -5,23 +5,28 @@
* 2.0.
*/
import { AnyAction } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { connect } from 'react-redux';
import type { Query } from 'src/plugins/data/public';
import { FilterEditor } from './filter_editor';
import { getSelectedLayer } from '../../../selectors/map_selectors';
import { setLayerQuery, updateSourceProp } from '../../../actions';
import { MapStoreState } from '../../../reducers/store';
function mapStateToProps(state = {}) {
function mapStateToProps(state: MapStoreState) {
return {
layer: getSelectedLayer(state),
layer: getSelectedLayer(state)!,
};
}
function mapDispatchToProps(dispatch) {
function mapDispatchToProps(dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) {
return {
setLayerQuery: (layerId, query) => {
setLayerQuery: (layerId: string, query: Query) => {
dispatch(setLayerQuery(layerId, query));
},
updateSourceProp: (id, propName, value) => dispatch(updateSourceProp(id, propName, value)),
updateSourceProp: (id: string, propName: string, value: unknown) =>
dispatch(updateSourceProp(id, propName, value)),
};
}

View file

@ -10,12 +10,19 @@ import React from 'react';
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiButtonEmpty } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export interface Props {
cancelLayerPanel: () => void;
saveLayerEdits: () => void;
removeLayer: () => void;
hasStateChanged: boolean;
}
export const FlyoutFooter = ({
cancelLayerPanel,
saveLayerEdits,
removeLayer,
hasStateChanged,
}) => {
}: Props) => {
const removeBtn = (
<EuiFlexItem grow={false}>
<EuiButtonEmpty

View file

@ -5,8 +5,10 @@
* 2.0.
*/
import { AnyAction } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { connect } from 'react-redux';
import { FlyoutFooter } from './view';
import { FlyoutFooter } from './flyout_footer';
import { FLYOUT_STATE } from '../../../reducers/ui';
import { hasDirtyState } from '../../../selectors/map_selectors';
@ -16,14 +18,15 @@ import {
removeTrackedLayerStateForSelectedLayer,
updateFlyout,
} from '../../../actions';
import { MapStoreState } from '../../../reducers/store';
function mapStateToProps(state = {}) {
function mapStateToProps(state: MapStoreState) {
return {
hasStateChanged: hasDirtyState(state),
};
}
const mapDispatchToProps = (dispatch) => {
const mapDispatchToProps = (dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) => {
return {
cancelLayerPanel: () => {
dispatch(updateFlyout(FLYOUT_STATE.NONE));

View file

@ -5,12 +5,16 @@
* 2.0.
*/
import { AnyAction } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { connect } from 'react-redux';
import { LayerPanel } from './view';
import { EditLayerPanel } from './edit_layer_panel';
import { LAYER_TYPE } from '../../../common/constants';
import { getSelectedLayer } from '../../selectors/map_selectors';
import { updateSourceProp } from '../../actions';
import { MapStoreState } from '../../reducers/store';
function mapStateToProps(state = {}) {
function mapStateToProps(state: MapStoreState) {
const selectedLayer = getSelectedLayer(state);
return {
key: selectedLayer ? `${selectedLayer.getId()}${selectedLayer.showJoinEditor()}` : '',
@ -18,12 +22,12 @@ function mapStateToProps(state = {}) {
};
}
function mapDispatchToProps(dispatch) {
function mapDispatchToProps(dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) {
return {
updateSourceProp: (id, propName, value, newLayerType) =>
updateSourceProp: (id: string, propName: string, value: unknown, newLayerType?: LAYER_TYPE) =>
dispatch(updateSourceProp(id, propName, value, newLayerType)),
};
}
const connectedLayerPanel = connect(mapStateToProps, mapDispatchToProps)(LayerPanel);
export { connectedLayerPanel as LayerPanel };
const connected = connect(mapStateToProps, mapDispatchToProps)(EditLayerPanel);
export { connected as EditLayerPanel };

View file

@ -31,3 +31,4 @@ function mapDispatchToProps(dispatch: ThunkDispatch<MapStoreState, void, AnyActi
const connectedJoinEditor = connect(mapStateToProps, mapDispatchToProps)(JoinEditor);
export { connectedJoinEditor as JoinEditor };
export { JoinField } from './join_editor';

View file

@ -23,14 +23,18 @@ import { i18n } from '@kbn/i18n';
import { Join } from './resources/join';
import { ILayer } from '../../../classes/layers/layer';
import { JoinDescriptor } from '../../../../common/descriptor_types';
import { IField } from '../../../classes/fields/field';
import { SOURCE_TYPES } from '../../../../common/constants';
export interface JoinField {
label: string;
name: string;
}
export interface Props {
joins: JoinDescriptor[];
layer: ILayer;
layerDisplayName: string;
leftJoinFields: IField[];
leftJoinFields: JoinField[];
onChange: (layer: ILayer, joins: JoinDescriptor[]) => void;
}

View file

@ -5,20 +5,24 @@
* 2.0.
*/
import { AnyAction } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { connect } from 'react-redux';
import { StyleSettings } from './style_settings';
import { getSelectedLayer } from '../../../selectors/map_selectors';
import { updateLayerStyleForSelectedLayer } from '../../../actions';
import { MapStoreState } from '../../../reducers/store';
import { StyleDescriptor } from '../../../../common/descriptor_types';
function mapStateToProps(state = {}) {
function mapStateToProps(state: MapStoreState) {
return {
layer: getSelectedLayer(state),
layer: getSelectedLayer(state)!,
};
}
function mapDispatchToProps(dispatch) {
function mapDispatchToProps(dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) {
return {
updateStyleDescriptor: (styleDescriptor) => {
updateStyleDescriptor: (styleDescriptor: StyleDescriptor) => {
dispatch(updateLayerStyleForSelectedLayer(styleDescriptor));
},
};

View file

@ -10,8 +10,15 @@ import React, { Fragment } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiPanel, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { StyleDescriptor } from '../../../../common/descriptor_types';
import { ILayer } from '../../../classes/layers/layer';
export function StyleSettings({ layer, updateStyleDescriptor }) {
export interface Props {
layer: ILayer;
updateStyleDescriptor: (styleDescriptor: StyleDescriptor) => void;
}
export function StyleSettings({ layer, updateStyleDescriptor }: Props) {
const settingsEditor = layer.renderStyleEditor(updateStyleDescriptor);
if (!settingsEditor) {

View file

@ -1,21 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Should render errors when layer has errors 1`] = `
<Fragment>
<EuiCallOut
color="warning"
title="Unable to load layer"
>
<p
data-test-subj="layerErrorMessage"
>
simulated layer error
</p>
</EuiCallOut>
<EuiSpacer
size="m"
/>
</Fragment>
`;
exports[`should render nothing when layer has no errors 1`] = `""`;

View file

@ -1,19 +0,0 @@
/*
* 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 { connect } from 'react-redux';
import { LayerErrors } from './layer_errors';
import { getSelectedLayer } from '../../../selectors/map_selectors';
function mapStateToProps(state = {}) {
return {
layer: getSelectedLayer(state),
};
}
const connectedLayerErrors = connect(mapStateToProps, null)(LayerErrors);
export { connectedLayerErrors as LayerErrors };

View file

@ -1,30 +0,0 @@
/*
* 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, { Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
export function LayerErrors({ layer }) {
if (!layer.hasErrors()) {
return null;
}
return (
<Fragment>
<EuiCallOut
color="warning"
title={i18n.translate('xpack.maps.layerPanel.settingsPanel.unableToLoadTitle', {
defaultMessage: 'Unable to load layer',
})}
>
<p data-test-subj="layerErrorMessage">{layer.getErrors()}</p>
</EuiCallOut>
<EuiSpacer size="m" />
</Fragment>
);
}

View file

@ -1,36 +0,0 @@
/*
* 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 { LayerErrors } from './layer_errors';
test('Should render errors when layer has errors', () => {
const mockLayer = {
hasErrors: () => {
return true;
},
getErrors: () => {
return 'simulated layer error';
},
};
const component = shallow(<LayerErrors layer={mockLayer} />);
expect(component).toMatchSnapshot();
});
test('should render nothing when layer has no errors', () => {
const mockLayer = {
hasErrors: () => {
return false;
},
};
const component = shallow(<LayerErrors layer={mockLayer} />);
expect(component).toMatchSnapshot();
});

View file

@ -1,16 +0,0 @@
/*
* 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.
*/
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import { LAYER_TYPE } from '../../../common/constants';
export type OnSourceChangeArgs = {
propName: string;
value: unknown;
newLayerType?: LAYER_TYPE;
};

View file

@ -16,8 +16,7 @@ import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
import { MBMap } from '../mb_map';
import { RightSideControls } from '../right_side_controls';
import { ToolbarOverlay } from '../toolbar_overlay';
// @ts-expect-error
import { LayerPanel } from '../layer_panel';
import { EditLayerPanel } from '../edit_layer_panel';
import { AddLayerPanel } from '../add_layer_panel';
import { ExitFullScreenButton } from '../../../../../../src/plugins/kibana_react/public';
import { getIndexPatternsFromIds } from '../../index_pattern_util';
@ -222,7 +221,7 @@ export class MapContainer extends Component<Props, State> {
if (flyoutDisplay === FLYOUT_STATE.ADD_LAYER_WIZARD) {
flyoutPanel = <AddLayerPanel />;
} else if (flyoutDisplay === FLYOUT_STATE.LAYER_PANEL) {
flyoutPanel = <LayerPanel />;
flyoutPanel = <EditLayerPanel />;
} else if (flyoutDisplay === FLYOUT_STATE.MAP_SETTINGS_PANEL) {
flyoutPanel = <MapSettingsPanel />;
}

View file

@ -42,7 +42,7 @@ import { getTopNavConfig } from '../top_nav_config';
import { MapRefreshConfig, MapQuery } from '../../../../common/descriptor_types';
import { goToSpecifiedPath } from '../../../render_app';
import { MapSavedObjectAttributes } from '../../../../common/map_saved_object_type';
import { getExistingMapPath } from '../../../../common/constants';
import { getExistingMapPath, APP_ID } from '../../../../common/constants';
import {
getInitialQuery,
getInitialRefreshConfig,
@ -355,7 +355,7 @@ export class MapApp extends React.Component<Props, State> {
return (
<TopNavMenu
setMenuMountPoint={this.props.setHeaderActionMenu}
appName="maps"
appName={APP_ID}
config={topNavConfig}
indexPatterns={this.state.indexPatterns}
filters={this.props.filters}