From bb9fc2940975a40968e44dcfc110bdd72424797f Mon Sep 17 00:00:00 2001 From: Satbir Singh Date: Wed, 1 Apr 2020 08:09:57 +0000 Subject: [PATCH] Page Params --- app/client/package.json | 3 +- .../editorComponents/DynamicActionCreator.tsx | 146 +++++++++--- .../propertyControls/KeyValueComponent.tsx | 179 +++++++++++++++ .../propertyControls/OptionControl.tsx | 215 +----------------- app/client/src/constants/routes.ts | 27 ++- .../src/entities/DataTree/dataTreeFactory.ts | 25 +- app/client/src/sagas/ActionSagas.ts | 14 +- app/client/src/selectors/dataTreeSelectors.ts | 45 +++- app/client/yarn.lock | 5 + 9 files changed, 410 insertions(+), 249 deletions(-) create mode 100644 app/client/src/components/propertyControls/KeyValueComponent.tsx diff --git a/app/client/package.json b/app/client/package.json index 9eaf2e58a4..19b2d3204b 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -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'", diff --git a/app/client/src/components/editorComponents/DynamicActionCreator.tsx b/app/client/src/components/editorComponents/DynamicActionCreator.tsx index 5979354991..2c7647da9b 100644 --- a/app/client/src/components/editorComponents/DynamicActionCreator.tsx +++ b/app/client/src/components/editorComponents/DynamicActionCreator.tsx @@ -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 = {}; + (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 = {}; + + 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 { }; handleValueUpdate = ( - updateValueOrEvent: string | ChangeEvent, + updateValueOrEvent: ValueType | ChangeEvent, 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 { argValue: string, allOptions: ActionCreatorDropdownOption[], parentChangeHandler: ( - updateValueOrEvent: string | ChangeEvent, + updateValueOrEvent: ValueType | ChangeEvent, valueUpdateHandler: ValueChangeHandler, ) => void, argumentConfig: ActionCreatorArgumentConfig, @@ -330,15 +401,18 @@ class DynamicActionCreator extends React.Component { } }); const handleValueUpdate = ( - updateValueOrEvent: string | ChangeEvent, + updateValueOrEvent: ValueType | ChangeEvent, 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 { selectedOption: ActionCreatorDropdownOption, allOptions: ActionCreatorDropdownOption[], handleUpdate: ( - updateValueOrEvent: string | ChangeEvent, + updateValueOrEvent: ValueType | ChangeEvent, valueUpdateHandler: ValueChangeHandler, ) => void, ) => { @@ -370,7 +444,7 @@ class DynamicActionCreator extends React.Component { handleUpdate(value, arg.valueChangeHandler) @@ -390,11 +464,25 @@ class DynamicActionCreator extends React.Component { - handleUpdate(value, arg.valueChangeHandler) + onSelect={newValue => { + handleUpdate(newValue, arg.valueChangeHandler); + }} + /> + + ); + case "KEY_VALUE_FIELD": + return ( + + { + handleUpdate(pageParams as any, arg.valueChangeHandler); + }} /> ); @@ -404,7 +492,7 @@ class DynamicActionCreator extends React.Component { { handleUpdate(e, arg.valueChangeHandler)} isValid={this.props.isValid} validationMessage={this.props.validationMessage} @@ -440,7 +528,7 @@ class DynamicActionCreator extends React.Component { handleUpdate(value, arg.valueChangeHandler) } diff --git a/app/client/src/components/propertyControls/KeyValueComponent.tsx b/app/client/src/components/propertyControls/KeyValueComponent.tsx new file mode 100644 index 0000000000..5fe20eda43 --- /dev/null +++ b/app/client/src/components/propertyControls/KeyValueComponent.tsx @@ -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( + options: Array, + index: number, + updatedLabel: string, +) { + return options.map((option: T, optionIndex) => { + if (index !== optionIndex) { + return option; + } + return { + ...option, + label: updatedLabel, + }; + }); +} + +function updateOptionValue( + options: Array, + 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([]); + 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 ( + + {renderPairs.map((pair: DropDownOptionWithKey, index) => { + return ( + + ) => { + updateKey(index, event.target.value); + }} + defaultValue={pair.label} + /> + ) => { + updateValue(index, event.target.value); + }} + defaultValue={pair.value} + /> + { + deletePair(index); + }} + /> + + ); + })} + + + ); +} diff --git a/app/client/src/components/propertyControls/OptionControl.tsx b/app/client/src/components/propertyControls/OptionControl.tsx index 2676a2a116..9209bffca1 100644 --- a/app/client/src/components/propertyControls/OptionControl.tsx +++ b/app/client/src/components/propertyControls/OptionControl.tsx @@ -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( - options: Array, - index: number, - updatedLabel: string, -) { - return options.map((option: T, optionIndex) => { - if (index !== optionIndex) { - return option; - } - return { - ...option, - label: updatedLabel, - }; - }); -} - -function updateOptionValue( - options: Array, - 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 { render() { - const { renderOptions } = this.state; return ( - - {renderOptions.map((option, index) => { - return ( - - ) => { - this.updateOptionLabel(index, event.target.value); - }} - defaultValue={option.label} - /> - ) => { - this.updateOptionValue(index, event.target.value); - }} - defaultValue={option.value} - /> - { - this.deleteOption(index); - }} - /> - - ); - })} - - + ); } - 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); }; diff --git a/app/client/src/constants/routes.ts b/app/client/src/constants/routes.ts index e1ee7f5e63..24bef97380 100644 --- a/app/client/src/constants/routes.ts +++ b/app/client/src/constants/routes.ts @@ -28,9 +28,13 @@ export const BUILDER_BASE_URL = (applicationId = ":applicationId"): string => export const BUILDER_PAGE_URL = ( applicationId?: string, pageId?: string, + params?: Record, ): 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 => { + const url = `/applications/${applicationId}/pages/${pageId}`; + const queryParams = convertToQueryParams(params); + return url + queryParams; +}; + +function convertToQueryParams(params: Record = {}): 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 = [ { diff --git a/app/client/src/entities/DataTree/dataTreeFactory.ts b/app/client/src/entities/DataTree/dataTreeFactory.ts index 57bb8b8a6c..1b985ea0a4 100644 --- a/app/client/src/entities/DataTree/dataTreeFactory.ts +++ b/app/client/src/entities/DataTree/dataTreeFactory.ts @@ -33,6 +33,17 @@ export interface DataTreeAction extends Omit { ENTITY_TYPE: ENTITY_TYPE.ACTION; } +export interface DataTreeUrl { + queryParams: Record; + 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; 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", diff --git a/app/client/src/sagas/ActionSagas.ts b/app/client/src/sagas/ActionSagas.ts index 14276de9e9..7537965d95 100644 --- a/app/client/src/sagas/ActionSagas.ts +++ b/app/client/src/sagas/ActionSagas.ts @@ -264,7 +264,7 @@ export function* executeActionSaga( } function* navigateActionSaga( - action: { pageName: string }, + action: { pageName: string; params: Record }, 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; diff --git a/app/client/src/selectors/dataTreeSelectors.ts b/app/client/src/selectors/dataTreeSelectors.ts index adeb0d4678..7722f685b7 100644 --- a/app/client/src/selectors/dataTreeSelectors.ts +++ b/app/client/src/selectors/dataTreeSelectors.ts @@ -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 = {}; + while (key) { + queryParams[key] = urlParams.get(key) as string; + key = keys.next().value; + } + return queryParams; +} + +const getUrlParams = createSelector( + getQueryParams, + (queryParams: Record): 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, + }); }, ); diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 67644b482b..8d85f9f197 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -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"