Skip to content

Commit

Permalink
[Maps] Introduce fields (#50044) (#51379)
Browse files Browse the repository at this point in the history
This introduces the `AbstractField` class and its implementations. Their use replace the ad-hoc object literals that were used to pass around field-level metadata in joins, metrics, styles, and tooltips.
  • Loading branch information
thomasneirynck authored Nov 22, 2019
1 parent 4c14ca6 commit 820d61d
Show file tree
Hide file tree
Showing 56 changed files with 1,116 additions and 715 deletions.
13 changes: 13 additions & 0 deletions x-pack/legacy/plugins/maps/common/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';

export const EMS_CATALOGUE_PATH = 'ems/catalogue';

Expand Down Expand Up @@ -114,3 +115,15 @@ export const METRIC_TYPE = {
SUM: 'sum',
UNIQUE_COUNT: 'cardinality',
};

export const COUNT_AGG_TYPE = METRIC_TYPE.COUNT;
export const COUNT_PROP_LABEL = i18n.translate('xpack.maps.aggs.defaultCountLabel', {
defaultMessage: 'count'
});

export const COUNT_PROP_NAME = 'doc_count';

export const STYLE_TYPE = {
'STATIC': 'STATIC',
'DYNAMIC': 'DYNAMIC'
};
4 changes: 1 addition & 3 deletions x-pack/legacy/plugins/maps/public/actions/map_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -732,9 +732,7 @@ export function clearMissingStyleProperties(layerId) {
return;
}

const dateFields = await targetLayer.getDateFields();
const numberFields = await targetLayer.getNumberFields();
const ordinalFields = [...dateFields, ...numberFields];
const ordinalFields = await targetLayer.getOrdinalFields();
const { hasChanges, nextStyleDescriptor } = style.getDescriptorWithMissingStylePropsRemoved(ordinalFields);
if (hasChanges) {
dispatch(updateLayerStyle(layerId, nextStyleDescriptor));
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

118 changes: 93 additions & 25 deletions x-pack/legacy/plugins/maps/public/components/tooltip_selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,109 @@ const reorder = (list, startIndex, endIndex) => {
return result;
};

const getProps = async field => {
return new Promise(async (resolve, reject) => {
try {
const label = await field.getLabel();
const type = await field.getDataType();
resolve({
label: label,
type: type,
name: field.getName()
});
} catch(e) {
reject(e);
}
});
};

export class TooltipSelector extends Component {

state = {
fieldProps: [],
selectedFieldProps: []
};

constructor() {
super();
this._isMounted = false;
this._previousFields = null;
this._previousSelectedTooltips = null;
}

componentDidMount() {
this._isMounted = true;
this._loadFieldProps();
this._loadTooltipFieldProps();
}

componentWillUnmount() {
this._isMounted = false;
}

componentDidUpdate() {
this._loadTooltipFieldProps();
this._loadFieldProps();
}

async _loadTooltipFieldProps() {

if (!this.props.tooltipFields || this.props.tooltipFields === this._previousSelectedTooltips) {
return;
}

this._previousSelectedTooltips = this.props.tooltipFields;
const selectedProps = this.props.tooltipFields.map(getProps);
const selectedFieldProps = await Promise.all(selectedProps);
if (this._isMounted) {
this.setState({ selectedFieldProps });
}

}

async _loadFieldProps() {

if (!this.props.fields || this.props.fields === this._previousFields) {
return;
}

this._previousFields = this.props.fields;
const props = this.props.fields.map(getProps);
const fieldProps = await Promise.all(props);
if (this._isMounted) {
this.setState({ fieldProps });
}

}

_getPropertyLabel = (propertyName) => {
if (!this.props.fields) {
if (!this.state.fieldProps.length) {
return propertyName;
}

const field = this.props.fields.find(field => {
const prop = this.state.fieldProps.find((field) => {
return field.name === propertyName;
});
return prop.label ? prop.label : propertyName;
}

return field && field.label
? field.label
: propertyName;
_getTooltipProperties() {
return this.props.tooltipFields.map(field => field.getName());
}

_onAdd = (properties) => {
if (!this.props.tooltipProperties) {
if (!this.props.tooltipFields) {
this.props.onChange([...properties]);
} else {
this.props.onChange([...this.props.tooltipProperties, ...properties]);
const existingProperties = this._getTooltipProperties();
this.props.onChange([...existingProperties, ...properties]);
}
}

_removeProperty = (index) => {
if (!this.props.tooltipProperties) {
if (!this.props.tooltipFields) {
this.props.onChange([]);
} else {
const tooltipProperties = [...this.props.tooltipProperties];
const tooltipProperties = this._getTooltipProperties();
tooltipProperties.splice(index, 1);
this.props.onChange(tooltipProperties);
}
Expand All @@ -70,24 +144,24 @@ export class TooltipSelector extends Component {
return;
}

this.props.onChange(reorder(this.props.tooltipProperties, source.index, destination.index));
this.props.onChange(reorder(this._getTooltipProperties(), source.index, destination.index));
};

_renderProperties() {
if (!this.props.tooltipProperties) {
if (!this.state.selectedFieldProps.length) {
return null;
}

return (
<EuiDragDropContext onDragEnd={this._onDragEnd}>
<EuiDroppable droppableId="mapLayerTOC" spacing="none">
{(provided, snapshot) => (
this.props.tooltipProperties.map((propertyName, idx) => (
this.state.selectedFieldProps.map((field, idx) => (
<EuiDraggable
spacing="none"
key={propertyName}
key={field.name}
index={idx}
draggableId={propertyName}
draggableId={field.name}
customDragHandle={true}
disableInteractiveElementBlocking // Allows button to be drag handle
>
Expand All @@ -99,7 +173,7 @@ export class TooltipSelector extends Component {
})}
>
<EuiText className="mapTooltipSelector__propertyContent" size="s">
{this._getPropertyLabel(propertyName)}
{this._getPropertyLabel(field.name)}
</EuiText>
<div className="mapTooltipSelector__propertyIcons">
<EuiButtonIcon
Expand Down Expand Up @@ -137,13 +211,6 @@ export class TooltipSelector extends Component {
}

render() {

const selectedFields = this.props.tooltipProperties
? this.props.tooltipProperties.map(propertyName => {
return { name: propertyName };
})
: [];

return (
<div>
<EuiTitle size="xxs">
Expand All @@ -160,11 +227,12 @@ export class TooltipSelector extends Component {
<EuiTextAlign textAlign="center">
<AddTooltipFieldPopover
onAdd={this._onAdd}
fields={this.props.fields}
selectedFields={selectedFields}
fields={this.state.fieldProps}
selectedFields={this.state.selectedFieldProps}
/>
</EuiTextAlign>
</div>
);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,59 @@ import { shallow } from 'enzyme';

import { TooltipSelector } from './tooltip_selector';


class MockField {
constructor({ name, label, type }) {
this._name = name;
this._label = label;
this._type = type;
}

getName() {
return this._name;
}

async getLabel() {
return this._label || 'foobar_label';
}

async getDataType() {
return this._type || 'foobar_type';
}
}

const defaultProps = {
tooltipProperties: ['iso2'],
tooltipFields: [new MockField({ name: 'iso2' })],
onChange: (()=>{}),
fields: [
{
new MockField({
name: 'iso2',
label: 'ISO 3166-1 alpha-2 code',
type: 'string'
},
{
}),
new MockField({
name: 'iso3',
type: 'string'
},
})
]
};

describe('TooltipSelector', () => {

test('should render component', async () => {

const component = shallow(
<TooltipSelector
{...defaultProps}
/>
);

expect(component)
.toMatchSnapshot();
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
// Ensure the state changes are reflected
component.update();
expect(component).toMatchSnapshot();

});

});
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,14 @@ export class Join extends Component {
async _loadLeftFields() {
let leftFields;
try {
leftFields = await this.props.layer.getLeftJoinFields();
const leftFieldsInstances = await this.props.layer.getLeftJoinFields();
const leftFieldPromises = leftFieldsInstances.map(async (field) => {
return {
name: field.getName(),
label: await field.getLabel()
};
});
leftFields = await Promise.all(leftFieldPromises);
} catch (error) {
leftFields = [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export class FeatureProperties extends React.Component {
}

const rows = this.state.properties.map(tooltipProperty => {

const label = tooltipProperty.getPropertyName();
return (
<tr key={label}>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ export class TOCEntry extends React.Component {
return null;
}

const tocDetails = this.props.layer.getLegendDetails();
const tocDetails = this.props.layer.renderLegendDetails();
if (!tocDetails) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const LAYER_ID = '1';

const mockLayer = {
getId: () => { return LAYER_ID; },
getLegendDetails: () => { return (<div>TOC details mock</div>); },
renderLegendDetails: () => { return (<div>TOC details mock</div>); },
getDisplayName: () => { return 'layer 1'; },
isVisible: () => { return true; },
showAtZoomLevel: () => { return true; },
Expand Down
Loading

0 comments on commit 820d61d

Please sign in to comment.