Skip to content

Commit

Permalink
[Widget Params] Split title and mapping editing
Browse files Browse the repository at this point in the history
  • Loading branch information
ranbena committed Feb 3, 2019
1 parent d92f539 commit c798550
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 66 deletions.
212 changes: 146 additions & 66 deletions client/app/components/ParameterMappingInput.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint react/no-multi-comp: 0 */

import { extend, map, includes, findIndex, find, fromPairs } from 'lodash';
import { extend, map, includes, findIndex, find, fromPairs, clone } from 'lodash';
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import Select from 'antd/lib/select';
Expand All @@ -9,6 +9,7 @@ import Popover from 'antd/lib/popover';
import Button from 'antd/lib/button';
import Icon from 'antd/lib/icon';
import Tag from 'antd/lib/tag';
import Input from 'antd/lib/input';
import { ParameterValueInput } from '@/components/ParameterValueInput';
import { ParameterMappingType } from '@/services/widget';
import { Parameter } from '@/services/query';
Expand Down Expand Up @@ -108,7 +109,7 @@ export class ParameterMappingInput extends React.Component {
<div>
<Select
className="w-100"
defaultValue={mapping.type}
value={mapping.type}
onChange={type => this.updateParamMapping(mapping, { type })}
dropdownClassName="ant-dropdown-in-bootstrap-modal"
>
Expand Down Expand Up @@ -150,7 +151,7 @@ export class ParameterMappingInput extends React.Component {
<div className="m-t-10">
<Select
className="w-100"
defaultValue={mapping.mapTo}
value={mapping.mapTo}
onChange={mapTo => this.updateParamMapping(mapping, { mapTo })}
disabled={existingParamNames.length === 0}
dropdownClassName="ant-dropdown-in-bootstrap-modal"
Expand Down Expand Up @@ -193,33 +194,12 @@ export class ParameterMappingInput extends React.Component {
}
}

renderTitleInput() {
const { mapping } = this.props;
if (mapping.type === MappingType.StaticValue) {
return null;
}
return (
<div className="m-t-10">
<label htmlFor="parameter-title">Change parameter title (leave empty to use existing):</label>
<input
id="parameter-title"
type="text"
className="form-control"
value={mapping.title}
onChange={event => this.updateParamMapping(mapping, { title: event.target.value })}
placeholder={mapping.param.title}
/>
</div>
);
}

render() {
const { mapping } = this.props;
return (
<div key={mapping.name}>
{this.renderMappingTypeSelector()}
{this.renderInputBlock()}
{this.renderTitleInput()}
</div>
);
}
Expand All @@ -244,36 +224,51 @@ class EditMapping extends React.Component {
super(props);
this.state = {
visible: false,
mapping: clone(this.props.mapping),
};
}

onVisibleChange = (visible) => {
if (visible) this.show(); else this.hide();
}

onChange = (mapping) => {
this.setState({ mapping });
}

get content() {
const { mapping, clientConfig, Query, onChange } = this.props;
const { mapping } = this.state;
const { clientConfig, Query } = this.props;

return (
<div className="editMapping">
<header>Edit parameter</header>
<ParameterMappingInput
mapping={mapping}
existingParamNames={this.props.existingParamNames}
onChange={newMapping => onChange(mapping, newMapping)}
onChange={this.onChange}
getContainerElement={() => this.wrapperRef.current}
clientConfig={clientConfig}
Query={Query}
/>
<footer>
<Button onClick={this.hide}>Close</Button>
<Button onClick={this.hide}>Cancel</Button>
<Button onClick={this.save} type="primary">OK</Button>
</footer>
</div>
);
}

save = () => {
this.props.onChange(this.props.mapping, this.state.mapping);
this.hide();
}

show = () => {
this.setState({ visible: true });
this.setState({
visible: true,
mapping: clone(this.props.mapping), // restore original state
});
}

hide = () => {
Expand All @@ -283,15 +278,89 @@ class EditMapping extends React.Component {
render() {
return (
<Popover
placement="right"
placement="left"
trigger="click"
content={this.content}
visible={this.state.visible}
onVisibleChange={this.onVisibleChange}
getPopupContainer={this.props.getContainerElement}
>
<Button size="small" type="dashed">
<Icon type="edit" theme="twoTone" />
<Icon type="edit" />
</Button>
</Popover>
);
}
}

class EditTitle extends React.Component {
static propTypes = {
mapping: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
onChange: PropTypes.func.isRequired,
getContainerElement: PropTypes.func.isRequired,
};

state = {
visible: false,
title: this.props.mapping.title,
}

onVisibleChange = (visible) => {
this.setState({
visible,
title: this.props.mapping.title, // reset title
});
}

onTitleChange = (event) => {
this.setState({ title: event.target.value });
}

get popover() {
const { param: { title: paramTitle } } = this.props.mapping;

return (
<div className="editTitle">
<Input
size="small"
value={this.state.title}
placeholder={paramTitle}
onChange={this.onTitleChange}
onPressEnter={this.save}
autoFocus
/>
<Button size="small" type="dashed" onClick={this.hide}>
<Icon type="close" />
</Button>
<Button size="small" type="dashed" onClick={this.save}>
<Icon type="check" />
</Button>
</div>
);
}

save = () => {
const newMapping = extend({}, this.props.mapping, { title: this.state.title });
this.props.onChange(newMapping);
this.hide();
}

hide = () => {
this.setState({ visible: false });
}

render() {
return (
<Popover
placement="right"
trigger="click"
content={this.popover}
visible={this.state.visible}
onVisibleChange={this.onVisibleChange}
getPopupContainer={this.props.getContainerElement}
>
<Button size="small" type="dashed">
<Icon type="edit" />
</Button>
</Popover>
);
Expand Down Expand Up @@ -363,6 +432,25 @@ export class ParameterMappingListInput extends React.Component {
return this.getStringValue(value);
}

static getSourceTypeLabel({ type, mapTo }) {
switch (type) {
case MappingType.DashboardAddNew:
case MappingType.DashboardMapToExisting:
return (
<Fragment>
Dashboard parameter{' '}
<Tag className="tag">{mapTo}</Tag>
</Fragment>
);
case MappingType.WidgetLevel:
return 'Widget parameter';
case MappingType.StaticValue:
return 'Static value';
default:
return ''; // won't happen (typescript-ftw)
}
}

updateParamMapping(oldMapping, newMapping) {
const mappings = [...this.props.mappings];
const index = findIndex(mappings, oldMapping);
Expand All @@ -388,32 +476,23 @@ export class ParameterMappingListInput extends React.Component {
rowKey={(record, idx) => `row${idx}`}
>
<Table.Column
title="Edit"
title="Title"
dataIndex="mapping"
key="edit"
key="title"
render={(mapping) => {
const existingParamsNames = existingParams
.filter(({ type }) => type === mapping.param.type) // exclude mismatching param types
.map(({ name }) => name); // keep names only

const { title, param: { title: paramTitle } } = mapping;
return (
<EditMapping
mapping={mapping}
existingParamNames={existingParamsNames}
onChange={(oldMapping, newMapping) => this.updateParamMapping(oldMapping, newMapping)}
getContainerElement={() => this.wrapperRef.current}
clientConfig={clientConfig}
Query={Query}
/>
<Fragment>
{title || paramTitle}{' '}
<EditTitle
mapping={mapping}
onChange={newMapping => this.updateParamMapping(mapping, newMapping)}
getContainerElement={() => this.wrapperRef.current}
/>
</Fragment>
);
}}
/>
<Table.Column
title="Title"
dataIndex="mapping"
key="title"
render={mapping => mapping.title || mapping.param.title}
/>
<Table.Column
title="Keyword"
dataIndex="mapping"
Expand All @@ -434,22 +513,23 @@ export class ParameterMappingListInput extends React.Component {
dataIndex="mapping"
key="source"
render={(mapping) => {
switch (mapping.type) {
case MappingType.DashboardAddNew:
case MappingType.DashboardMapToExisting:
return (
<Fragment>
Dashboard parameter{' '}
<Tag className="tag">{mapping.mapTo}</Tag>
</Fragment>
);
case MappingType.WidgetLevel:
return 'Widget parameter';
case MappingType.StaticValue:
return 'Static value';
default:
return ''; // won't happen (typescript-ftw)
}
const existingParamsNames = existingParams
.filter(({ type }) => type === mapping.param.type) // exclude mismatching param types
.map(({ name }) => name); // keep names only

return (
<Fragment>
{this.constructor.getSourceTypeLabel(mapping)}{' '}
<EditMapping
mapping={mapping}
existingParamNames={existingParamsNames}
onChange={(oldMapping, newMapping) => this.updateParamMapping(oldMapping, newMapping)}
getContainerElement={() => this.wrapperRef.current}
clientConfig={clientConfig}
Query={Query}
/>
</Fragment>
);
}}
/>
</Table>
Expand Down
14 changes: 14 additions & 0 deletions client/app/components/ParameterMappingInput.less
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,19 @@
padding: 10px 16px 0;
margin: 20px -16px 0;
text-align: right;

button {
margin-left: 8px;
}
}
}

.editTitle {
input {
width: 100px;
}

button {
margin-left: 2px;
}
}

0 comments on commit c798550

Please sign in to comment.