Skip to content

Commit

Permalink
Parameter Mapping UI (1/2)
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrieldutra committed Feb 18, 2020
1 parent b70f0fa commit 9cf3965
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 60 deletions.
133 changes: 124 additions & 9 deletions client/app/components/EditParameterSettingsDialog.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { includes, words, capitalize, clone, isNull } from "lodash";
import React, { useState, useEffect } from "react";
import { includes, words, capitalize, clone, isNull, keys, values } from "lodash";
import React, { useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";
import Checkbox from "antd/lib/checkbox";
import Modal from "antd/lib/modal";
import Form from "antd/lib/form";
import Button from "antd/lib/button";
import Select from "antd/lib/select";
import Icon from "antd/lib/icon";
import Input from "antd/lib/input";
import Table from "antd/lib/table";
import Divider from "antd/lib/divider";
import Tooltip from "antd/lib/tooltip";
import Popover from "antd/lib/popover";
import Radio from "antd/lib/radio";
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
import QuerySelector from "@/components/QuerySelector";
import ParameterMappingEditor from "@/components//ParameterMappingEditor";
import { Query } from "@/services/query";
import { QueryBasedParameterMappingType } from "@/services/parameters/QueryBasedDropdownParameter";

const { Option } = Select;
const formItemProps = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
Expand Down Expand Up @@ -66,20 +73,85 @@ NameInput.propTypes = {
type: PropTypes.string.isRequired,
};

// TODO: put this component in its own file
function QueryBasedParamMappingEditor({ parameter, mappingType, staticValue, searchAvailable, onChange }) {
const [showPopover, setShowPopover] = useState(false);

let currentState = "Undefined";
if (mappingType === QueryBasedParameterMappingType.DROPDOWN_SEARCH) {
currentState = "Dropdown Search";
} else if (mappingType === QueryBasedParameterMappingType.STATIC) {
currentState = `Static value: ${staticValue}`;
}
return (
<>
{currentState}
<Popover
placement="left"
trigger="click"
content={
<ParameterMappingEditor header="Edit Parameter Source" onCancel={() => setShowPopover(false)}>
<Form>
<Form.Item label="Source" {...formItemProps}>
<Radio.Group value={mappingType}>
<Radio
className="radio"
value={QueryBasedParameterMappingType.DROPDOWN_SEARCH}
disabled={!searchAvailable}>
Dropdown Search{" "}
{!searchAvailable && (
<Tooltip title="There is already a parameter mapped with the Dropdown Search type.">
<Icon type="question-circle" theme="filled" />
</Tooltip>
)}
</Radio>
<Radio className="radio" value={QueryBasedParameterMappingType.STATIC}>
Static Value
</Radio>
</Radio.Group>
</Form.Item>
</Form>
</ParameterMappingEditor>
}
visible={showPopover}
onVisibleChange={setShowPopover}>
<Button className="m-l-5" size="small" type="dashed">
<Icon type="edit" />
</Button>
</Popover>
</>
);
}

QueryBasedParamMappingEditor.propTypes = {
parameter: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
mappingType: PropTypes.oneOf(values(QueryBasedParameterMappingType)),
staticValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
searchAvailable: PropTypes.bool,
onChange: PropTypes.func,
};

QueryBasedParamMappingEditor.defaultProps = {
mappingType: QueryBasedParameterMappingType.UNDEFINED,
staticValue: undefined,
searchAvailable: false,
onChance: () => {},
};

function EditParameterSettingsDialog(props) {
const [param, setParam] = useState(clone(props.parameter));
const [isNameValid, setIsNameValid] = useState(true);
const [initialQuery, setInitialQuery] = useState();
const [paramQuery, setParamQuery] = useState();

const isNew = !props.parameter.name;

// fetch query by id
const initialQueryId = useRef(props.parameter.queryId);
useEffect(() => {
const queryId = props.parameter.queryId;
if (queryId) {
Query.get({ id: queryId }).then(setInitialQuery);
if (initialQueryId.current) {
Query.get({ id: initialQueryId.current }).then(setParamQuery);
}
}, [props.parameter.queryId]);
}, []);

function isFulfilled() {
// name
Expand Down Expand Up @@ -191,12 +263,55 @@ function EditParameterSettingsDialog(props) {
{param.type === "query" && (
<Form.Item label="Query" help="Select query to load dropdown values from" {...formItemProps}>
<QuerySelector
selectedQuery={initialQuery}
onChange={q => setParam({ ...param, queryId: q && q.id })}
selectedQuery={paramQuery}
onChange={q => {
if (q) {
setParamQuery(q);
setParam({ ...param, queryId: q.id });
}
}}
type="select"
/>
</Form.Item>
)}
{param.type === "query" && paramQuery && paramQuery.hasParameters() && (
<Form.Item className="m-t-15 m-b-5" label="Parameters" {...formItemProps}>
<Table
dataSource={paramQuery.getParametersDefs()}
size="middle"
pagination={false}
rowKey={(record, idx) => `row${idx}`}>
<Table.Column title="Title" key="title" render={mappingParam => mappingParam.getTitle()} />
<Table.Column
title="Keyword"
key="keyword"
className="keyword"
render={mappingParam => <code>{`{{ ${mappingParam.name} }}`}</code>}
/>
<Table.Column
title="Value Source"
key="source"
render={mappingParam => {
const mappingProps = {
parameter: mappingParam,
mappingType: QueryBasedParameterMappingType.UNDEFINED,
searchAvailable: !param.searchColumn,
};

// TODO: Update backend verification accordingly and avoid this
if (param.searchColumn === mappingParam.name) {
mappingProps.mappingType = QueryBasedParameterMappingType.DROPDOWN_SEARCH;
mappingProps.searchAvailable = true;
} else if (includes(keys(param.staticValues), mappingParam.name)) {
mappingProps.mappingType = QueryBasedParameterMappingType.STATIC;
mappingProps.staticValue = param.staticValues[mappingParam.name];
}
return <QueryBasedParamMappingEditor {...mappingProps} />;
}}
/>
</Table>
</Form.Item>
)}
{(param.type === "enum" || param.type === "query") && (
<Form.Item className="m-b-0" label=" " colon={false} {...formItemProps}>
<Checkbox
Expand Down
36 changes: 36 additions & 0 deletions client/app/components/ParameterMappingEditor.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react";
import PropTypes from "prop-types";

import "./ParameterMappingEditor.less";
import Button from "antd/lib/button";

export default function ParameterMappingEditor({ header, children, saveDisabled, onCancel, onSave }) {
return (
<div className="parameter-mapping-editor" data-test="EditParamMappingPopover">
{header && <header>{header}</header>}
{children}
<footer>
<Button onClick={onCancel}>Cancel</Button>
<Button onClick={onSave} disabled={saveDisabled} type="primary">
OK
</Button>
</footer>
</div>
);
}

ParameterMappingEditor.propTypes = {
header: PropTypes.node,
children: PropTypes.node,
saveDisabled: PropTypes.bool,
onSave: PropTypes.func,
onCancel: PropTypes.func,
};

ParameterMappingEditor.defaultProps = {
header: null,
children: null,
saveDisabled: false,
onSave: () => {},
onCancel: () => {},
};
37 changes: 37 additions & 0 deletions client/app/components/ParameterMappingEditor.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@import "~antd/lib/modal/style/index"; // for ant @vars

.parameter-mapping-editor {
width: 390px;

.radio {
display: block;
height: 30px;
line-height: 30px;
}

.form-item {
margin-bottom: 10px;
}

header {
padding: 0 16px 10px;
margin: 0 -16px 20px;
border-bottom: @border-width-base @border-style-base @border-color-split;
font-size: @font-size-lg;
font-weight: 500;
color: @heading-color;
display: flex;
justify-content: space-between;
}

footer {
border-top: @border-width-base @border-style-base @border-color-split;
padding: 10px 16px 0;
margin: 0 -16px;
text-align: right;

button {
margin-left: 8px;
}
}
}
22 changes: 11 additions & 11 deletions client/app/components/ParameterMappingInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ParameterValueInput from "@/components/ParameterValueInput";
import { ParameterMappingType } from "@/services/widget";
import { Parameter, cloneParameter } from "@/services/parameters";
import HelpTrigger from "@/components/HelpTrigger";
import ParameterMappingEditor from "@/components/ParameterMappingEditor";

import "./ParameterMappingInput.less";

Expand Down Expand Up @@ -325,23 +326,22 @@ class MappingEditor extends React.Component {
const { mapping, inputError } = this.state;

return (
<div className="parameter-mapping-editor" data-test="EditParamMappingPopover">
<header>
Edit Source and Value <HelpTrigger type="VALUE_SOURCE_OPTIONS" />
</header>
<ParameterMappingEditor
header={
<>
Edit Source and Value <HelpTrigger type="VALUE_SOURCE_OPTIONS" />
</>
}
saveDisabled={!!inputError}
onSave={this.save}
onCancel={this.hide}>
<ParameterMappingInput
mapping={mapping}
existingParamNames={this.props.existingParamNames}
onChange={this.onChange}
inputError={inputError}
/>
<footer>
<Button onClick={this.hide}>Cancel</Button>
<Button onClick={this.save} disabled={!!inputError} type="primary">
OK
</Button>
</footer>
</div>
</ParameterMappingEditor>
);
}

Expand Down
41 changes: 3 additions & 38 deletions client/app/components/ParameterMappingInput.less
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '~antd/lib/modal/style/index'; // for ant @vars
@import "~antd/lib/modal/style/index"; // for ant @vars

.parameters-mapping-list {
.keyword {
Expand All @@ -22,48 +22,13 @@
}
}

.parameter-mapping-editor {
width: 390px;

.radio {
display: block;
height: 30px;
line-height: 30px;
}

.form-item {
margin-bottom: 10px;
}

header {
padding: 0 16px 10px;
margin: 0 -16px 20px;
border-bottom: @border-width-base @border-style-base @border-color-split;
font-size: @font-size-lg;
font-weight: 500;
color: @heading-color;
display: flex;
justify-content: space-between;
}

footer {
border-top: @border-width-base @border-style-base @border-color-split;
padding: 10px 16px 0;
margin: 0 -16px;
text-align: right;

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

.parameter-mapping-title {
.text {
margin-right: 3px;
}

&.disabled, .fa {
&.disabled,
.fa {
color: #a4a4a4;
}

Expand Down
3 changes: 1 addition & 2 deletions client/app/components/Parameters.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { Parameter, createParameter } from "@/services/parameters";
import ParameterApplyButton from "@/components/ParameterApplyButton";
import ParameterValueInput from "@/components/ParameterValueInput";
import EditParameterSettingsDialog from "./EditParameterSettingsDialog";
import { toHuman } from "@/lib/utils";

import "./Parameters.less";

Expand Down Expand Up @@ -123,7 +122,7 @@ export default class Parameters extends React.Component {
return (
<div key={param.name} className="di-block" data-test={`ParameterName-${param.name}`}>
<div className="parameter-heading">
<label>{param.title || toHuman(param.name)}</label>
<label>{param.getTitle()}</label>
{editable && (
<button
className="btn btn-default btn-xs m-l-5"
Expand Down
5 changes: 5 additions & 0 deletions client/app/services/parameters/Parameter.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isNull, isObject, isFunction, isUndefined, isEqual, has, omit, isArray, each } from "lodash";
import { toHuman } from "@/lib/utils";

class Parameter {
constructor(parameter, parentQueryId) {
Expand Down Expand Up @@ -44,6 +45,10 @@ class Parameter {
return this.$$value;
}

getTitle() {
return this.title || toHuman(this.name);
}

isEmptyValue(value) {
return isNull(this.normalizeValue(value));
}
Expand Down
Loading

0 comments on commit 9cf3965

Please sign in to comment.