mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[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:
parent
a0ddca8b07
commit
b30d96e7f2
52 changed files with 216 additions and 227 deletions
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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';
|
||||
|
|
|
@ -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`] = `""`;
|
|
@ -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));
|
|
@ -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}
|
||||
/>
|
||||
|
|
@ -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}
|
|
@ -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)),
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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));
|
|
@ -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 };
|
|
@ -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';
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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));
|
||||
},
|
||||
};
|
|
@ -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) {
|
|
@ -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`] = `""`;
|
|
@ -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 };
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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();
|
||||
});
|
|
@ -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;
|
||||
};
|
|
@ -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 />;
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue