[Maps] convert ToolbarOverlay to TS (#95368)

* [Maps] convert ToolbarOverlay to TS

* remove getDerivedStateFromProps

* remove unused function

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Nathan Reese 2021-03-31 08:50:40 -06:00 committed by GitHub
parent 73109fb3ab
commit 5f487292fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 161 additions and 156 deletions

View file

@ -14,8 +14,6 @@ import { trackMapSettings } from './map_actions';
import { setSelectedLayer } from './layer_actions';
export const UPDATE_FLYOUT = 'UPDATE_FLYOUT';
export const CLOSE_SET_VIEW = 'CLOSE_SET_VIEW';
export const OPEN_SET_VIEW = 'OPEN_SET_VIEW';
export const SET_IS_LAYER_TOC_OPEN = 'SET_IS_LAYER_TOC_OPEN';
export const SET_FULL_SCREEN = 'SET_FULL_SCREEN';
export const SET_READ_ONLY = 'SET_READ_ONLY';
@ -50,16 +48,6 @@ export function openMapSettings() {
dispatch(updateFlyout(FLYOUT_STATE.MAP_SETTINGS_PANEL));
};
}
export function closeSetView() {
return {
type: CLOSE_SET_VIEW,
};
}
export function openSetView() {
return {
type: OPEN_SET_VIEW,
};
}
export function setIsLayerTOCOpen(isLayerTOCOpen: boolean) {
return {
type: SET_IS_LAYER_TOC_OPEN,

View file

@ -25,9 +25,9 @@ export class DataMappingPopover extends Component<Props, State> {
};
_togglePopover = () => {
this.setState({
isPopoverOpen: !this.state.isPopoverOpen,
});
this.setState((prevState) => ({
isPopoverOpen: !prevState.isPopoverOpen,
}));
};
_closePopover = () => {

View file

@ -98,9 +98,9 @@ export class AddTooltipFieldPopover extends Component<Props, State> {
}
_togglePopover = () => {
this.setState({
isPopoverOpen: !this.state.isPopoverOpen,
});
this.setState((prevState) => ({
isPopoverOpen: !prevState.isPopoverOpen,
}));
};
_closePopover = () => {

View file

@ -16,7 +16,6 @@ import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
import { MBMap } from '../mb_map';
// @ts-expect-error
import { WidgetOverlay } from '../widget_overlay';
// @ts-expect-error
import { ToolbarOverlay } from '../toolbar_overlay';
// @ts-expect-error
import { LayerPanel } from '../layer_panel';

View file

@ -35,7 +35,12 @@ exports[`Must zoom tools and draw filter tools 1`] = `
<Connect(ToolsControl)
geoFields={
Array [
"coordinates",
Object {
"geoFieldName": "myGeoFieldName",
"geoFieldType": "geo_point",
"indexPatternId": "1",
"indexPatternTitle": "myIndex",
},
]
}
/>

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.
*/
import { connect } from 'react-redux';
import { ToolbarOverlay } from './toolbar_overlay';
function mapStateToProps() {
return {};
}
const connectedToolbarOverlay = connect(mapStateToProps, null)(ToolbarOverlay);
export { connectedToolbarOverlay as ToolbarOverlay };

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { ToolbarOverlay } from './toolbar_overlay';

View file

@ -5,33 +5,27 @@
* 2.0.
*/
import { AnyAction } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { connect } from 'react-redux';
import { SetViewControl } from './set_view_control';
import { setGotoWithCenter, closeSetView, openSetView } from '../../../actions';
import { setGotoWithCenter } from '../../../actions';
import { getMapZoom, getMapCenter, getMapSettings } from '../../../selectors/map_selectors';
import { getIsSetViewOpen } from '../../../selectors/ui_selectors';
import { MapStoreState } from '../../../reducers/store';
function mapStateToProps(state = {}) {
function mapStateToProps(state: MapStoreState) {
return {
settings: getMapSettings(state),
isSetViewOpen: getIsSetViewOpen(state),
zoom: getMapZoom(state),
center: getMapCenter(state),
};
}
function mapDispatchToProps(dispatch) {
function mapDispatchToProps(dispatch: ThunkDispatch<MapStoreState, void, AnyAction>) {
return {
onSubmit: ({ lat, lon, zoom }) => {
dispatch(closeSetView());
onSubmit: ({ lat, lon, zoom }: { lat: number; lon: number; zoom: number }) => {
dispatch(setGotoWithCenter({ lat, lon, zoom }));
},
closeSetView: () => {
dispatch(closeSetView());
},
openSetView: () => {
dispatch(openSetView());
},
};
}

View file

@ -5,8 +5,7 @@
* 2.0.
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import React, { ChangeEvent, Component } from 'react';
import {
EuiForm,
EuiFormRow,
@ -19,57 +18,86 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { MapCenter } from '../../../../common/descriptor_types';
import { MapSettings } from '../../../reducers/map';
function getViewString(lat, lon, zoom) {
return `${lat},${lon},${zoom}`;
export interface Props {
settings: MapSettings;
zoom: number;
center: MapCenter;
onSubmit: ({ lat, lon, zoom }: { lat: number; lon: number; zoom: number }) => void;
}
export class SetViewControl extends Component {
state = {};
interface State {
isPopoverOpen: boolean;
lat: number | string;
lon: number | string;
zoom: number | string;
}
static getDerivedStateFromProps(nextProps, prevState) {
const nextView = getViewString(nextProps.center.lat, nextProps.center.lon, nextProps.zoom);
if (nextView !== prevState.prevView) {
return {
lat: nextProps.center.lat,
lon: nextProps.center.lon,
zoom: nextProps.zoom,
prevView: nextView,
};
}
return null;
}
export class SetViewControl extends Component<Props, State> {
state: State = {
isPopoverOpen: false,
lat: 0,
lon: 0,
zoom: 0,
};
_togglePopover = () => {
if (this.props.isSetViewOpen) {
this.props.closeSetView();
if (this.state.isPopoverOpen) {
this._closePopover();
return;
}
this.props.openSetView();
this.setState({
lat: this.props.center.lat,
lon: this.props.center.lon,
zoom: this.props.zoom,
isPopoverOpen: true,
});
};
_onLatChange = (evt) => {
_closePopover = () => {
this.setState({
isPopoverOpen: false,
});
};
_onLatChange = (evt: ChangeEvent<HTMLInputElement>) => {
this._onChange('lat', evt);
};
_onLonChange = (evt) => {
_onLonChange = (evt: ChangeEvent<HTMLInputElement>) => {
this._onChange('lon', evt);
};
_onZoomChange = (evt) => {
_onZoomChange = (evt: ChangeEvent<HTMLInputElement>) => {
this._onChange('zoom', evt);
};
_onChange = (name, evt) => {
_onChange = (name: 'lat' | 'lon' | 'zoom', evt: ChangeEvent<HTMLInputElement>) => {
const sanitizedValue = parseFloat(evt.target.value);
// @ts-expect-error
this.setState({
[name]: isNaN(sanitizedValue) ? '' : sanitizedValue,
});
};
_renderNumberFormRow = ({ value, min, max, onChange, label, dataTestSubj }) => {
_renderNumberFormRow = ({
value,
min,
max,
onChange,
label,
dataTestSubj,
}: {
value: string | number;
min: number;
max: number;
onChange: (evt: ChangeEvent<HTMLInputElement>) => void;
label: string;
dataTestSubj: string;
}) => {
const isInvalid = value === '' || value > max || value < min;
const error = isInvalid ? `Must be between ${min} and ${max}` : null;
return {
@ -90,7 +118,8 @@ export class SetViewControl extends Component {
_onSubmit = () => {
const { lat, lon, zoom } = this.state;
this.props.onSubmit({ lat, lon, zoom });
this._closePopover();
this.props.onSubmit({ lat: lat as number, lon: lon as number, zoom: zoom as number });
};
_renderSetViewForm() {
@ -175,23 +204,11 @@ export class SetViewControl extends Component {
})}
/>
}
isOpen={this.props.isSetViewOpen}
closePopover={this.props.closeSetView}
isOpen={this.state.isPopoverOpen}
closePopover={this._closePopover}
>
{this._renderSetViewForm()}
</EuiPopover>
);
}
}
SetViewControl.propTypes = {
isSetViewOpen: PropTypes.bool.isRequired,
zoom: PropTypes.number.isRequired,
center: PropTypes.shape({
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
}),
onSubmit: PropTypes.func.isRequired,
closeSetView: PropTypes.func.isRequired,
openSetView: PropTypes.func.isRequired,
};

View file

@ -1,53 +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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { SetViewControl } from './set_view_control';
import { ToolsControl } from './tools_control';
import { FitToData } from './fit_to_data';
export class ToolbarOverlay extends React.Component {
_renderToolsControl() {
const { addFilters, geoFields, getFilterActions, getActionContext } = this.props;
if (!addFilters || !geoFields.length) {
return null;
}
return (
<EuiFlexItem>
<ToolsControl
geoFields={geoFields}
getFilterActions={getFilterActions}
getActionContext={getActionContext}
/>
</EuiFlexItem>
);
}
render() {
return (
<EuiFlexGroup
className="mapToolbarOverlay"
responsive={false}
direction="column"
alignItems="flexStart"
gutterSize="s"
>
<EuiFlexItem>
<SetViewControl />
</EuiFlexItem>
<EuiFlexItem>
<FitToData />
</EuiFlexItem>
{this._renderToolsControl()}
</EuiFlexGroup>
);
}
}

View file

@ -7,6 +7,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { Filter } from 'src/plugins/data/public';
jest.mock('../../kibana_services', () => {
return {
@ -16,15 +17,25 @@ jest.mock('../../kibana_services', () => {
};
});
// @ts-ignore
import { ToolbarOverlay } from './toolbar_overlay';
test('Must render zoom tools', async () => {
const component = shallow(<ToolbarOverlay />);
const component = shallow(<ToolbarOverlay geoFields={[]} />);
expect(component).toMatchSnapshot();
});
test('Must zoom tools and draw filter tools', async () => {
const component = shallow(<ToolbarOverlay addFilters={() => {}} geoFields={['coordinates']} />);
const geoFieldWithIndex = {
geoFieldName: 'myGeoFieldName',
geoFieldType: 'geo_point',
indexPatternTitle: 'myIndex',
indexPatternId: '1',
};
const component = shallow(
<ToolbarOverlay
addFilters={async (filters: Filter[], actionId: string) => {}}
geoFields={[geoFieldWithIndex]}
/>
);
expect(component).toMatchSnapshot();
});

View file

@ -0,0 +1,61 @@
/*
* 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 { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { Filter } from 'src/plugins/data/public';
import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
import { SetViewControl } from './set_view_control';
import { ToolsControl } from './tools_control';
import { FitToData } from './fit_to_data';
import { GeoFieldWithIndex } from '../../components/geo_field_with_index';
export interface Props {
addFilters?: ((filters: Filter[], actionId: string) => Promise<void>) | null;
geoFields: GeoFieldWithIndex[];
getFilterActions?: () => Promise<Action[]>;
getActionContext?: () => ActionExecutionContext;
}
export function ToolbarOverlay(props: Props) {
function renderToolsControl() {
const { addFilters, geoFields, getFilterActions, getActionContext } = props;
if (!addFilters || !geoFields.length) {
return null;
}
return (
<EuiFlexItem>
<ToolsControl
geoFields={geoFields}
getFilterActions={getFilterActions}
getActionContext={getActionContext}
/>
</EuiFlexItem>
);
}
return (
<EuiFlexGroup
className="mapToolbarOverlay"
responsive={false}
direction="column"
alignItems="flexStart"
gutterSize="s"
>
<EuiFlexItem>
<SetViewControl />
</EuiFlexItem>
<EuiFlexItem>
<FitToData />
</EuiFlexItem>
{renderToolsControl()}
</EuiFlexGroup>
);
}

View file

@ -11,8 +11,6 @@ import { getMapsCapabilities } from '../kibana_services';
import {
UPDATE_FLYOUT,
CLOSE_SET_VIEW,
OPEN_SET_VIEW,
SET_IS_LAYER_TOC_OPEN,
SET_FULL_SCREEN,
SET_READ_ONLY,
@ -33,7 +31,6 @@ export type MapUiState = {
isFullScreen: boolean;
isReadOnly: boolean;
isLayerTOCOpen: boolean;
isSetViewOpen: boolean;
openTOCDetails: string[];
};
@ -44,7 +41,6 @@ export const DEFAULT_MAP_UI_STATE = {
isFullScreen: false,
isReadOnly: !getMapsCapabilities().save,
isLayerTOCOpen: DEFAULT_IS_LAYER_TOC_OPEN,
isSetViewOpen: false,
// storing TOC detail visibility outside of map.layerList because its UI state and not map rendering state.
// This also makes for easy read/write access for embeddables.
openTOCDetails: [],
@ -55,10 +51,6 @@ export function ui(state: MapUiState = DEFAULT_MAP_UI_STATE, action: any) {
switch (action.type) {
case UPDATE_FLYOUT:
return { ...state, flyoutDisplay: action.display };
case CLOSE_SET_VIEW:
return { ...state, isSetViewOpen: false };
case OPEN_SET_VIEW:
return { ...state, isSetViewOpen: true };
case SET_IS_LAYER_TOC_OPEN:
return { ...state, isLayerTOCOpen: action.isLayerTOCOpen };
case SET_FULL_SCREEN:

View file

@ -10,7 +10,6 @@ import { MapStoreState } from '../reducers/store';
import { FLYOUT_STATE } from '../reducers/ui';
export const getFlyoutDisplay = ({ ui }: MapStoreState): FLYOUT_STATE => ui.flyoutDisplay;
export const getIsSetViewOpen = ({ ui }: MapStoreState): boolean => ui.isSetViewOpen;
export const getIsLayerTOCOpen = ({ ui }: MapStoreState): boolean => ui.isLayerTOCOpen;
export const getOpenTOCDetails = ({ ui }: MapStoreState): string[] => ui.openTOCDetails;
export const getIsFullScreen = ({ ui }: MapStoreState): boolean => ui.isFullScreen;