Merge branch 'feature/page-params' into 'release'
Page Params See merge request theappsmith/internal-tools-client!430
This commit is contained in:
commit
292aa713f9
|
|
@ -94,7 +94,8 @@
|
|||
"toposort": "^2.0.2",
|
||||
"ts-loader": "^6.0.4",
|
||||
"typescript": "^3.6.3",
|
||||
"unescape-js": "^1.1.4"
|
||||
"unescape-js": "^1.1.4",
|
||||
"url-search-params-polyfill": "^8.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import StyledDropdown from "components/editorComponents/StyledDropdown";
|
|||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||
import { getModalDropdownList } from "selectors/widgetSelectors";
|
||||
import { getActionsForCurrentPage } from "selectors/entitiesSelector";
|
||||
import { KeyValueComponent } from "components/propertyControls/KeyValueComponent";
|
||||
import { createModalAction } from "actions/widgetActions";
|
||||
|
||||
const ACTION_TRIGGER_REGEX = /^{{([\s\S]*?)\(([\s\S]*?)\)}}$/g;
|
||||
|
|
@ -22,7 +23,12 @@ const ALERT_STYLE_OPTIONS = [
|
|||
{ label: "Warning", value: "'warning'", id: "warning" },
|
||||
];
|
||||
|
||||
type ValueChangeHandler = (changeValue: string, currentValue: string) => string;
|
||||
type ValueType = string | DropdownOption[];
|
||||
|
||||
type ValueChangeHandler = (
|
||||
changeValue: ValueType,
|
||||
currentValue: string,
|
||||
) => string;
|
||||
type ActionCreatorArgumentConfig = {
|
||||
label: string;
|
||||
field: string;
|
||||
|
|
@ -31,7 +37,7 @@ type ActionCreatorArgumentConfig = {
|
|||
dispatchPayload: ReduxActionWithoutPayload;
|
||||
};
|
||||
valueChangeHandler: ValueChangeHandler;
|
||||
getSelectedValue: (value: string, returnArguments: boolean) => string;
|
||||
getSelectedValue: (value: string, returnArguments: boolean) => ValueType;
|
||||
};
|
||||
|
||||
interface ActionCreatorDropdownOption extends DropdownOption {
|
||||
|
|
@ -39,7 +45,7 @@ interface ActionCreatorDropdownOption extends DropdownOption {
|
|||
}
|
||||
|
||||
const handleTopLevelFuncUpdate: ValueChangeHandler = (
|
||||
value: string,
|
||||
value: ValueType,
|
||||
): string => {
|
||||
return value === "none" ? "" : `{{${value}()}}`;
|
||||
};
|
||||
|
|
@ -69,19 +75,46 @@ const handleApiArgSelect = (
|
|||
);
|
||||
};
|
||||
|
||||
const handlePageNameArgSelect = (changeValue: string, currentValue: string) => {
|
||||
return currentValue.replace(ACTION_TRIGGER_REGEX, `{{$1(${changeValue})}}`);
|
||||
const handlePageNameArgSelect = (
|
||||
changeValue: ValueType,
|
||||
currentValue: string,
|
||||
) => {
|
||||
const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)];
|
||||
const args = matches[0][2].split(",");
|
||||
args[0] = `${changeValue}`;
|
||||
|
||||
return currentValue.replace(
|
||||
ACTION_TRIGGER_REGEX,
|
||||
`{{$1(${args.join(",")})}}`,
|
||||
);
|
||||
};
|
||||
|
||||
const handlePageParamsArgSelect = (
|
||||
changeValue: ValueType,
|
||||
currentValue: string,
|
||||
) => {
|
||||
const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)];
|
||||
const args = matches[0][2].split(",").slice(0, 2);
|
||||
const paramsObject: Record<string, string> = {};
|
||||
(changeValue as DropdownOption[]).forEach(pageParam => {
|
||||
paramsObject[pageParam.label] = pageParam.value;
|
||||
});
|
||||
args[1] = JSON.stringify(paramsObject);
|
||||
return currentValue.replace(
|
||||
ACTION_TRIGGER_REGEX,
|
||||
`{{$1(${args.join(",")})}}`,
|
||||
);
|
||||
};
|
||||
|
||||
const handleTextArgChange = (
|
||||
changeValue: string,
|
||||
changeValue: ValueType,
|
||||
currentValue: string,
|
||||
): string => {
|
||||
return currentValue.replace(ACTION_TRIGGER_REGEX, `{{$1('${changeValue}')}}`);
|
||||
};
|
||||
|
||||
const handleAlertTextChange = (
|
||||
changeValue: string,
|
||||
changeValue: ValueType,
|
||||
currentValue: string,
|
||||
): string => {
|
||||
const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)];
|
||||
|
|
@ -95,12 +128,12 @@ const handleAlertTextChange = (
|
|||
};
|
||||
|
||||
const handleAlertTypeChange = (
|
||||
changeValue: string,
|
||||
changeValue: ValueType,
|
||||
currentValue: string,
|
||||
): string => {
|
||||
const matches = [...currentValue.matchAll(ACTION_TRIGGER_REGEX)];
|
||||
const args = matches[0][2].split(",");
|
||||
args[1] = changeValue;
|
||||
args[1] = changeValue as string;
|
||||
return currentValue.replace(
|
||||
ACTION_TRIGGER_REGEX,
|
||||
`{{$1(${args.join(",")})}}`,
|
||||
|
|
@ -131,7 +164,36 @@ const getApiArgumentValue = (
|
|||
|
||||
const getPageNameSelectedValue = (value: string) => {
|
||||
const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)];
|
||||
return matches.length ? matches[0][2] : "none";
|
||||
return matches.length ? matches[0][2].split(",")[0] : "none";
|
||||
};
|
||||
|
||||
const getPageParamsSelectedValue = (value: ValueType) => {
|
||||
const match = getPageSelectedParamsObject(value as string);
|
||||
const keyPairs: DropdownOption[] = [];
|
||||
Object.keys(match).forEach((key: string) => {
|
||||
keyPairs.push({
|
||||
label: key,
|
||||
value: match[key],
|
||||
});
|
||||
});
|
||||
return keyPairs;
|
||||
};
|
||||
|
||||
const getPageSelectedParamsObject = (value: string) => {
|
||||
const matches = [...value.matchAll(ACTION_TRIGGER_REGEX)];
|
||||
let match: Record<string, string> = {};
|
||||
|
||||
if (matches.length) {
|
||||
try {
|
||||
match = JSON.parse(
|
||||
matches[0][2].substring(
|
||||
matches[0][2].indexOf(",") + 1,
|
||||
matches[0][2].length,
|
||||
),
|
||||
);
|
||||
} catch {}
|
||||
}
|
||||
return match;
|
||||
};
|
||||
|
||||
export const getTextArgValue = (value: string) => {
|
||||
|
|
@ -179,7 +241,7 @@ export const PropertyPaneActionDropdownOptions: ActionCreatorDropdownOption[] =
|
|||
label: "onSuccess",
|
||||
field: "ACTION_SELECTOR_FIELD",
|
||||
valueChangeHandler: (changeValue, currentValue) =>
|
||||
handleApiArgSelect(changeValue, currentValue, "onSuccess"),
|
||||
handleApiArgSelect(changeValue as string, currentValue, "onSuccess"),
|
||||
getSelectedValue: (value: string, returnArgs = false) =>
|
||||
getApiArgumentValue(value, "onSuccess", returnArgs),
|
||||
},
|
||||
|
|
@ -187,7 +249,7 @@ export const PropertyPaneActionDropdownOptions: ActionCreatorDropdownOption[] =
|
|||
label: "onError",
|
||||
field: "ACTION_SELECTOR_FIELD",
|
||||
valueChangeHandler: (changeValue, currentValue) =>
|
||||
handleApiArgSelect(changeValue, currentValue, "onError"),
|
||||
handleApiArgSelect(changeValue as string, currentValue, "onError"),
|
||||
getSelectedValue: (value: string, returnArgs = false) =>
|
||||
getApiArgumentValue(value, "onError", returnArgs),
|
||||
},
|
||||
|
|
@ -234,6 +296,12 @@ export const PropertyPaneActionDropdownOptions: ActionCreatorDropdownOption[] =
|
|||
valueChangeHandler: handlePageNameArgSelect,
|
||||
getSelectedValue: getPageNameSelectedValue,
|
||||
},
|
||||
{
|
||||
label: "params",
|
||||
field: "KEY_VALUE_FIELD",
|
||||
valueChangeHandler: handlePageParamsArgSelect,
|
||||
getSelectedValue: getPageParamsSelectedValue,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -298,15 +366,18 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
};
|
||||
|
||||
handleValueUpdate = (
|
||||
updateValueOrEvent: string | ChangeEvent<HTMLTextAreaElement>,
|
||||
updateValueOrEvent: ValueType | ChangeEvent<HTMLTextAreaElement>,
|
||||
valueUpdateHandler: ValueChangeHandler,
|
||||
) => {
|
||||
const { value, onValueChange } = this.props;
|
||||
let updateValue = updateValueOrEvent;
|
||||
if (typeof updateValueOrEvent !== "string") {
|
||||
updateValue = updateValueOrEvent.target.value;
|
||||
if (
|
||||
typeof updateValueOrEvent !== "string" &&
|
||||
(updateValueOrEvent as any).target
|
||||
) {
|
||||
updateValue = (updateValueOrEvent as any).target.value;
|
||||
}
|
||||
const newValue = valueUpdateHandler(updateValue as string, value);
|
||||
const newValue = valueUpdateHandler(updateValue as ValueType, value);
|
||||
onValueChange(newValue);
|
||||
};
|
||||
|
||||
|
|
@ -314,7 +385,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
argValue: string,
|
||||
allOptions: ActionCreatorDropdownOption[],
|
||||
parentChangeHandler: (
|
||||
updateValueOrEvent: string | ChangeEvent<HTMLTextAreaElement>,
|
||||
updateValueOrEvent: ValueType | ChangeEvent<HTMLTextAreaElement>,
|
||||
valueUpdateHandler: ValueChangeHandler,
|
||||
) => void,
|
||||
argumentConfig: ActionCreatorArgumentConfig,
|
||||
|
|
@ -330,15 +401,18 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
}
|
||||
});
|
||||
const handleValueUpdate = (
|
||||
updateValueOrEvent: string | ChangeEvent<HTMLTextAreaElement>,
|
||||
updateValueOrEvent: ValueType | ChangeEvent<HTMLTextAreaElement>,
|
||||
valueUpdateHandler: ValueChangeHandler,
|
||||
) => {
|
||||
let updateValue = updateValueOrEvent;
|
||||
if (typeof updateValueOrEvent !== "string") {
|
||||
updateValue = updateValueOrEvent.target.value;
|
||||
if (
|
||||
typeof updateValueOrEvent !== "string" &&
|
||||
(updateValueOrEvent as any).target
|
||||
) {
|
||||
updateValue = (updateValueOrEvent as any).target.value;
|
||||
}
|
||||
const tempArg = `{{${subArgValue}${subArguments}}}`;
|
||||
const newValue = valueUpdateHandler(updateValue as string, tempArg);
|
||||
const newValue = valueUpdateHandler(updateValue as ValueType, tempArg);
|
||||
const newArgValue = newValue.substring(2, newValue.length - 2);
|
||||
parentChangeHandler(newArgValue, argumentConfig.valueChangeHandler);
|
||||
};
|
||||
|
|
@ -356,7 +430,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
selectedOption: ActionCreatorDropdownOption,
|
||||
allOptions: ActionCreatorDropdownOption[],
|
||||
handleUpdate: (
|
||||
updateValueOrEvent: string | ChangeEvent<HTMLTextAreaElement>,
|
||||
updateValueOrEvent: ValueType | ChangeEvent<HTMLTextAreaElement>,
|
||||
valueUpdateHandler: ValueChangeHandler,
|
||||
) => void,
|
||||
) => {
|
||||
|
|
@ -370,7 +444,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
<label>{arg.label}</label>
|
||||
<StyledDropdown
|
||||
options={allOptions}
|
||||
selectedValue={arg.getSelectedValue(value, false)}
|
||||
selectedValue={arg.getSelectedValue(value, false) as string}
|
||||
defaultText={"Select Action"}
|
||||
onSelect={value =>
|
||||
handleUpdate(value, arg.valueChangeHandler)
|
||||
|
|
@ -390,11 +464,25 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
<label>{arg.label}</label>
|
||||
<StyledDropdown
|
||||
options={this.props.pageNameDropdown}
|
||||
selectedValue={arg.getSelectedValue(value, false)}
|
||||
selectedValue={arg.getSelectedValue(value, false) as string}
|
||||
defaultText={"Select Page"}
|
||||
onSelect={value =>
|
||||
handleUpdate(value, arg.valueChangeHandler)
|
||||
onSelect={newValue => {
|
||||
handleUpdate(newValue, arg.valueChangeHandler);
|
||||
}}
|
||||
/>
|
||||
</ControlWrapper>
|
||||
);
|
||||
case "KEY_VALUE_FIELD":
|
||||
return (
|
||||
<ControlWrapper key={arg.label}>
|
||||
<KeyValueComponent
|
||||
pairs={
|
||||
arg.getSelectedValue(value, false) as DropdownOption[]
|
||||
}
|
||||
addLabel={"QueryParam"}
|
||||
updatePairs={(pageParams: DropdownOption[]) => {
|
||||
handleUpdate(pageParams as any, arg.valueChangeHandler);
|
||||
}}
|
||||
/>
|
||||
</ControlWrapper>
|
||||
);
|
||||
|
|
@ -404,7 +492,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
<label>{arg.label}</label>
|
||||
<StyledDropdown
|
||||
options={this.props.modalDropdown || []}
|
||||
selectedValue={arg.getSelectedValue(value, false)}
|
||||
selectedValue={arg.getSelectedValue(value, false) as string}
|
||||
defaultText={"Select Modal"}
|
||||
createButton={
|
||||
arg.create && {
|
||||
|
|
@ -426,7 +514,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
<label>{arg.label}</label>
|
||||
<InputText
|
||||
label={arg.label}
|
||||
value={arg.getSelectedValue(value, false)}
|
||||
value={arg.getSelectedValue(value, false) as string}
|
||||
onChange={e => handleUpdate(e, arg.valueChangeHandler)}
|
||||
isValid={this.props.isValid}
|
||||
validationMessage={this.props.validationMessage}
|
||||
|
|
@ -440,7 +528,7 @@ class DynamicActionCreator extends React.Component<Props & ReduxStateProps> {
|
|||
<StyledDropdown
|
||||
options={ALERT_STYLE_OPTIONS}
|
||||
defaultText={"Select type"}
|
||||
selectedValue={arg.getSelectedValue(value, false)}
|
||||
selectedValue={arg.getSelectedValue(value, false) as string}
|
||||
onSelect={value =>
|
||||
handleUpdate(value, arg.valueChangeHandler)
|
||||
}
|
||||
|
|
|
|||
179
app/client/src/components/propertyControls/KeyValueComponent.tsx
Normal file
179
app/client/src/components/propertyControls/KeyValueComponent.tsx
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { FormIcons } from "icons/FormIcons";
|
||||
import { AnyStyledComponent } from "styled-components";
|
||||
import {
|
||||
ControlWrapper,
|
||||
StyledInputGroup,
|
||||
StyledPropertyPaneButton,
|
||||
} from "./StyledControls";
|
||||
|
||||
import { DropDownOptionWithKey } from "./OptionControl";
|
||||
import { DropdownOption } from "widgets/DropdownWidget";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
|
||||
function updateOptionLabel<T>(
|
||||
options: Array<T>,
|
||||
index: number,
|
||||
updatedLabel: string,
|
||||
) {
|
||||
return options.map((option: T, optionIndex) => {
|
||||
if (index !== optionIndex) {
|
||||
return option;
|
||||
}
|
||||
return {
|
||||
...option,
|
||||
label: updatedLabel,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function updateOptionValue<T>(
|
||||
options: Array<T>,
|
||||
index: number,
|
||||
updatedValue: string,
|
||||
) {
|
||||
return options.map((option, optionIndex) => {
|
||||
if (index !== optionIndex) {
|
||||
return option;
|
||||
}
|
||||
return {
|
||||
...option,
|
||||
value: updatedValue,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const StyledDeleteIcon = styled(FormIcons.DELETE_ICON as AnyStyledComponent)`
|
||||
padding: 0px 5px;
|
||||
position: absolute;
|
||||
right: -2px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledOptionControlInputGroup = styled(StyledInputGroup)`
|
||||
margin-right: 2px;
|
||||
`;
|
||||
|
||||
const StyledOptionControlWrapper = styled(ControlWrapper)`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding-right: 16px;
|
||||
`;
|
||||
|
||||
type KeyValueComponentProps = {
|
||||
pairs: DropdownOption[];
|
||||
updatePairs: Function;
|
||||
addLabel?: string;
|
||||
};
|
||||
export function KeyValueComponent(props: KeyValueComponentProps) {
|
||||
const [renderPairs, setRenderPairs] = useState<DropDownOptionWithKey[]>([]);
|
||||
const { pairs } = props;
|
||||
useEffect(() => {
|
||||
let { pairs } = props;
|
||||
pairs = Array.isArray(pairs) ? pairs.slice() : [];
|
||||
|
||||
const newRenderPairs: DropDownOptionWithKey[] = pairs.map(pair => {
|
||||
return {
|
||||
...pair,
|
||||
key: generateReactKey(),
|
||||
};
|
||||
});
|
||||
|
||||
pairs.length !== 0 &&
|
||||
renderPairs.length === 0 &&
|
||||
setRenderPairs(newRenderPairs);
|
||||
}, [props, pairs.length, renderPairs.length]);
|
||||
|
||||
function deletePair(index: number) {
|
||||
let { pairs } = props;
|
||||
pairs = Array.isArray(pairs) ? pairs : [];
|
||||
|
||||
const newPairs = pairs.filter((o, i) => i !== index);
|
||||
const newRenderPairs = renderPairs.filter((o, i) => i !== index);
|
||||
|
||||
setRenderPairs(newRenderPairs);
|
||||
props.updatePairs(newPairs);
|
||||
}
|
||||
|
||||
function updateKey(index: number, updatedKey: string) {
|
||||
let { pairs } = props;
|
||||
pairs = Array.isArray(pairs) ? pairs : [];
|
||||
const updatedPairs = updateOptionLabel(pairs, index, updatedKey);
|
||||
const updatedRenderPairs = updateOptionLabel(
|
||||
renderPairs,
|
||||
index,
|
||||
updatedKey,
|
||||
);
|
||||
|
||||
setRenderPairs(updatedRenderPairs);
|
||||
props.updatePairs(updatedPairs);
|
||||
}
|
||||
|
||||
function updateValue(index: number, updatedValue: string) {
|
||||
let { pairs } = props;
|
||||
pairs = Array.isArray(pairs) ? pairs : [];
|
||||
const updatedPairs = updateOptionValue(pairs, index, updatedValue);
|
||||
const updatedRenderPairs = updateOptionValue(
|
||||
renderPairs,
|
||||
index,
|
||||
updatedValue,
|
||||
);
|
||||
|
||||
setRenderPairs(updatedRenderPairs);
|
||||
props.updatePairs(updatedPairs);
|
||||
}
|
||||
|
||||
function addPair() {
|
||||
let { pairs } = props;
|
||||
pairs = Array.isArray(pairs) ? pairs.slice() : [];
|
||||
pairs.push({ label: "", value: "" });
|
||||
const updatedRenderPairs = renderPairs.slice();
|
||||
updatedRenderPairs.push({ label: "", value: "", key: generateReactKey() });
|
||||
|
||||
setRenderPairs(updatedRenderPairs);
|
||||
props.updatePairs(pairs);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{renderPairs.map((pair: DropDownOptionWithKey, index) => {
|
||||
return (
|
||||
<StyledOptionControlWrapper orientation={"HORIZONTAL"} key={pair.key}>
|
||||
<StyledOptionControlInputGroup
|
||||
type={"text"}
|
||||
placeholder={"Name"}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateKey(index, event.target.value);
|
||||
}}
|
||||
defaultValue={pair.label}
|
||||
/>
|
||||
<StyledOptionControlInputGroup
|
||||
type={"text"}
|
||||
placeholder={"Value"}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateValue(index, event.target.value);
|
||||
}}
|
||||
defaultValue={pair.value}
|
||||
/>
|
||||
<StyledDeleteIcon
|
||||
height={20}
|
||||
width={20}
|
||||
onClick={() => {
|
||||
deletePair(index);
|
||||
}}
|
||||
/>
|
||||
</StyledOptionControlWrapper>
|
||||
);
|
||||
})}
|
||||
<StyledPropertyPaneButton
|
||||
text={props.addLabel || "Option"}
|
||||
icon={"plus"}
|
||||
color={"#FFFFFF"}
|
||||
minimal={true}
|
||||
onClick={addPair}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,223 +1,24 @@
|
|||
import React from "react";
|
||||
import BaseControl, { ControlProps } from "./BaseControl";
|
||||
import {
|
||||
ControlWrapper,
|
||||
StyledInputGroup,
|
||||
StyledPropertyPaneButton,
|
||||
} from "./StyledControls";
|
||||
import { DropdownOption } from "widgets/DropdownWidget";
|
||||
import { ControlType } from "constants/PropertyControlConstants";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { FormIcons } from "icons/FormIcons";
|
||||
import { AnyStyledComponent } from "styled-components";
|
||||
import { generateReactKey } from "utils/generators";
|
||||
import { KeyValueComponent } from "./KeyValueComponent";
|
||||
|
||||
const StyledDeleteIcon = styled(FormIcons.DELETE_ICON as AnyStyledComponent)`
|
||||
padding: 5px 5px;
|
||||
position: absolute;
|
||||
right: -4px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledOptionControlInputGroup = styled(StyledInputGroup)`
|
||||
margin-right: 2px;
|
||||
`;
|
||||
|
||||
const StyledOptionControlWrapper = styled(ControlWrapper)`
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding-right: 16px;
|
||||
`;
|
||||
|
||||
function updateOptionLabel<T>(
|
||||
options: Array<T>,
|
||||
index: number,
|
||||
updatedLabel: string,
|
||||
) {
|
||||
return options.map((option: T, optionIndex) => {
|
||||
if (index !== optionIndex) {
|
||||
return option;
|
||||
}
|
||||
return {
|
||||
...option,
|
||||
label: updatedLabel,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function updateOptionValue<T>(
|
||||
options: Array<T>,
|
||||
index: number,
|
||||
updatedValue: string,
|
||||
) {
|
||||
return options.map((option, optionIndex) => {
|
||||
if (index !== optionIndex) {
|
||||
return option;
|
||||
}
|
||||
return {
|
||||
...option,
|
||||
value: updatedValue,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
type DropDownOptionWithKey = DropdownOption & {
|
||||
export type DropDownOptionWithKey = DropdownOption & {
|
||||
key: string;
|
||||
};
|
||||
|
||||
class OptionControl extends BaseControl<
|
||||
ControlProps,
|
||||
{
|
||||
renderOptions: DropDownOptionWithKey[];
|
||||
}
|
||||
> {
|
||||
constructor(props: ControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
renderOptions: [],
|
||||
};
|
||||
}
|
||||
class OptionControl extends BaseControl<ControlProps> {
|
||||
render() {
|
||||
const { renderOptions } = this.state;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{renderOptions.map((option, index) => {
|
||||
return (
|
||||
<StyledOptionControlWrapper
|
||||
orientation={"HORIZONTAL"}
|
||||
key={option.key}
|
||||
>
|
||||
<StyledOptionControlInputGroup
|
||||
type={"text"}
|
||||
placeholder={"Name"}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.updateOptionLabel(index, event.target.value);
|
||||
}}
|
||||
defaultValue={option.label}
|
||||
/>
|
||||
<StyledOptionControlInputGroup
|
||||
type={"text"}
|
||||
placeholder={"Value"}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.updateOptionValue(index, event.target.value);
|
||||
}}
|
||||
defaultValue={option.value}
|
||||
/>
|
||||
<StyledDeleteIcon
|
||||
height={20}
|
||||
width={20}
|
||||
onClick={() => {
|
||||
this.deleteOption(index);
|
||||
}}
|
||||
/>
|
||||
</StyledOptionControlWrapper>
|
||||
);
|
||||
})}
|
||||
<StyledPropertyPaneButton
|
||||
text={"Option"}
|
||||
icon={"plus"}
|
||||
color={"#FFFFFF"}
|
||||
minimal={true}
|
||||
onClick={this.addOption}
|
||||
/>
|
||||
</React.Fragment>
|
||||
<KeyValueComponent
|
||||
pairs={this.props.propertyValue}
|
||||
updatePairs={this.updateOptions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { propertyValue } = this.props;
|
||||
|
||||
const options: DropdownOption[] = Array.isArray(propertyValue)
|
||||
? propertyValue
|
||||
: [{}];
|
||||
|
||||
options.map(option => {
|
||||
return {
|
||||
...option,
|
||||
key: generateReactKey(),
|
||||
};
|
||||
});
|
||||
this.setState({
|
||||
renderOptions: options.map(option => {
|
||||
return {
|
||||
...option,
|
||||
key: generateReactKey(),
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
deleteOption = (index: number) => {
|
||||
const { propertyValue } = this.props;
|
||||
const options: DropdownOption[] = Array.isArray(propertyValue)
|
||||
? propertyValue
|
||||
: [{}];
|
||||
const { renderOptions } = this.state;
|
||||
|
||||
const newOptions = options.filter((o, i) => i !== index);
|
||||
const newRenderOptions = renderOptions.filter((o, i) => i !== index);
|
||||
|
||||
this.updateProperty("options", newOptions);
|
||||
this.setState({
|
||||
renderOptions: newRenderOptions,
|
||||
});
|
||||
};
|
||||
|
||||
updateOptionLabel = (index: number, updatedLabel: string) => {
|
||||
const { propertyValue } = this.props;
|
||||
const options: DropdownOption[] = Array.isArray(propertyValue)
|
||||
? propertyValue
|
||||
: [{}];
|
||||
this.updateProperty(
|
||||
"options",
|
||||
updateOptionLabel(options, index, updatedLabel),
|
||||
);
|
||||
|
||||
this.setState({
|
||||
renderOptions: updateOptionLabel(
|
||||
this.state.renderOptions,
|
||||
index,
|
||||
updatedLabel,
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
updateOptionValue = (index: number, updatedValue: string) => {
|
||||
const { propertyValue } = this.props;
|
||||
const options: DropdownOption[] = Array.isArray(propertyValue)
|
||||
? propertyValue
|
||||
: [{}];
|
||||
this.updateProperty(
|
||||
"options",
|
||||
updateOptionValue(options, index, updatedValue),
|
||||
);
|
||||
|
||||
this.setState({
|
||||
renderOptions: updateOptionValue(
|
||||
this.state.renderOptions,
|
||||
index,
|
||||
updatedValue,
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
addOption = () => {
|
||||
const { propertyValue } = this.props;
|
||||
const options: DropdownOption[] = Array.isArray(propertyValue)
|
||||
? propertyValue
|
||||
: [{}];
|
||||
const { renderOptions } = this.state;
|
||||
|
||||
options.push({ label: "", value: "" });
|
||||
renderOptions.push({
|
||||
label: "",
|
||||
value: "",
|
||||
key: generateReactKey(),
|
||||
});
|
||||
|
||||
this.setState({
|
||||
renderOptions: renderOptions,
|
||||
});
|
||||
updateOptions = (options: DropdownOption[]) => {
|
||||
this.updateProperty("options", options);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,13 @@ export const BUILDER_BASE_URL = (applicationId = ":applicationId"): string =>
|
|||
export const BUILDER_PAGE_URL = (
|
||||
applicationId?: string,
|
||||
pageId?: string,
|
||||
params?: Record<string, string>,
|
||||
): string => {
|
||||
if (!pageId) return APPLICATIONS_URL;
|
||||
return `${BUILDER_BASE_URL(applicationId)}/pages/${pageId}/edit`;
|
||||
const queryParams = convertToQueryParams(params);
|
||||
return (
|
||||
`${BUILDER_BASE_URL(applicationId)}/pages/${pageId}/edit` + queryParams
|
||||
);
|
||||
};
|
||||
|
||||
export const API_EDITOR_URL = (
|
||||
|
|
@ -58,7 +62,26 @@ export const getApplicationViewerURL = (
|
|||
export const getApplicationViewerPageURL = (
|
||||
applicationId = ":applicationId",
|
||||
pageId = ":pageId",
|
||||
): string => `/applications/${applicationId}/pages/${pageId}`;
|
||||
params: Record<string, string> = {},
|
||||
): string => {
|
||||
const url = `/applications/${applicationId}/pages/${pageId}`;
|
||||
const queryParams = convertToQueryParams(params);
|
||||
return url + queryParams;
|
||||
};
|
||||
|
||||
function convertToQueryParams(params: Record<string, string> = {}): string {
|
||||
const paramKeys = Object.keys(params);
|
||||
let queryParams = "";
|
||||
if (paramKeys) {
|
||||
paramKeys.forEach((paramKey: string, index: number) => {
|
||||
const value = params[paramKey];
|
||||
if (paramKey && value) {
|
||||
queryParams = queryParams + `&${paramKey}=${value}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
return queryParams ? "?" + queryParams : "";
|
||||
}
|
||||
|
||||
export const EDITOR_ROUTES = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,6 +33,17 @@ export interface DataTreeAction extends Omit<ActionData, "data"> {
|
|||
ENTITY_TYPE: ENTITY_TYPE.ACTION;
|
||||
}
|
||||
|
||||
export interface DataTreeUrl {
|
||||
queryParams: Record<string, string>;
|
||||
protocol: string;
|
||||
host: string;
|
||||
hostname: string;
|
||||
port: string;
|
||||
pathname: string;
|
||||
hash: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface DataTreeWidget extends WidgetProps {
|
||||
ENTITY_TYPE: ENTITY_TYPE.WIDGET;
|
||||
}
|
||||
|
|
@ -40,6 +51,7 @@ export interface DataTreeWidget extends WidgetProps {
|
|||
export type DataTreeEntity =
|
||||
| DataTreeAction
|
||||
| DataTreeWidget
|
||||
| DataTreeUrl
|
||||
| ActionDispatcher<any, any>;
|
||||
|
||||
export type DataTree = {
|
||||
|
|
@ -50,10 +62,16 @@ type DataTreeSeed = {
|
|||
actions: ActionDataState;
|
||||
widgets: CanvasWidgetsReduxState;
|
||||
widgetsMeta: MetaState;
|
||||
url: DataTreeUrl;
|
||||
};
|
||||
|
||||
export class DataTreeFactory {
|
||||
static create({ actions, widgets, widgetsMeta }: DataTreeSeed): DataTree {
|
||||
static create({
|
||||
actions,
|
||||
widgets,
|
||||
widgetsMeta,
|
||||
url,
|
||||
}: DataTreeSeed): DataTree {
|
||||
const dataTree: DataTree = {};
|
||||
dataTree.actionPaths = [
|
||||
"navigateTo",
|
||||
|
|
@ -89,10 +107,10 @@ export class DataTreeFactory {
|
|||
ENTITY_TYPE: ENTITY_TYPE.WIDGET,
|
||||
};
|
||||
});
|
||||
dataTree.navigateTo = function(pageName: string) {
|
||||
dataTree.navigateTo = function(pageName: string, params: object) {
|
||||
return {
|
||||
type: "NAVIGATE_TO",
|
||||
payload: { pageName },
|
||||
payload: { pageName, params },
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -110,6 +128,7 @@ export class DataTreeFactory {
|
|||
};
|
||||
};
|
||||
|
||||
dataTree.url = url;
|
||||
dataTree.showModal = function(modalName: string) {
|
||||
return {
|
||||
type: "SHOW_MODAL_BY_NAME",
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ export function* executeActionSaga(
|
|||
}
|
||||
|
||||
function* navigateActionSaga(
|
||||
action: { pageName: string },
|
||||
action: { pageName: string; params: Record<string, string> },
|
||||
event: ExecuteActionPayloadEvent,
|
||||
) {
|
||||
const pageList = yield select(getPageList);
|
||||
|
|
@ -272,9 +272,14 @@ function* navigateActionSaga(
|
|||
const page = _.find(pageList, { pageName: action.pageName });
|
||||
if (page) {
|
||||
// TODO need to make this check via RENDER_MODE;
|
||||
const path = history.location.pathname.endsWith("/edit")
|
||||
? BUILDER_PAGE_URL(applicationId, page.pageId)
|
||||
: getApplicationViewerPageURL(applicationId, page.pageId);
|
||||
const path =
|
||||
history.location.pathname.indexOf("/edit") !== -1
|
||||
? BUILDER_PAGE_URL(applicationId, page.pageId, action.params)
|
||||
: getApplicationViewerPageURL(
|
||||
applicationId,
|
||||
page.pageId,
|
||||
action.params,
|
||||
);
|
||||
history.push(path);
|
||||
if (event.callback) event.callback({ success: true });
|
||||
} else {
|
||||
|
|
@ -293,6 +298,7 @@ export function* executeActionTriggers(
|
|||
case "NAVIGATE_TO":
|
||||
AnalyticsUtil.logEvent("NAVIGATE", {
|
||||
pageName: trigger.payload.pageName,
|
||||
pageParams: trigger.payload.pageParams,
|
||||
});
|
||||
yield call(navigateActionSaga, trigger.payload, event);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -3,17 +3,56 @@ import { getActionsForCurrentPage } from "./entitiesSelector";
|
|||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||
import { getEvaluatedDataTree } from "utils/DynamicBindingUtils";
|
||||
import { extraLibraries } from "jsExecution/JSExecutionManagerSingleton";
|
||||
import { DataTree, DataTreeFactory } from "entities/DataTree/dataTreeFactory";
|
||||
import {
|
||||
DataTree,
|
||||
DataTreeFactory,
|
||||
DataTreeUrl,
|
||||
} from "entities/DataTree/dataTreeFactory";
|
||||
import _ from "lodash";
|
||||
import { getWidgets, getWidgetsMeta } from "sagas/selectors";
|
||||
import * as log from "loglevel";
|
||||
import "url-search-params-polyfill";
|
||||
|
||||
function getQueryParams() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const keys = urlParams.keys();
|
||||
let key = keys.next().value;
|
||||
const queryParams: Record<string, string> = {};
|
||||
while (key) {
|
||||
queryParams[key] = urlParams.get(key) as string;
|
||||
key = keys.next().value;
|
||||
}
|
||||
return queryParams;
|
||||
}
|
||||
|
||||
const getUrlParams = createSelector(
|
||||
getQueryParams,
|
||||
(queryParams: Record<string, string>): DataTreeUrl => {
|
||||
return {
|
||||
host: window.location.host,
|
||||
hostname: window.location.hostname,
|
||||
queryParams: queryParams,
|
||||
protocol: window.location.protocol,
|
||||
pathname: window.location.pathname,
|
||||
port: window.location.port,
|
||||
href: window.location.href,
|
||||
hash: window.location.hash,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export const getUnevaluatedDataTree = createSelector(
|
||||
getActionsForCurrentPage,
|
||||
getWidgets,
|
||||
getWidgetsMeta,
|
||||
(actions, widgets, widgetsMeta) => {
|
||||
return DataTreeFactory.create({ actions, widgets, widgetsMeta });
|
||||
getUrlParams,
|
||||
(actions, widgets, widgetsMeta, url) => {
|
||||
return DataTreeFactory.create({
|
||||
actions,
|
||||
widgets,
|
||||
widgetsMeta,
|
||||
url,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -13798,6 +13798,11 @@ url-parse@^1.4.3:
|
|||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
url-search-params-polyfill@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/url-search-params-polyfill/-/url-search-params-polyfill-8.0.0.tgz#17415ca6815ff0661e07737b84bcc28e708a7875"
|
||||
integrity sha512-X4BTaEq1UMz9bTbMKQ6r+CippkKBsFWHiP9wycQc7aHH2Ml/Iieuo44+GJDb77pfP71bONYA/nUd4iokYAxVRQ==
|
||||
|
||||
url@0.11.0, url@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user