[Maps] split settings into layer and source panels (#33788)

* [Maps] split settings into layer and source panels

* fix SCSS import
This commit is contained in:
Nathan Reese 2019-03-26 10:00:53 -06:00 committed by GitHub
parent 2f9ad0a814
commit e247609e8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 317 additions and 54 deletions

View file

@ -82,10 +82,9 @@ exports[`LayerPanel is rendered 1`] = `
<EuiFlyoutBody
className="mapLayerPanel__body"
>
<SettingsPanel />
<EuiSpacer
size="s"
/>
<LayerErrors />
<LayerSettings />
<SourceSettings />
<EuiPanel
grow={true}
hasShadow={false}

View file

@ -1,3 +1,3 @@
@import './layer_panel';
@import './join_editor/resources/join';
@import './settings_panel/settings_panel';
@import './layer_settings/layer_settings';

View file

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

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
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

@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
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 margin="m"/>
</Fragment>
);
}

View file

@ -0,0 +1,39 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
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

@ -5,14 +5,13 @@
*/
import { connect } from 'react-redux';
import { SettingsPanel } from './settings_panel';
import { LayerSettings } from './layer_settings';
import { getSelectedLayer } from '../../../selectors/map_selectors';
import {
updateLayerLabel,
updateLayerMaxZoom,
updateLayerMinZoom,
updateLayerAlpha,
updateSourceProp,
} from '../../../actions/store_actions';
function mapStateToProps(state = {}) {
@ -33,9 +32,8 @@ function mapDispatchToProps(dispatch) {
updateMinZoom: (id, minZoom) => dispatch(updateLayerMinZoom(id, minZoom)),
updateMaxZoom: (id, maxZoom) => dispatch(updateLayerMaxZoom(id, maxZoom)),
updateAlpha: (id, alpha) => dispatch(updateLayerAlpha(id, alpha)),
updateSourceProp: (id, propName, value) => dispatch(updateSourceProp(id, propName, value)),
};
}
const connectedSettingsPanel = connect(mapStateToProps, mapDispatchToProps)(SettingsPanel);
export { connectedSettingsPanel as SettingsPanel };
const connectedLayerSettings = connect(mapStateToProps, mapDispatchToProps)(LayerSettings);
export { connectedLayerSettings as LayerSettings };

View file

@ -14,7 +14,6 @@ import {
EuiFormRow,
EuiFieldText,
EuiSpacer,
EuiCallOut
} from '@elastic/eui';
import { ValidatedRange } from '../../../shared/components/validated_range';
@ -25,7 +24,7 @@ import { ValidatedDualRange } from 'ui/validated_range';
const MIN_ZOOM = 0;
const MAX_ZOOM = 24;
export function SettingsPanel(props) {
export function LayerSettings(props) {
const onLabelChange = (event) => {
const label = event.target.value;
@ -41,34 +40,6 @@ export function SettingsPanel(props) {
props.updateAlpha(props.layerId, alpha);
};
const onSourceChange = ({ propName, value }) => {
props.updateSourceProp(props.layerId, propName, value);
};
const renderLayerErrors = () => {
if (!props.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">
{props.layer.getErrors()}
</p>
</EuiCallOut>
<EuiSpacer margin="m"/>
</Fragment>
);
};
const renderZoomSliders = () => {
return (
<EuiFormRow
@ -146,17 +117,14 @@ export function SettingsPanel(props) {
return (
<Fragment>
{renderLayerErrors()}
<EuiPanel>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="xs">
<h5>
<FormattedMessage
id="xpack.maps.layerPanel.settingsPanel.settingsTitle"
defaultMessage="Settings"
id="xpack.maps.layerPanel.layerSettingsTitle"
defaultMessage="Layer Settings"
/>
</h5>
</EuiTitle>
@ -170,9 +138,9 @@ export function SettingsPanel(props) {
{renderZoomSliders()}
{renderAlphaSlider()}
{props.layer.renderSourceSettingsEditor({ onChange: onSourceChange })}
</EuiPanel>
<EuiSpacer size="s" />
</Fragment>
);
}

View file

@ -0,0 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Should render source settings editor 1`] = `
<Fragment>
<EuiPanel
grow={true}
hasShadow={false}
paddingSize="m"
>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle
size="xs"
textTransform="none"
>
<h5>
<FormattedMessage
defaultMessage="Source Settings"
id="xpack.maps.layerPanel.sourceSettingsTitle"
values={Object {}}
/>
</h5>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
margin="m"
/>
<div>
mockSourceEditor
</div>
</EuiPanel>
<EuiSpacer
size="s"
/>
</Fragment>
`;
exports[`should render nothing when source has no editor 1`] = `""`;

View file

@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { connect } from 'react-redux';
import { SourceSettings } from './source_settings';
import { getSelectedLayer } from '../../../selectors/map_selectors';
import { updateSourceProp } from '../../../actions/store_actions';
function mapStateToProps(state = {}) {
return {
layer: getSelectedLayer(state)
};
}
function mapDispatchToProps(dispatch) {
return {
updateSourceProp: (id, propName, value) => dispatch(updateSourceProp(id, propName, value)),
};
}
const connectedSourceSettings = connect(mapStateToProps, mapDispatchToProps)(SourceSettings);
export { connectedSourceSettings as SourceSettings };

View file

@ -0,0 +1,55 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Fragment } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiTitle,
EuiPanel,
EuiSpacer,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
export function SourceSettings({ layer, updateSourceProp }) {
const onSourceChange = ({ propName, value }) => {
updateSourceProp(layer.getId(), propName, value);
};
const sourceSettingsEditor = layer.renderSourceSettingsEditor({ onChange: onSourceChange });
if (!sourceSettingsEditor) {
return null;
}
return (
<Fragment>
<EuiPanel>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="xs">
<h5>
<FormattedMessage
id="xpack.maps.layerPanel.sourceSettingsTitle"
defaultMessage="Source Settings"
/>
</h5>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer margin="m"/>
{sourceSettingsEditor}
</EuiPanel>
<EuiSpacer size="s" />
</Fragment>
);
}

View file

@ -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;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { shallow } from 'enzyme';
import { SourceSettings } from './source_settings';
test('Should render source settings editor', () => {
const mockLayer = {
renderSourceSettingsEditor: () => {
return (<div>mockSourceEditor</div>);
},
};
const component = shallow(
<SourceSettings
layer={mockLayer}
/>
);
expect(component)
.toMatchSnapshot();
});
test('should render nothing when source has no editor', () => {
const mockLayer = {
renderSourceSettingsEditor: () => {
return null;
},
};
const component = shallow(
<SourceSettings
layer={mockLayer}
/>
);
expect(component)
.toMatchSnapshot();
});

View file

@ -9,7 +9,9 @@ import React, { Fragment } from 'react';
import { StyleTabs } from './style_tabs';
import { JoinEditor } from './join_editor';
import { FlyoutFooter } from './flyout_footer';
import { SettingsPanel } from './settings_panel';
import { LayerErrors } from './layer_errors';
import { LayerSettings } from './layer_settings';
import { SourceSettings } from './source_settings';
import {
EuiButtonIcon,
EuiFlexItem,
@ -175,10 +177,17 @@ export class LayerPanel extends React.Component {
</EuiFlyoutHeader>
<EuiFlyoutBody className="mapLayerPanel__body">
<SettingsPanel/>
<EuiSpacer size="s" />
<LayerErrors/>
<LayerSettings/>
<SourceSettings/>
{this._renderJoinSection()}
<StyleTabs layer={selectedLayer}/>
</EuiFlyoutBody>
<EuiFlyoutFooter className="mapLayerPanel__footer">

View file

@ -22,9 +22,21 @@ jest.mock('./flyout_footer', () => ({
}
}));
jest.mock('./settings_panel', () => ({
SettingsPanel: () => {
return (<div>mockSettingsPanel</div>);
jest.mock('./layer_errors', () => ({
LayerErrors: () => {
return (<div>mockLayerErrors</div>);
}
}));
jest.mock('./layer_settings', () => ({
LayerSettings: () => {
return (<div>mockLayerSettings</div>);
}
}));
jest.mock('./source_settings', () => ({
SourceSettings: () => {
return (<div>mockSourceSettings</div>);
}
}));
@ -42,7 +54,7 @@ const mockLayer = {
];
},
isJoinable: () => { return true; },
getLayerTypeIconName: () => { return 'vector'; }
getLayerTypeIconName: () => { return 'vector'; },
};
const defaultProps = {