this.updateParamMapping({ value })}
regex={mapping.param.regex}
/>
);
}
renderInputBlock() {
const { mapping } = this.props;
switch (mapping.type) {
case MappingType.DashboardAddNew:
return ["Key", "Enter a new parameter keyword", this.renderDashboardAddNew()];
case MappingType.DashboardMapToExisting:
return ["Key", "Select from a list of existing parameters", this.renderDashboardMapToExisting()];
case MappingType.StaticValue:
return ["Value", null, this.renderStaticValue()];
default:
return [];
}
}
render() {
const { inputError } = this.props;
const [label, help, input] = this.renderInputBlock();
return (
{this.renderMappingTypeSelector()}
{input}
);
}
}
class MappingEditor extends React.Component {
static propTypes = {
mapping: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
existingParamNames: PropTypes.arrayOf(PropTypes.string).isRequired,
onChange: PropTypes.func.isRequired,
};
constructor(props) {
super(props);
this.state = {
visible: false,
mapping: clone(this.props.mapping),
inputError: null,
};
}
onVisibleChange = (visible) => {
if (visible) this.show();
else this.hide();
};
onChange = (mapping) => {
let inputError = null;
if (mapping.type === MappingType.DashboardAddNew) {
if (isEmpty(mapping.mapTo)) {
inputError = "Keyword must have a value";
} else if (includes(this.props.existingParamNames, mapping.mapTo)) {
inputError = "A parameter with this name already exists";
}
}
this.setState({ mapping, inputError });
};
save = () => {
this.props.onChange(this.props.mapping, this.state.mapping);
this.hide();
};
show = () => {
this.setState({
visible: true,
mapping: clone(this.props.mapping), // restore original state
});
};
hide = () => {
this.setState({ visible: false });
};
renderContent() {
const { mapping, inputError } = this.state;
return (
);
}
render() {
const { visible, mapping } = this.state;
return (
);
}
}
class TitleEditor extends React.Component {
static propTypes = {
existingParams: PropTypes.arrayOf(PropTypes.object),
mapping: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
onChange: PropTypes.func.isRequired,
};
static defaultProps = {
existingParams: [],
};
state = {
showPopup: false,
title: "", // will be set on editing
};
onPopupVisibleChange = (showPopup) => {
this.setState({
showPopup,
title: showPopup ? this.getMappingTitle() : "",
});
};
onEditingTitleChange = (event) => {
this.setState({ title: event.target.value });
};
getMappingTitle() {
let { mapping } = this.props;
if (isString(mapping.title) && mapping.title !== "") {
return mapping.title;
}
// if mapped to dashboard, find source param and return it's title
if (mapping.type === MappingType.DashboardMapToExisting) {
const source = find(this.props.existingParams, { name: mapping.mapTo });
if (source) {
mapping = source;
}
}
return mapping.title || mapping.param.title;
}
save = () => {
const newMapping = extend({}, this.props.mapping, { title: this.state.title });
this.props.onChange(newMapping);
this.hide();
};
hide = () => {
this.setState({ showPopup: false });
};
renderPopover() {
const {
param: { title: paramTitle },
} = this.props.mapping;
return (
);
}
renderEditButton() {
const { mapping } = this.props;
if (mapping.type === MappingType.StaticValue) {
return (
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
);
}
return (
);
}
render() {
const { mapping } = this.props;
// static value are non-editable hence disabled
const disabled = mapping.type === MappingType.StaticValue;
return (
{this.getMappingTitle()}
{this.renderEditButton()}
);
}
}
export class ParameterMappingListInput extends React.Component {
static propTypes = {
mappings: PropTypes.arrayOf(PropTypes.object),
existingParams: PropTypes.arrayOf(PropTypes.object),
onChange: PropTypes.func,
};
static defaultProps = {
mappings: [],
existingParams: [],
onChange: () => {},
};
static getStringValue(value) {
// null
if (!value) {
return "";
}
// range
if (value instanceof Object && "start" in value && "end" in value) {
return `${value.start} ~ ${value.end}`;
}
// just to be safe, array or object
if (typeof value === "object") {
return map(value, (v) => this.getStringValue(v)).join(", ");
}
// rest
return value.toString();
}
static getDefaultValue(mapping, existingParams) {
const { type, mapTo, name } = mapping;
let { param } = mapping;
// if mapped to another param, swap 'em
if (type === MappingType.DashboardMapToExisting && mapTo !== name) {
const mappedTo = find(existingParams, { name: mapTo });
if (mappedTo) {
// just being safe
param = mappedTo;
}
// static type is different since it's fed param.normalizedValue
} else if (type === MappingType.StaticValue) {
param = cloneParameter(param).setValue(mapping.value);
}
let value = Parameter.getExecutionValue(param);
// in case of dynamic value display the name instead of value
if (param.hasDynamicValue) {
value = param.normalizedValue.name;
}
return this.getStringValue(value);
}
static getSourceTypeLabel({ type, mapTo }) {
switch (type) {
case MappingType.DashboardAddNew:
case MappingType.DashboardMapToExisting:
return (
Dashboard {mapTo}
);
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);
if (index >= 0) {
// This should be the only possible case, but need to handle `else` too
mappings[index] = newMapping;
} else {
mappings.push(newMapping);
}
this.props.onChange(mappings);
}
render() {
const { existingParams } = this.props; // eslint-disable-line react/prop-types
const dataSource = this.props.mappings.map((mapping) => ({ mapping }));
return (
`row${idx}`}>
(
this.updateParamMapping(mapping, newMapping)}
/>
)}
/>
{`{{ ${mapping.name} }}`}}
/>
this.constructor.getDefaultValue(mapping, this.props.existingParams)}
/>
{
const existingParamsNames = existingParams
.filter(({ type }) => type === mapping.param.type) // exclude mismatching param types
.map(({ name }) => name); // keep names only
return (
{this.constructor.getSourceTypeLabel(mapping)}{" "}
this.updateParamMapping(oldMapping, newMapping)}
/>
);
}}
/>
);
}
}