From 982262b76c45eea526084d3059d375083dae47a7 Mon Sep 17 00:00:00 2001 From: Manish Kushare <37823141+manishkushare@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:02:42 +0530 Subject: [PATCH 01/41] add new row is crashing when no data is fed to table (#7102) --- frontend/src/Editor/Components/Table/columns/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/Editor/Components/Table/columns/index.jsx b/frontend/src/Editor/Components/Table/columns/index.jsx index 1808ce0b2e..c01424a662 100644 --- a/frontend/src/Editor/Components/Table/columns/index.jsx +++ b/frontend/src/Editor/Components/Table/columns/index.jsx @@ -99,10 +99,10 @@ export default function generateColumnsData({ const rowChangeSet = updatedChangeSet ? updatedChangeSet[cell.row.index] : null; let cellValue = rowChangeSet ? rowChangeSet[column.key || column.name] ?? cell.value : cell.value; - const rowData = tableData[cell.row.index]; + const rowData = tableData?.[cell?.row?.index]; if ( cell.row.index === 0 && - variablesExposedForPreview && + !_.isEmpty(variablesExposedForPreview) && !_.isEqual(variablesExposedForPreview[id]?.rowData, rowData) ) { const customResolvables = {}; From 402c8878f53c81b9311cce1acef31f2b5438f783 Mon Sep 17 00:00:00 2001 From: Kiran Ashok Date: Wed, 9 Aug 2023 09:09:18 +0530 Subject: [PATCH 02/41] Bugfix :: Validation errors should only be shown once the user has made a change (#7067) * fix :: show validation error only when user has interacted with the widget * showing error in textinput only when user unfocuses input * updating password widget * show invalid border only on interaction :: password input --- frontend/src/Editor/Components/Datepicker.jsx | 10 ++++++---- frontend/src/Editor/Components/DropDown.jsx | 6 +++++- frontend/src/Editor/Components/PasswordInput.jsx | 10 ++++++---- frontend/src/Editor/Components/TextInput.jsx | 4 +++- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/frontend/src/Editor/Components/Datepicker.jsx b/frontend/src/Editor/Components/Datepicker.jsx index 2a46afc4d7..2b5f238a72 100644 --- a/frontend/src/Editor/Components/Datepicker.jsx +++ b/frontend/src/Editor/Components/Datepicker.jsx @@ -23,6 +23,7 @@ export const Datepicker = function Datepicker({ const [date, setDate] = useState(null); const [excludedDates, setExcludedDates] = useState([]); + const [showValidationError, setShowValidationError] = useState(false); const selectedDateFormat = enableTime ? `${format} LT` : format; @@ -37,6 +38,7 @@ export const Datepicker = function Datepicker({ }; const onDateChange = (date) => { + setShowValidationError(true); setDate(date); const dateString = computeDateString(date); setExposedVariable('value', dateString).then(() => { @@ -89,9 +91,9 @@ export const Datepicker = function Datepicker({ }} > onDateChange(date)} @@ -109,7 +111,7 @@ export const Datepicker = function Datepicker({ />
- {validationError} + {showValidationError && validationError}
); diff --git a/frontend/src/Editor/Components/DropDown.jsx b/frontend/src/Editor/Components/DropDown.jsx index 2e9ed3d735..831e2e9e0d 100644 --- a/frontend/src/Editor/Components/DropDown.jsx +++ b/frontend/src/Editor/Components/DropDown.jsx @@ -20,6 +20,7 @@ export const DropDown = function DropDown({ const { selectedTextColor, borderRadius, visibility, disabledState, justifyContent, boxShadow } = styles; const [currentValue, setCurrentValue] = useState(() => (advanced ? findDefaultItem(schema) : value)); const { value: exposedValue } = exposedVariables; + const [showValidationError, setShowValidationError] = useState(false); function findDefaultItem(schema) { const foundItem = schema?.find((item) => item?.default === true); @@ -244,6 +245,7 @@ export const DropDown = function DropDown({ isDisabled={disabledState} value={selectOptions.filter((option) => option.value === currentValue)[0] ?? null} onChange={(selectedOption, actionProps) => { + setShowValidationError(true); if (actionProps.action === 'select-option') { setCurrentValue(selectedOption.value); setExposedVariable('value', selectedOption.value).then(() => fireEvent('onSelect')); @@ -260,7 +262,9 @@ export const DropDown = function DropDown({ /> -
{validationError}
+
+ {showValidationError && validationError} +
); }; diff --git a/frontend/src/Editor/Components/PasswordInput.jsx b/frontend/src/Editor/Components/PasswordInput.jsx index 0fcf47506b..d39732ba69 100644 --- a/frontend/src/Editor/Components/PasswordInput.jsx +++ b/frontend/src/Editor/Components/PasswordInput.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; export const PasswordInput = ({ height, @@ -15,8 +15,9 @@ export const PasswordInput = ({ const placeholder = properties.placeholder; - const [passwordValue, setPasswordValue] = React.useState(''); + const [passwordValue, setPasswordValue] = useState(''); const { isValid, validationError } = validate(passwordValue); + const [showValidationError, setShowValidationError] = useState(false); React.useEffect(() => { setExposedVariable('isValid', isValid); @@ -30,9 +31,10 @@ export const PasswordInput = ({ onChange={(e) => { setPasswordValue(e.target.value); setExposedVariable('value', e.target.value).then(() => fireEvent('onChange')); + setShowValidationError(true); }} type={'password'} - className={`form-control ${!isValid ? 'is-invalid' : ''} validation-without-icon ${ + className={`form-control ${!isValid && showValidationError ? 'is-invalid' : ''} validation-without-icon ${ darkMode && 'dark-theme-placeholder' }`} placeholder={placeholder} @@ -47,7 +49,7 @@ export const PasswordInput = ({ data-cy={dataCy} />
- {validationError} + {showValidationError && validationError}
); diff --git a/frontend/src/Editor/Components/TextInput.jsx b/frontend/src/Editor/Components/TextInput.jsx index 915eef6ead..f8d92d8771 100644 --- a/frontend/src/Editor/Components/TextInput.jsx +++ b/frontend/src/Editor/Components/TextInput.jsx @@ -18,6 +18,7 @@ export const TextInput = function TextInput({ const [value, setValue] = useState(properties.value); const [visibility, setVisibility] = useState(styles.visibility); const { isValid, validationError } = validate(value); + const [showValidationError, setShowValidationError] = useState(false); const computedStyles = { height, @@ -96,6 +97,7 @@ export const TextInput = function TextInput({ fireEvent('onChange'); }} onBlur={(e) => { + setShowValidationError(true); e.stopPropagation(); fireEvent('onBlur'); }} @@ -117,7 +119,7 @@ export const TextInput = function TextInput({ data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`} style={{ color: styles.errTextColor }} > - {validationError} + {showValidationError && validationError} ); From 48a9efc02b0ad5cfbb2d06587d6f062b7f2478a3 Mon Sep 17 00:00:00 2001 From: Kiran Ashok Date: Tue, 29 Aug 2023 08:39:38 +0530 Subject: [PATCH 03/41] fix :: issues with number input typing (#7070) --- frontend/src/Editor/Components/NumberInput.jsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/frontend/src/Editor/Components/NumberInput.jsx b/frontend/src/Editor/Components/NumberInput.jsx index df329c3b0b..b13986a320 100644 --- a/frontend/src/Editor/Components/NumberInput.jsx +++ b/frontend/src/Editor/Components/NumberInput.jsx @@ -32,24 +32,21 @@ export const NumberInput = function NumberInput({ !isNaN(parseFloat(properties.maxValue)) && parseFloat(properties.minValue) > parseFloat(properties.maxValue) ) { - setValue(Number(parseFloat(properties.maxValue)).toFixed(properties.decimalPlaces)); + setValue(Number(parseFloat(properties.maxValue))); } else if ( !isNaN(parseFloat(properties.maxValue)) && parseFloat(e.target.value) > parseFloat(properties.maxValue) ) { - setValue(Number(parseFloat(properties.maxValue)).toFixed(properties.decimalPlaces)); - } else if ( - !isNaN(parseFloat(properties.minValue)) && - parseFloat(e.target.value) < parseFloat(properties.minValue) - ) { - setValue(Number(parseFloat(properties.minValue)).toFixed(properties.decimalPlaces)); + setValue(Number(parseFloat(properties.maxValue))); } else { setValue(Number(parseFloat(e.target.value))); } fireEvent('onChange'); }; const handleBlur = (e) => { - setValue(Number(parseFloat(e.target.value).toFixed(properties.decimalPlaces))); + if (!isNaN(parseFloat(properties.minValue)) && parseFloat(e.target.value) < parseFloat(properties.minValue)) { + setValue(Number(parseFloat(properties.minValue))); + } else setValue(Number(parseFloat(e.target.value).toFixed(properties.decimalPlaces))); }; useEffect(() => { @@ -83,6 +80,8 @@ export const NumberInput = function NumberInput({ style={computedStyles} value={value} data-cy={dataCy} + min={properties.minValue} + max={properties.maxValue} /> )} {properties.loadingState === true && ( From 81a32d76ed03903c96b86a166debd4fd7f121681 Mon Sep 17 00:00:00 2001 From: Manish Kushare <37823141+manishkushare@users.noreply.github.com> Date: Thu, 31 Aug 2023 13:38:53 +0530 Subject: [PATCH 04/41] Added horizontal alignment property for each columns in the table to set the horizontal alignment individually (#7106) * added property to align the column horizontally * Revert "added property to align the column horizontally" This reverts commit fc5e54fb43544b421944c6e87a5522cdd9a5022b. * added horizontal alignment property to each column to set the horizontal alignment * added text alignment for the input element as well * handled alignment for few elements such as datepicker,tags,radio as well * code enhancement * handeld edge case * handled edge cases * updated default case to ensure that any unexpected values return a defined value. * removed unwanted code and code enhancement * fixed horizontal alignment of textarea according to column alignment * made link column alignment consistent with the horizontal alignment for the same column * adjusted horizontal alignment for image column type --- frontend/assets/translations/en.json | 3 +- frontend/src/Editor/Components/Table/Link.jsx | 2 +- .../src/Editor/Components/Table/Table.jsx | 37 +++++++--- .../Editor/Components/Table/columns/index.jsx | 29 +++++++- ...sx => ProgramaticallyHandleProperties.jsx} | 30 ++------ .../Inspector/Components/Table/Table.jsx | 40 +++++++++-- frontend/src/_styles/custom.scss | 69 +++++++++++++++++++ frontend/src/_styles/theme.scss | 2 +- 8 files changed, 164 insertions(+), 48 deletions(-) rename frontend/src/Editor/Inspector/Components/Table/{ProgramaticallyHandleToggleSwitch.jsx => ProgramaticallyHandleProperties.jsx} (72%) diff --git a/frontend/assets/translations/en.json b/frontend/assets/translations/en.json index c4ee0384d9..bb3ceaa230 100644 --- a/frontend/assets/translations/en.json +++ b/frontend/assets/translations/en.json @@ -718,7 +718,8 @@ "remove": "Remove", "addButton": "+ Add button", "addColumn": "+ Add column", - "noActionMessage": "This table doesn't have any action buttons" + "noActionMessage": "This table doesn't have any action buttons", + "horizontalAlignment":"horizontal alignment" }, "Button": { "displayName": "Button", diff --git a/frontend/src/Editor/Components/Table/Link.jsx b/frontend/src/Editor/Components/Table/Link.jsx index bcb4281da9..67083e6b05 100644 --- a/frontend/src/Editor/Components/Table/Link.jsx +++ b/frontend/src/Editor/Components/Table/Link.jsx @@ -2,7 +2,7 @@ import React from 'react'; export const Link = ({ cellValue, linkTarget }) => { return ( -
+
{column.render('Header')}
@@ -1168,6 +1171,7 @@ export function Table({ > {row.cells.map((cell, index) => { let cellProps = cell.getCellProps(); + cellProps.style.textAlign = cell.column?.horizontalAlignment; if (tableDetails.changeSet) { if (tableDetails.changeSet[cell.row.index]) { const currentColumn = columnData.find((column) => column.id === cell.column.id); @@ -1210,6 +1214,7 @@ export function Table({ cellValue, rowData, }); + const horizontalAlignment = cell.column?.horizontalAlignment; return ( // Does not require key as its already being passed by react-table via cellProps // eslint-disable-next-line react/jsx-key @@ -1217,15 +1222,20 @@ export function Table({ data-cy={`${cell.column.columnType ?? ''}${String( cell.column.id === 'rightActions' || cell.column.id === 'leftActions' ? cell.column.id : '' )}${String(cellValue ?? '').toLocaleLowerCase()}-cell-${index}`} - className={cx(`${wrapAction ? wrapAction : 'wrap'}-wrapper`, { - 'has-actions': cell.column.id === 'rightActions' || cell.column.id === 'leftActions', - 'has-text': cell.column.columnType === 'text' || isEditable, - 'has-dropdown': cell.column.columnType === 'dropdown', - 'has-multiselect': cell.column.columnType === 'multiselect', - 'has-datepicker': cell.column.columnType === 'datepicker', - 'align-items-center flex-column': cell.column.columnType === 'selector', - [cellSize]: true, - })} + className={cx( + `table-text-align-${cell.column.horizontalAlignment} ${ + wrapAction ? wrapAction : 'wrap' + }-wrapper`, + { + 'has-actions': cell.column.id === 'rightActions' || cell.column.id === 'leftActions', + 'has-text': cell.column.columnType === 'text' || isEditable, + 'has-dropdown': cell.column.columnType === 'dropdown', + 'has-multiselect': cell.column.columnType === 'multiselect', + 'has-datepicker': cell.column.columnType === 'datepicker', + 'align-items-center flex-column': cell.column.columnType === 'selector', + [cellSize]: true, + } + )} {...cellProps} style={{ ...cellProps.style, backgroundColor: cellBackgroundColor ?? 'inherit' }} onClick={(e) => { @@ -1244,7 +1254,12 @@ export function Table({ { + switch (value) { + case 'left': + return 'start'; + case 'right': + return 'end'; + case 'center': + return 'center'; + default: + return 'start'; + } + }; const updatedChangeSet = newRowsChangeSet === null ? changeSet : newRowsChangeSet; const rowChangeSet = updatedChangeSet ? updatedChangeSet[cell.row.index] : null; let cellValue = rowChangeSet ? rowChangeSet[column.key || column.name] ?? cell.value : cell.value; @@ -183,7 +196,12 @@ export default function generateColumnsData({ ); } return ( -
+
{String(cellValue)}
); @@ -251,7 +269,12 @@ export default function generateColumnsData({ ); } return ( -
+
{cellValue}
); diff --git a/frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleToggleSwitch.jsx b/frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleProperties.jsx similarity index 72% rename from frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleToggleSwitch.jsx rename to frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleProperties.jsx index 66fc8f0913..695376bb8b 100644 --- a/frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleToggleSwitch.jsx +++ b/frontend/src/Editor/Inspector/Components/Table/ProgramaticallyHandleProperties.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { CodeHinter } from '../../../CodeBuilder/CodeHinter'; -export const ProgramaticallyHandleToggleSwitch = ({ +export const ProgramaticallyHandleProperties = ({ darkMode, // eslint-disable-next-line no-unused-vars label, @@ -18,10 +18,8 @@ export const ProgramaticallyHandleToggleSwitch = ({ switch (property) { case 'isEditable': return props.isEditable; - case 'disableActionButton': return props.disableActionButton; - case 'columnVisibility': return props.columnVisibility; case 'linkTarget': @@ -30,33 +28,15 @@ export const ProgramaticallyHandleToggleSwitch = ({ return; } }; - const getOptionsForSelectElement = (property, paramMeta) => { - switch (property) { - case 'linkTarget': - return { - ...paramMeta, - options: [ - { name: 'Same window', value: '_self' }, - { name: 'New window', value: '_blank' }, - ], - }; - default: - break; - } - }; - if (paramMeta.type === 'select') { - paramMeta = getOptionsForSelectElement(property, paramMeta); - } - - const getInitialValue = (property, definition) => { + const getInitialValue = (property, definitionObj) => { if (property === 'columnVisibility') { - return definition?.value ?? `{{true}}`; + return definitionObj?.value ?? `{{true}}`; } if (property === 'linkTarget') { - return definition?.value ?? '_blank'; + return definitionObj?.value ?? '_blank'; } - return definition?.value ?? `{{false}}`; + return definitionObj?.value ?? `{{false}}`; }; const value = getValueBasedOnProperty(property, props); diff --git a/frontend/src/Editor/Inspector/Components/Table/Table.jsx b/frontend/src/Editor/Inspector/Components/Table/Table.jsx index 656d3dae4b..c5805016f6 100644 --- a/frontend/src/Editor/Inspector/Components/Table/Table.jsx +++ b/frontend/src/Editor/Inspector/Components/Table/Table.jsx @@ -13,7 +13,7 @@ import { v4 as uuidv4 } from 'uuid'; import { EventManager } from '../../EventManager'; import { CodeHinter } from '../../../CodeBuilder/CodeHinter'; import { withTranslation } from 'react-i18next'; -import { ProgramaticallyHandleToggleSwitch } from './ProgramaticallyHandleToggleSwitch'; +import { ProgramaticallyHandleProperties } from './ProgramaticallyHandleProperties'; class TableComponent extends React.Component { constructor(props) { super(props); @@ -264,6 +264,27 @@ class TableComponent extends React.Component { }} />
+
+ + { + this.onColumnItemChange(index, 'horizontalAlignment', value); + }} + fuzzySearch + placeholder={this.props.t('globals.select', 'Select') + '...'} + /> +
{(column.columnType === 'string' || column.columnType === undefined || column.columnType === 'default') && (
@@ -673,7 +694,7 @@ class TableComponent extends React.Component { )} {column.columnType === 'link' && (
-
)} {!['image', 'link'].includes(column.columnType) && ( - )} - this.onActionButtonPropertyChanged(index, 'textColor', color)} cyLabel={`action-button-text`} /> - *{ + width: fit-content !important; + } } \ No newline at end of file diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss index bbd14394ac..6e0350441b 100644 --- a/frontend/src/_styles/theme.scss +++ b/frontend/src/_styles/theme.scss @@ -3632,7 +3632,7 @@ input:focus-visible { } .jet-table-image-column { - margin: 0 auto; + width: 100%; } .modal-backdrop.show { From 17a048cd3c5ab67f24964446152da97e5683d000 Mon Sep 17 00:00:00 2001 From: Johnson Cherian Date: Thu, 31 Aug 2023 13:39:24 +0530 Subject: [PATCH 05/41] reword datasource to data source (#7247) * fix: update datasource to data source * fix: typo fix for data sources --- .../QueryManager/Components/DataSourcePicker.jsx | 11 ++++------- .../QueryManager/Components/DataSourceSelect.jsx | 4 ++-- frontend/src/Editor/QueryPanel/FilterandSortPopup.jsx | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/frontend/src/Editor/QueryManager/Components/DataSourcePicker.jsx b/frontend/src/Editor/QueryManager/Components/DataSourcePicker.jsx index e7280e2659..fc62d72121 100644 --- a/frontend/src/Editor/QueryManager/Components/DataSourcePicker.jsx +++ b/frontend/src/Editor/QueryManager/Components/DataSourcePicker.jsx @@ -48,10 +48,10 @@ function DataSourcePicker({ dataSources, staticDataSources, darkMode, globalData return ( <>

- Connect to a datasource + Connect to a data source

- Select a datasource to start creating a new query. To know more about queries in ToolJet, you can read our + Select a data source to start creating a new query. To know more about queries in ToolJet, you can read our   documentation @@ -81,7 +81,7 @@ function DataSourcePicker({ dataSources, staticDataSources, darkMode, globalData

(
-
- No global datasources have been added yet.
- Add new datasources to connect to your app! 🚀 -
+
No global data sources have been added yet.
); diff --git a/frontend/src/Editor/QueryManager/Components/DataSourceSelect.jsx b/frontend/src/Editor/QueryManager/Components/DataSourceSelect.jsx index bc707d5ff9..c6eeb2d014 100644 --- a/frontend/src/Editor/QueryManager/Components/DataSourceSelect.jsx +++ b/frontend/src/Editor/QueryManager/Components/DataSourceSelect.jsx @@ -53,7 +53,7 @@ function DataSourceSelect({ darkMode, isDisabled, selectRef, closePopup }) {
{index === 0 && (
- Global datasources + Global data sources
)} @@ -245,7 +245,7 @@ const MenuList = ({ children, getStyles, innerRef, ...props }) => { {admin && (
- + Add new datasource + + Add new data source
)} diff --git a/frontend/src/Editor/QueryPanel/FilterandSortPopup.jsx b/frontend/src/Editor/QueryPanel/FilterandSortPopup.jsx index c13c7558b1..bc98851d6a 100644 --- a/frontend/src/Editor/QueryPanel/FilterandSortPopup.jsx +++ b/frontend/src/Editor/QueryPanel/FilterandSortPopup.jsx @@ -255,7 +255,7 @@ const DataSourceSelector = ({
setSearch(e.target.value)} ref={searchBoxRef} value={search} From 97c6805d05c1bcc683c946b486f6bd920720ec7f Mon Sep 17 00:00:00 2001 From: Arpit Date: Thu, 31 Aug 2023 13:40:04 +0530 Subject: [PATCH 06/41] avoid resolving same value multiple times (#7231) --- .../src/Editor/CodeBuilder/CodeHinter.jsx | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/frontend/src/Editor/CodeBuilder/CodeHinter.jsx b/frontend/src/Editor/CodeBuilder/CodeHinter.jsx index 3cb65fe45b..3af118afa9 100644 --- a/frontend/src/Editor/CodeBuilder/CodeHinter.jsx +++ b/frontend/src/Editor/CodeBuilder/CodeHinter.jsx @@ -89,6 +89,11 @@ export function CodeHinter({ const currentState = useCurrentState(); const [realState, setRealState] = useState(currentState); const [currentValue, setCurrentValue] = useState(initialValue); + + const [prevCurrentValue, setPrevCurrentValue] = useState(null); + const [resolvedValue, setResolvedValue] = useState(null); + const [resolvingError, setResolvingError] = useState(null); + const [isFocused, setFocused] = useState(false); const [heightRef, currentHeight] = useHeight(); const isPreviewFocused = useRef(false); @@ -139,6 +144,28 @@ export function CodeHinter({ }; }, [wrapperRef, isFocused, isPreviewFocused, currentValue, prevCountRef, isOpen]); + useEffect(() => { + if (JSON.stringify(currentValue) !== JSON.stringify(prevCurrentValue)) { + const customResolvables = getCustomResolvables(); + const [preview, error] = resolveReferences(currentValue, realState, null, customResolvables, true, true); + setPrevCurrentValue(currentValue); + + if (error) { + setResolvingError(error); + setResolvedValue(null); + } else { + setResolvingError(null); + setResolvedValue(preview); + } + } + + return () => { + setPrevCurrentValue(null); + setResolvedValue(null); + setResolvingError(null); + }; + }, [JSON.stringify({ currentValue, realState })]); + function valueChanged(editor, onChange, ignoreBraces) { if (editor.getValue()?.trim() !== currentValue) { handleChange(editor, onChange, ignoreBraces, realState, componentName); @@ -185,9 +212,12 @@ export function CodeHinter({ const getPreview = () => { if (!enablePreview) return; - const customResolvables = getCustomResolvables(); - const [preview, error] = resolveReferences(currentValue, realState, null, customResolvables, true, true); + // const customResolvables = getCustomResolvables(); + // const [preview, error] = resolveReferences(currentValue, realState, null, customResolvables, true, true); + const themeCls = darkMode ? 'bg-dark py-1' : 'bg-light py-1'; + const preview = resolvedValue; + const error = resolvingError; if (error) { const err = String(error); From f0a8077415979624e7d541bf25c3a79669629049 Mon Sep 17 00:00:00 2001 From: Kiran Ashok Date: Thu, 31 Aug 2023 13:40:35 +0530 Subject: [PATCH 07/41] Bugfix :: Clicking on custom validation field and clicking outside will set isValid to false (#7065) * fix:: customrule default val * check if resolvedCustomRule is empty --- frontend/src/_helpers/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/_helpers/utils.js b/frontend/src/_helpers/utils.js index 2cc3a877d7..89c8f09ce0 100644 --- a/frontend/src/_helpers/utils.js +++ b/frontend/src/_helpers/utils.js @@ -391,7 +391,7 @@ export function validateWidget({ validationObject, widgetValue, currentState, cu } const resolvedCustomRule = resolveWidgetFieldValue(customRule, currentState, false, customResolveObjects); - if (typeof resolvedCustomRule === 'string') { + if (typeof resolvedCustomRule === 'string' && resolvedCustomRule !== '') { return { isValid: false, validationError: resolvedCustomRule }; } From 06b558c84a17e28a37d261a8b3272c0a9772da8b Mon Sep 17 00:00:00 2001 From: Nakul Nagargade <133095394+nakulnagargade@users.noreply.github.com> Date: Thu, 31 Aug 2023 13:42:32 +0530 Subject: [PATCH 08/41] Prevent app re-renders on hovering widgets in canvas (#7015) * Move version manager editor states to zustand * Update appDataStore to appVersionsManagerStore * Move released version popup state to global * Move isVersionReleased to global store * Rename appVersionsManagerStore to appVersionStore and destructure zustand data inside components * Move showComments and toggleComments states to zustand store * Move current layout from editor local states to editor store * Move hovered component to zustand * Move selected components to zustand * Merge branch 'refactor/zustand-edior-store' into zustand-editor-store-2 * Fix * FIx * Remove util function * Reolve code comments * Resolve code comments --- docs/docs/setup/env-vars.md | 2 +- .../version-2.8.0/setup/env-vars.md | 2 +- .../version-2.9.0/actions/generate-file.md | 2 +- .../version-2.9.0/setup/env-vars.md | 2 +- .../version-2.9.4/actions/generate-file.md | 2 +- .../version-2.9.4/setup/env-vars.md | 2 +- frontend/src/Editor/Box.jsx | 468 +++++++-------- frontend/src/Editor/Container.jsx | 334 ++++++----- frontend/src/Editor/DraggableBox.jsx | 532 +++++++++--------- frontend/src/Editor/Editor.jsx | 96 ++-- frontend/src/Editor/LeftSidebar/index.jsx | 1 - frontend/src/Editor/SubContainer.jsx | 4 - frontend/src/_helpers/appUtils.js | 4 +- frontend/src/_stores/editorStore.js | 57 +- 14 files changed, 802 insertions(+), 706 deletions(-) diff --git a/docs/docs/setup/env-vars.md b/docs/docs/setup/env-vars.md index 0299608bff..62e12a702b 100644 --- a/docs/docs/setup/env-vars.md +++ b/docs/docs/setup/env-vars.md @@ -325,4 +325,4 @@ By default, only embedding of public apps is permitted. By setting this variable :::caution The option is only available starting from ToolJet Enterprise Edition `2.8.0` or higher, and `2.10.0` for the Community edition and cloud version. -::: \ No newline at end of file +::: diff --git a/docs/versioned_docs/version-2.8.0/setup/env-vars.md b/docs/versioned_docs/version-2.8.0/setup/env-vars.md index 6400f6f2a5..49e3d5fedd 100644 --- a/docs/versioned_docs/version-2.8.0/setup/env-vars.md +++ b/docs/versioned_docs/version-2.8.0/setup/env-vars.md @@ -325,4 +325,4 @@ By default, only embedding of public apps is permitted. By setting this variable :::caution The option is only available starting from ToolJet Enterprise Edition `2.8.0` or higher, and `2.10.0` for the Community edition and cloud version. -::: \ No newline at end of file +::: diff --git a/docs/versioned_docs/version-2.9.0/actions/generate-file.md b/docs/versioned_docs/version-2.9.0/actions/generate-file.md index 82cab3a578..fc329c7f1e 100644 --- a/docs/versioned_docs/version-2.9.0/actions/generate-file.md +++ b/docs/versioned_docs/version-2.9.0/actions/generate-file.md @@ -49,4 +49,4 @@ To use the `Text` file format, the data field should contain a string. If you want to generate a text file based on an array of objects, you need to stringify the data before providing it. -For example, if you are using the table component to provide the data, you can enter **`{{JSON.stringify(components.table1.currentPageData)}}`** in the Data field. \ No newline at end of file +For example, if you are using the table component to provide the data, you can enter **`{{JSON.stringify(components.table1.currentPageData)}}`** in the Data field. diff --git a/docs/versioned_docs/version-2.9.0/setup/env-vars.md b/docs/versioned_docs/version-2.9.0/setup/env-vars.md index 6400f6f2a5..49e3d5fedd 100644 --- a/docs/versioned_docs/version-2.9.0/setup/env-vars.md +++ b/docs/versioned_docs/version-2.9.0/setup/env-vars.md @@ -325,4 +325,4 @@ By default, only embedding of public apps is permitted. By setting this variable :::caution The option is only available starting from ToolJet Enterprise Edition `2.8.0` or higher, and `2.10.0` for the Community edition and cloud version. -::: \ No newline at end of file +::: diff --git a/docs/versioned_docs/version-2.9.4/actions/generate-file.md b/docs/versioned_docs/version-2.9.4/actions/generate-file.md index 82cab3a578..fc329c7f1e 100644 --- a/docs/versioned_docs/version-2.9.4/actions/generate-file.md +++ b/docs/versioned_docs/version-2.9.4/actions/generate-file.md @@ -49,4 +49,4 @@ To use the `Text` file format, the data field should contain a string. If you want to generate a text file based on an array of objects, you need to stringify the data before providing it. -For example, if you are using the table component to provide the data, you can enter **`{{JSON.stringify(components.table1.currentPageData)}}`** in the Data field. \ No newline at end of file +For example, if you are using the table component to provide the data, you can enter **`{{JSON.stringify(components.table1.currentPageData)}}`** in the Data field. diff --git a/docs/versioned_docs/version-2.9.4/setup/env-vars.md b/docs/versioned_docs/version-2.9.4/setup/env-vars.md index 6400f6f2a5..49e3d5fedd 100644 --- a/docs/versioned_docs/version-2.9.4/setup/env-vars.md +++ b/docs/versioned_docs/version-2.9.4/setup/env-vars.md @@ -325,4 +325,4 @@ By default, only embedding of public apps is permitted. By setting this variable :::caution The option is only available starting from ToolJet Enterprise Edition `2.8.0` or higher, and `2.10.0` for the Community edition and cloud version. -::: \ No newline at end of file +::: diff --git a/frontend/src/Editor/Box.jsx b/frontend/src/Editor/Box.jsx index 03cb1e9f24..1e43625484 100644 --- a/frontend/src/Editor/Box.jsx +++ b/frontend/src/Editor/Box.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useMemo, useContext, useRef } from 'react'; +import React, { useEffect, useState, useMemo, useContext, useRef, memo } from 'react'; import { Button } from './Components/Button'; import { Image } from './Components/Image'; import { Text } from './Components/Text'; @@ -123,257 +123,259 @@ const AllComponents = { BoundedBox, }; -export const Box = function Box({ - id, - width, - height, - yellow, - preview, - component, - inCanvas, - onComponentClick, - onEvent, - onComponentOptionChanged, - onComponentOptionsChanged, - paramUpdated, - changeCanDrag, - containerProps, - darkMode, - removeComponent, - canvasWidth, - mode, - customResolvables, - parentId, - sideBarDebugger, - readOnly, - childComponents, -}) { - const { t } = useTranslation(); - const backgroundColor = yellow ? 'yellow' : ''; - const currentState = useCurrentState(); +export const Box = memo( + ({ + id, + width, + height, + yellow, + preview, + component, + inCanvas, + onComponentClick, + onEvent, + onComponentOptionChanged, + onComponentOptionsChanged, + paramUpdated, + changeCanDrag, + containerProps, + darkMode, + removeComponent, + canvasWidth, + mode, + customResolvables, + parentId, + sideBarDebugger, + readOnly, + childComponents, + }) => { + const { t } = useTranslation(); + const backgroundColor = yellow ? 'yellow' : ''; + const currentState = useCurrentState(); - let styles = { - height: '100%', - padding: '1px', - }; - - if (inCanvas) { - styles = { - ...styles, + let styles = { + height: '100%', + padding: '1px', }; - } - const componentMeta = useMemo(() => { - return componentTypes.find((comp) => component.component === comp.component); - }, [component]); + if (inCanvas) { + styles = { + ...styles, + }; + } - const ComponentToRender = AllComponents[component.component]; - const [renderCount, setRenderCount] = useState(0); - const [renderStartTime, setRenderStartTime] = useState(new Date()); - const [resetComponent, setResetStatus] = useState(false); + const componentMeta = useMemo(() => { + return componentTypes.find((comp) => component.component === comp.component); + }, [component]); - const resolvedProperties = resolveProperties(component, currentState, null, customResolvables); - const [validatedProperties, propertyErrors] = - mode === 'edit' && component.validate - ? validateProperties(resolvedProperties, componentMeta.properties) - : [resolvedProperties, []]; + const ComponentToRender = AllComponents[component.component]; + const [renderCount, setRenderCount] = useState(0); + const [renderStartTime, setRenderStartTime] = useState(new Date()); + const [resetComponent, setResetStatus] = useState(false); - const resolvedStyles = resolveStyles(component, currentState, null, customResolvables); - const [validatedStyles, styleErrors] = - mode === 'edit' && component.validate - ? validateProperties(resolvedStyles, componentMeta.styles) - : [resolvedStyles, []]; - validatedStyles.visibility = validatedStyles.visibility !== false ? true : false; + const resolvedProperties = resolveProperties(component, currentState, null, customResolvables); + const [validatedProperties, propertyErrors] = + mode === 'edit' && component.validate + ? validateProperties(resolvedProperties, componentMeta.properties) + : [resolvedProperties, []]; - const resolvedGeneralProperties = resolveGeneralProperties(component, currentState, null, customResolvables); - const [validatedGeneralProperties, generalPropertiesErrors] = - mode === 'edit' && component.validate - ? validateProperties(resolvedGeneralProperties, componentMeta.general) - : [resolvedGeneralProperties, []]; + const resolvedStyles = resolveStyles(component, currentState, null, customResolvables); + const [validatedStyles, styleErrors] = + mode === 'edit' && component.validate + ? validateProperties(resolvedStyles, componentMeta.styles) + : [resolvedStyles, []]; + validatedStyles.visibility = validatedStyles.visibility !== false ? true : false; - const resolvedGeneralStyles = resolveGeneralStyles(component, currentState, null, customResolvables); - resolvedStyles.visibility = resolvedStyles.visibility !== false ? true : false; - const [validatedGeneralStyles, generalStylesErrors] = - mode === 'edit' && component.validate - ? validateProperties(resolvedGeneralStyles, componentMeta.generalStyles) - : [resolvedGeneralStyles, []]; + const resolvedGeneralProperties = resolveGeneralProperties(component, currentState, null, customResolvables); + const [validatedGeneralProperties, generalPropertiesErrors] = + mode === 'edit' && component.validate + ? validateProperties(resolvedGeneralProperties, componentMeta.general) + : [resolvedGeneralProperties, []]; - const { variablesExposedForPreview, exposeToCodeHinter } = useContext(EditorContext) || {}; + const resolvedGeneralStyles = resolveGeneralStyles(component, currentState, null, customResolvables); + resolvedStyles.visibility = resolvedStyles.visibility !== false ? true : false; + const [validatedGeneralStyles, generalStylesErrors] = + mode === 'edit' && component.validate + ? validateProperties(resolvedGeneralStyles, componentMeta.generalStyles) + : [resolvedGeneralStyles, []]; - const componentActions = useRef(new Set()); + const { variablesExposedForPreview, exposeToCodeHinter } = useContext(EditorContext) || {}; - useEffect(() => { - const currentPage = currentState?.page; + const componentActions = useRef(new Set()); - const componentName = getComponentName(currentState, id); - const errorLog = Object.fromEntries( - [...propertyErrors, ...styleErrors, ...generalPropertiesErrors, ...generalStylesErrors].map((error) => [ - `${componentName} - ${error.property}`, - { - page: currentPage, - type: 'component', - kind: 'component', - strace: 'page_level', - data: { message: `${error.message}`, status: true }, - resolvedProperties: resolvedProperties, - effectiveProperties: validatedProperties, - }, - ]) - ); - sideBarDebugger?.error(errorLog); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify({ propertyErrors, styleErrors, generalPropertiesErrors })]); + useEffect(() => { + const currentPage = currentState?.page; - useEffect(() => { - setRenderCount(renderCount + 1); - if (renderCount > 10) { - setRenderCount(0); - const currentTime = new Date(); - const timeDifference = Math.abs(currentTime - renderStartTime); - if (timeDifference < 1000) { - throw Error; + const componentName = getComponentName(currentState, id); + const errorLog = Object.fromEntries( + [...propertyErrors, ...styleErrors, ...generalPropertiesErrors, ...generalStylesErrors].map((error) => [ + `${componentName} - ${error.property}`, + { + page: currentPage, + type: 'component', + kind: 'component', + strace: 'page_level', + data: { message: `${error.message}`, status: true }, + resolvedProperties: resolvedProperties, + effectiveProperties: validatedProperties, + }, + ]) + ); + sideBarDebugger?.error(errorLog); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify({ propertyErrors, styleErrors, generalPropertiesErrors })]); + + useEffect(() => { + setRenderCount(renderCount + 1); + if (renderCount > 10) { + setRenderCount(0); + const currentTime = new Date(); + const timeDifference = Math.abs(currentTime - renderStartTime); + if (timeDifference < 1000) { + throw Error; + } + setRenderStartTime(currentTime); } - setRenderStartTime(currentTime); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify({ resolvedProperties, resolvedStyles })]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify({ resolvedProperties, resolvedStyles })]); - useEffect(() => { - if (customResolvables && !readOnly && mode === 'edit') { - const newCustomResolvable = {}; - newCustomResolvable[id] = { ...customResolvables }; - exposeToCodeHinter((prevState) => ({ ...prevState, ...newCustomResolvable })); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(customResolvables), readOnly]); - - useEffect(() => { - if (resetComponent) setResetStatus(false); - }, [resetComponent]); - - let exposedVariables = currentState?.components[component.name] ?? {}; - - const fireEvent = (eventName, options) => { - if (mode === 'edit' && eventName === 'onClick') { - onComponentClick(id, component); - } - onEvent(eventName, { ...options, customVariables: { ...customResolvables }, component }); - }; - const validate = (value) => - validateWidget({ - ...{ widgetValue: value }, - ...{ validationObject: component.definition.validation, currentState }, - customResolveObjects: customResolvables, - }); - - return ( - - renderTooltip({ - props, - text: inCanvas - ? `${validatedGeneralProperties.tooltip}` - : `${t(`widget.${component.name}.description`, component.description)}`, - }) + useEffect(() => { + if (customResolvables && !readOnly && mode === 'edit') { + const newCustomResolvable = {}; + newCustomResolvable[id] = { ...customResolvables }; + exposeToCodeHinter((prevState) => ({ ...prevState, ...newCustomResolvable })); } - > -
{ + if (resetComponent) setResetStatus(false); + }, [resetComponent]); + + let exposedVariables = currentState?.components[component.name] ?? {}; + + const fireEvent = (eventName, options) => { + if (mode === 'edit' && eventName === 'onClick') { + onComponentClick(id, component); + } + onEvent(eventName, { ...options, customVariables: { ...customResolvables }, component }); + }; + const validate = (value) => + validateWidget({ + ...{ widgetValue: value }, + ...{ validationObject: component.definition.validation, currentState }, + customResolveObjects: customResolvables, + }); + + return ( + + renderTooltip({ + props, + text: inCanvas + ? `${validatedGeneralProperties.tooltip}` + : `${t(`widget.${component.name}.description`, component.description)}`, + }) + } > - {inCanvas ? ( - !resetComponent ? ( - onComponentOptionChanged(component, variable, value, id)} - setExposedVariables={(variableSet) => onComponentOptionsChanged(component, Object.entries(variableSet))} - registerAction={(actionName, func, dependencies = []) => { - if ( - Object.keys(currentState?.components ?? {}).includes(component.name) && - currentState?.components[component.name].id === id - ) { - if (!Object.keys(exposedVariables).includes(actionName)) { - func.dependencies = dependencies; - componentActions.current.add(actionName); - return onComponentOptionChanged(component, actionName, func); - } else if (exposedVariables[actionName]?.dependencies?.length === 0) { - return Promise.resolve(); - } else if ( - JSON.stringify(dependencies) !== JSON.stringify(exposedVariables[actionName]?.dependencies) || - !componentActions.current.has(actionName) +
+ {inCanvas ? ( + !resetComponent ? ( + onComponentOptionChanged(component, variable, value, id)} + setExposedVariables={(variableSet) => onComponentOptionsChanged(component, Object.entries(variableSet))} + registerAction={(actionName, func, dependencies = []) => { + if ( + Object.keys(currentState?.components ?? {}).includes(component.name) && + currentState?.components[component.name].id === id ) { - func.dependencies = dependencies; - componentActions.current.add(actionName); - return onComponentOptionChanged(component, actionName, func); + if (!Object.keys(exposedVariables).includes(actionName)) { + func.dependencies = dependencies; + componentActions.current.add(actionName); + return onComponentOptionChanged(component, actionName, func); + } else if (exposedVariables[actionName]?.dependencies?.length === 0) { + return Promise.resolve(); + } else if ( + JSON.stringify(dependencies) !== JSON.stringify(exposedVariables[actionName]?.dependencies) || + !componentActions.current.has(actionName) + ) { + func.dependencies = dependencies; + componentActions.current.add(actionName); + return onComponentOptionChanged(component, actionName, func); + } } - } - }} - fireEvent={fireEvent} - validate={validate} - parentId={parentId} - customResolvables={customResolvables} - variablesExposedForPreview={variablesExposedForPreview} - exposeToCodeHinter={exposeToCodeHinter} - setProperty={(property, value) => { - paramUpdated(id, property, { value }); - }} - mode={mode} - resetComponent={() => setResetStatus(true)} - childComponents={childComponents} - dataCy={`draggable-widget-${String(component.name).toLowerCase()}`} - > + }} + fireEvent={fireEvent} + validate={validate} + parentId={parentId} + customResolvables={customResolvables} + variablesExposedForPreview={variablesExposedForPreview} + exposeToCodeHinter={exposeToCodeHinter} + setProperty={(property, value) => { + paramUpdated(id, property, { value }); + }} + mode={mode} + resetComponent={() => setResetStatus(true)} + childComponents={childComponents} + dataCy={`draggable-widget-${String(component.name).toLowerCase()}`} + > + ) : ( + <> + ) ) : ( - <> - ) - ) : ( -
-
-
-
-
- - {t(`widget.${component.name}.displayName`, component.displayName)} - +
+
+
+
+
+ + {t(`widget.${component.name}.displayName`, component.displayName)} + +
-
- )} -
- - ); -}; + )} +
+
+ ); + } +); diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx index 37c2d6acab..a806f625eb 100644 --- a/frontend/src/Editor/Container.jsx +++ b/frontend/src/Editor/Container.jsx @@ -37,13 +37,10 @@ export const Container = ({ zoomLevel, removeComponent, deviceWindowWidth, - selectedComponents, darkMode, socket, handleUndo, handleRedo, - onComponentHover, - hoveredComponent, sideBarDebugger, currentPageId, }) => { @@ -65,10 +62,11 @@ export const Container = ({ }), shallow ); - const { showComments, currentLayout } = useEditorStore( + const { showComments, currentLayout, selectedComponents } = useEditorStore( (state) => ({ showComments: state?.showComments, currentLayout: state?.currentLayout, + selectedComponents: state?.selectedComponents, }), shallow ); @@ -194,21 +192,24 @@ export const Container = ({ return (x * 100) / canvasWidth; } - function updateCanvasHeight(components) { - const maxHeight = Object.values(components).reduce((max, component) => { - const layout = component?.layouts?.[currentLayout]; - if (!layout) { - return max; - } - const sum = layout.top + layout.height; - return Math.max(max, sum); - }, 0); + const updateCanvasHeight = useCallback( + (components) => { + const maxHeight = Object.values(components).reduce((max, component) => { + const layout = component?.layouts?.[currentLayout]; + if (!layout) { + return max; + } + const sum = layout.top + layout.height; + return Math.max(max, sum); + }, 0); - const bottomPadding = mode === 'view' ? 100 : 300; - const frameHeight = mode === 'view' ? (appDefinition.globalSettings?.hideHeader ? 0 : 45) : 85; + const bottomPadding = mode === 'view' ? 100 : 300; + const frameHeight = mode === 'view' ? 45 : 85; - setCanvasHeight(`max(100vh - ${frameHeight}px, ${maxHeight + bottomPadding}px)`); - } + setCanvasHeight(`max(100vh - ${frameHeight}px, ${maxHeight + bottomPadding}px)`); + }, + [setCanvasHeight, currentLayout, mode] + ); useEffect(() => { setIsDragging(draggingState); @@ -242,7 +243,6 @@ export const Container = ({ const canvasBoundingRect = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect(); const componentMeta = componentTypes.find((component) => component.component === item.component.component); console.log('adding new component'); - const newComponent = addNewWidgetToTheEditor( componentMeta, monitor, @@ -274,129 +274,136 @@ export const Container = ({ [moveBox] ); - function onDragStop(e, componentId, direction, currentLayout) { - if (isVersionReleased) { - enableReleasedVersionPopupState(); - return; - } - // const id = componentId ? componentId : uuidv4(); + const onDragStop = useCallback( + (e, componentId, direction, currentLayout) => { + if (isVersionReleased) { + enableReleasedVersionPopupState(); + return; + } + // const id = componentId ? componentId : uuidv4(); - // Get the width of the canvas - const canvasBounds = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect(); - const canvasWidth = canvasBounds?.width; - const nodeBounds = direction.node.getBoundingClientRect(); + // Get the width of the canvas + const canvasBounds = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect(); + const canvasWidth = canvasBounds?.width; + const nodeBounds = direction.node.getBoundingClientRect(); - // Computing the left offset - const leftOffset = nodeBounds.x - canvasBounds.x; - const currentLeftOffset = boxes[componentId].layouts[currentLayout].left; - const leftDiff = currentLeftOffset - convertXToPercentage(leftOffset, canvasWidth); + // Computing the left offset + const leftOffset = nodeBounds.x - canvasBounds.x; + const currentLeftOffset = boxes[componentId]?.layouts?.[currentLayout]?.left; + const leftDiff = currentLeftOffset - convertXToPercentage(leftOffset, canvasWidth); - // Computing the top offset - // const currentTopOffset = boxes[componentId].layouts[currentLayout].top; - const topDiff = boxes[componentId].layouts[currentLayout].top - (nodeBounds.y - canvasBounds.y); + // Computing the top offset + // const currentTopOffset = boxes[componentId].layouts[currentLayout].top; + const topDiff = boxes[componentId].layouts[currentLayout].top - (nodeBounds.y - canvasBounds.y); - let newBoxes = { ...boxes }; + let newBoxes = { ...boxes }; - for (const selectedComponent of selectedComponents) { - newBoxes = produce(newBoxes, (draft) => { - if (draft[selectedComponent.id]) { - const topOffset = draft[selectedComponent.id].layouts[currentLayout].top; - const leftOffset = draft[selectedComponent.id].layouts[currentLayout].left; + for (const selectedComponent of selectedComponents) { + newBoxes = produce(newBoxes, (draft) => { + if (draft[selectedComponent.id]) { + const topOffset = draft[selectedComponent.id].layouts[currentLayout].top; + const leftOffset = draft[selectedComponent.id].layouts[currentLayout].left; - draft[selectedComponent.id].layouts[currentLayout].top = topOffset - topDiff; - draft[selectedComponent.id].layouts[currentLayout].left = leftOffset - leftDiff; - } - }); - } + draft[selectedComponent.id].layouts[currentLayout].top = topOffset - topDiff; + draft[selectedComponent.id].layouts[currentLayout].left = leftOffset - leftDiff; + } + }); + } - setBoxes(newBoxes); - updateCanvasHeight(newBoxes); - } + setBoxes(newBoxes); + updateCanvasHeight(newBoxes); + }, + [isVersionReleased, enableReleasedVersionPopupState, boxes, setBoxes, selectedComponents, updateCanvasHeight] + ); - function onResizeStop(id, e, direction, ref, d, position) { - if (isVersionReleased) { - enableReleasedVersionPopupState(); - return; - } + const onResizeStop = useCallback( + (id, e, direction, ref, d, position) => { + if (isVersionReleased) { + enableReleasedVersionPopupState(); + return; + } - const deltaWidth = Math.round(d.width / gridWidth) * gridWidth; //rounding of width of element to nearest mulitple of gridWidth - const deltaHeight = d.height; + const deltaWidth = Math.round(d.width / gridWidth) * gridWidth; //rounding of width of element to nearest mulitple of gridWidth + const deltaHeight = d.height; - if (deltaWidth === 0 && deltaHeight === 0) { - return; - } + if (deltaWidth === 0 && deltaHeight === 0) { + return; + } - let { x, y } = position; - x = Math.round(x / gridWidth) * gridWidth; + let { x, y } = position; + x = Math.round(x / gridWidth) * gridWidth; - const defaultData = { - top: 100, - left: 0, - width: 445, - height: 500, - }; + const defaultData = { + top: 100, + left: 0, + width: 445, + height: 500, + }; - let { left, top, width, height } = boxes[id]['layouts'][currentLayout] || defaultData; + let { left, top, width, height } = boxes[id]['layouts'][currentLayout] || defaultData; - const boundingRect = document.getElementsByClassName('canvas-area')[0].getBoundingClientRect(); - const canvasWidth = boundingRect?.width; + const boundingRect = document.getElementsByClassName('canvas-area')[0].getBoundingClientRect(); + const canvasWidth = boundingRect?.width; - //round the width to nearest multiple of gridwidth before converting to % - const currentWidth = (canvasWidth * width) / NO_OF_GRIDS; - let newWidth = currentWidth + deltaWidth; - newWidth = Math.round(newWidth / gridWidth) * gridWidth; - width = (newWidth * NO_OF_GRIDS) / canvasWidth; + //round the width to nearest multiple of gridwidth before converting to % + const currentWidth = (canvasWidth * width) / NO_OF_GRIDS; + let newWidth = currentWidth + deltaWidth; + newWidth = Math.round(newWidth / gridWidth) * gridWidth; + width = (newWidth * NO_OF_GRIDS) / canvasWidth; - height = height + deltaHeight; + height = height + deltaHeight; - top = y; - left = (x * 100) / canvasWidth; + top = y; + left = (x * 100) / canvasWidth; - let newBoxes = { - ...boxes, - [id]: { - ...boxes[id], - layouts: { - ...boxes[id]['layouts'], - [currentLayout]: { - ...boxes[id]['layouts'][currentLayout], - width, - height, - top, - left, + let newBoxes = { + ...boxes, + [id]: { + ...boxes[id], + layouts: { + ...boxes[id]['layouts'], + [currentLayout]: { + ...boxes[id]['layouts'][currentLayout], + width, + height, + top, + left, + }, }, }, - }, - }; + }; - setBoxes(newBoxes); - updateCanvasHeight(newBoxes); - } + setBoxes(newBoxes); + updateCanvasHeight(newBoxes); + }, + [setBoxes, currentLayout, boxes, enableReleasedVersionPopupState, isVersionReleased, updateCanvasHeight, gridWidth] + ); - function paramUpdated(id, param, value) { - if (Object.keys(value).length > 0) { - setBoxes((boxes) => - update(boxes, { - [id]: { - $merge: { - component: { - ...boxes[id].component, - definition: { - ...boxes[id].component.definition, - properties: { - ...boxes[id].component.definition.properties, - [param]: value, + const paramUpdated = useCallback( + (id, param, value) => { + if (Object.keys(value).length > 0) { + setBoxes((boxes) => + update(boxes, { + [id]: { + $merge: { + component: { + ...boxes[id].component, + definition: { + ...boxes[id].component.definition, + properties: { + ...boxes[id].component.definition.properties, + [param]: value, + }, }, }, }, }, - }, - }) - ); - } - } - - React.useEffect(() => {}, [selectedComponents]); + }) + ); + } + }, + [setBoxes] + ); const handleAddThread = async (e) => { e.stopPropogation && e.stopPropogation(); @@ -503,6 +510,66 @@ export const Container = ({ return componentWithChildren; }, [components]); + const resizingStatusChanged = useCallback( + (status) => { + setIsResizing(status); + }, + [setIsResizing] + ); + + const draggingStatusChanged = useCallback( + (status) => { + setIsDragging(status); + }, + [setIsDragging] + ); + + const containerProps = useMemo(() => { + return { + mode, + snapToGrid, + onComponentClick, + onEvent, + appDefinition, + appDefinitionChanged, + currentState, + onComponentOptionChanged, + onComponentOptionsChanged, + appLoading, + zoomLevel, + setSelectedComponent, + removeComponent, + currentLayout, + deviceWindowWidth, + selectedComponents, + darkMode, + sideBarDebugger, + currentPageId, + childComponents, + }; + }, [ + mode, + snapToGrid, + onComponentClick, + onEvent, + appDefinition, + appDefinitionChanged, + currentState, + onComponentOptionChanged, + onComponentOptionsChanged, + appLoading, + zoomLevel, + setSelectedComponent, + removeComponent, + currentLayout, + deviceWindowWidth, + selectedComponents, + darkMode, + sideBarDebugger, + currentPageId, + childComponents, + ]); + return (
setIsResizing(status)} - draggingStatusChanged={(status) => setIsDragging(status)} + resizingStatusChanged={resizingStatusChanged} + draggingStatusChanged={draggingStatusChanged} inCanvas={true} zoomLevel={zoomLevel} setSelectedComponent={setSelectedComponent} removeComponent={removeComponent} deviceWindowWidth={deviceWindowWidth} - isSelectedComponent={ - mode === 'edit' ? selectedComponents.find((component) => component.id === key) : false - } darkMode={darkMode} - onComponentHover={onComponentHover} - hoveredComponent={hoveredComponent} sideBarDebugger={sideBarDebugger} - isMultipleComponentsSelected={selectedComponents?.length > 1 ? true : false} childComponents={childComponents[key]} - containerProps={{ - mode, - snapToGrid, - onComponentClick, - onEvent, - appDefinition, - appDefinitionChanged, - currentState, - onComponentOptionChanged, - onComponentOptionsChanged, - appLoading, - zoomLevel, - setSelectedComponent, - removeComponent, - currentLayout, - deviceWindowWidth, - selectedComponents, - darkMode, - onComponentHover, - hoveredComponent, - sideBarDebugger, - addDefaultChildren, - currentPageId, - childComponents, - }} - isVersionReleased={isVersionReleased} + containerProps={{ ...containerProps, addDefaultChildren }} /> ); } diff --git a/frontend/src/Editor/DraggableBox.jsx b/frontend/src/Editor/DraggableBox.jsx index a600727135..bd38c36a40 100644 --- a/frontend/src/Editor/DraggableBox.jsx +++ b/frontend/src/Editor/DraggableBox.jsx @@ -1,5 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import cx from 'classnames'; import { useDrag } from 'react-dnd'; import { ItemTypes } from './ItemTypes'; @@ -63,271 +63,287 @@ function getStyles(isDragging, isSelectedComponent) { }; } -export const DraggableBox = function DraggableBox({ - id, - className, - mode, - title, - _left, - _top, - parent, - allComponents, - component, - index, - inCanvas, - onEvent, - onComponentClick, - onComponentOptionChanged, - onComponentOptionsChanged, - onResizeStop, - onDragStop, - paramUpdated, - resizingStatusChanged, - zoomLevel, - containerProps, - setSelectedComponent, - removeComponent, - layouts, - isSelectedComponent, - draggingStatusChanged, - darkMode, - canvasWidth, - readOnly, - customResolvables, - parentId, - hoveredComponent, - onComponentHover, - sideBarDebugger, - isMultipleComponentsSelected, - childComponents = null, - isVersionReleased, -}) { - const [isResizing, setResizing] = useState(false); - const [isDragging2, setDragging] = useState(false); - const [canDrag, setCanDrag] = useState(true); - const [mouseOver, setMouseOver] = useState(false); - const currentState = useCurrentState(); - - const { currentLayout } = useEditorStore( - (state) => ({ - currentLayout: state?.currentLayout, - }), - shallow - ); - useEffect(() => { - setMouseOver(hoveredComponent === id); - }, [hoveredComponent]); - - const [{ isDragging }, drag, preview] = useDrag( - () => ({ - type: ItemTypes.BOX, - item: { - id, - title, - component, - zoomLevel, - parent, - layouts, - canvasWidth, - currentLayout, - }, - collect: (monitor) => ({ - isDragging: monitor.isDragging(), +export const DraggableBox = React.memo( + ({ + id, + className, + mode, + title, + _left, + _top, + parent, + allComponents, + component, + index, + inCanvas, + onEvent, + onComponentClick, + onComponentOptionChanged, + onComponentOptionsChanged, + onResizeStop, + onDragStop, + paramUpdated, + resizingStatusChanged, + zoomLevel, + containerProps, + setSelectedComponent, + removeComponent, + layouts, + draggingStatusChanged, + darkMode, + canvasWidth, + readOnly, + customResolvables, + parentId, + sideBarDebugger, + childComponents = null, + }) => { + const [isResizing, setResizing] = useState(false); + const [isDragging2, setDragging] = useState(false); + const [canDrag, setCanDrag] = useState(true); + const { + currentLayout, + setHoveredComponent, + mouseOver, + selectionInProgress, + isSelectedComponent, + isMultipleComponentsSelected, + } = useEditorStore( + (state) => ({ + currentLayout: state?.currentLayout, + setHoveredComponent: state?.actions?.setHoveredComponent, + mouseOver: state?.hoveredComponent === id, + selectionInProgress: state?.selectionInProgress, + isSelectedComponent: + mode === 'edit' ? state?.selectedComponents?.some((component) => component?.id === id) : false, + isMultipleComponentsSelected: state?.selectedComponents?.length > 1 ? true : false, }), - }), - [id, title, component, index, zoomLevel, parent, layouts, canvasWidth, currentLayout] - ); + shallow + ); + const currentState = useCurrentState(); - useEffect(() => { - preview(getEmptyImage(), { captureDraggingState: true }); - }, [isDragging]); + const [{ isDragging }, drag, preview] = useDrag( + () => ({ + type: ItemTypes.BOX, + item: { + id, + title, + component, + zoomLevel, + parent, + layouts, + canvasWidth, + currentLayout, + }, + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + }), + }), + [id, title, component, index, currentLayout, zoomLevel, parent, layouts, canvasWidth] + ); - useEffect(() => { - if (resizingStatusChanged) { - resizingStatusChanged(isResizing); - } - }, [isResizing]); + useEffect(() => { + preview(getEmptyImage(), { captureDraggingState: true }); + }, [isDragging]); - useEffect(() => { - if (draggingStatusChanged) { - draggingStatusChanged(isDragging2); - } - - if (isDragging2 && !isSelectedComponent) { - setSelectedComponent(id, component); - } - }, [isDragging2]); - - const style = { - display: 'inline-block', - alignItems: 'center', - justifyContent: 'center', - padding: '0px', - }; - - let _refProps = {}; - - if (mode === 'edit' && canDrag) { - _refProps = { - ref: drag, - }; - } - - function changeCanDrag(newState) { - setCanDrag(newState); - } - - const defaultData = { - top: 100, - left: 0, - width: 445, - height: 500, - }; - - const layoutData = inCanvas ? layouts[currentLayout] || defaultData : defaultData; - const gridWidth = canvasWidth / NO_OF_GRIDS; - const width = (canvasWidth * layoutData.width) / NO_OF_GRIDS; - - const configWidgetHandlerForModalComponent = - !isSelectedComponent && - component.component === 'Modal' && - resolveWidgetFieldValue(component.definition.properties.useDefaultButton, currentState)?.value === false; - - return ( -
{ + if (resizingStatusChanged) { + resizingStatusChanged(isResizing); } - style={!inCanvas ? {} : { width: computeWidth() }} - > - {inCanvas ? ( -
{ - if (e.currentTarget.className.includes(`widget-${id}`)) { - onComponentHover?.(id); - e.stopPropagation(); - } - }} - onMouseLeave={() => onComponentHover?.(false)} - style={getStyles(isDragging, isSelectedComponent)} - > - setResizing(true)} - onDrag={(e) => { - e.preventDefault(); - e.stopImmediatePropagation(); - if (!isDragging2) { - setDragging(true); + }, [isResizing]); + + useEffect(() => { + if (draggingStatusChanged) { + draggingStatusChanged(isDragging2); + } + + if (isDragging2 && !isSelectedComponent) { + setSelectedComponent(id, component); + } + }, [isDragging2]); + + const style = { + display: 'inline-block', + alignItems: 'center', + justifyContent: 'center', + padding: '0px', + }; + + let _refProps = {}; + + if (mode === 'edit' && canDrag) { + _refProps = { + ref: drag, + }; + } + + const changeCanDrag = useCallback( + (newState) => { + setCanDrag(newState); + }, + [setCanDrag] + ); + + const defaultData = { + top: 100, + left: 0, + width: 445, + height: 500, + }; + + const layoutData = inCanvas ? layouts[currentLayout] || defaultData : defaultData; + const gridWidth = canvasWidth / NO_OF_GRIDS; + const width = (canvasWidth * layoutData.width) / NO_OF_GRIDS; + + const configWidgetHandlerForModalComponent = + !isSelectedComponent && + component.component === 'Modal' && + resolveWidgetFieldValue(component.definition.properties.useDefaultButton, currentState)?.value === false; + + const onComponentHover = (id) => { + if (selectionInProgress) return; + setHoveredComponent(id); + }; + + return ( +
+ {inCanvas ? ( +
{ + if (e.currentTarget.className.includes(`widget-${id}`)) { + onComponentHover?.(id); + e.stopPropagation(); } }} - resizeHandleClasses={isSelectedComponent || mouseOver ? resizerClasses : {}} - resizeHandleStyles={resizerStyles} - enableResizing={mode === 'edit' && !readOnly} - disableDragging={mode !== 'edit' || readOnly} - onDragStop={(e, direction) => { - setDragging(false); - onDragStop(e, id, direction, currentLayout, layoutData); + onMouseLeave={() => { + setHoveredComponent(''); }} - cancel={`div.table-responsive.jet-data-table, div.calendar-widget, div.text-input, .textarea, .map-widget, .range-slider, .kanban-container, div.real-canvas`} - onResizeStop={(e, direction, ref, d, position) => { - setResizing(false); - onResizeStop(id, e, direction, ref, d, position); - }} - bounds={parent !== undefined ? `#canvas-${parent}` : '.real-canvas'} - widgetId={id} + style={getStyles(isDragging, isSelectedComponent)} > -
- {mode === 'edit' && - !readOnly && - (configWidgetHandlerForModalComponent || mouseOver || isSelectedComponent) && - !isResizing && ( - setResizing(true)} + onDrag={(e) => { + e.preventDefault(); + e.stopImmediatePropagation(); + if (!isDragging2) { + setDragging(true); + } + }} + resizeHandleClasses={isSelectedComponent || mouseOver ? resizerClasses : {}} + resizeHandleStyles={resizerStyles} + enableResizing={mode === 'edit' && !readOnly} + disableDragging={mode !== 'edit' || readOnly} + onDragStop={(e, direction) => { + setDragging(false); + onDragStop(e, id, direction, currentLayout, layoutData); + }} + cancel={`div.table-responsive.jet-data-table, div.calendar-widget, div.text-input, .textarea, .map-widget, .range-slider, .kanban-container, div.real-canvas`} + onResizeStop={(e, direction, ref, d, position) => { + setResizing(false); + onResizeStop(id, e, direction, ref, d, position); + }} + bounds={parent !== undefined ? `#canvas-${parent}` : '.real-canvas'} + widgetId={id} + > +
+ {mode === 'edit' && + !readOnly && + (configWidgetHandlerForModalComponent || mouseOver || isSelectedComponent) && + !isResizing && ( + + )} + + - )} - - - -
- -
- ) : ( -
- - - -
- )} -
- ); -}; + +
+
+
+ ) : ( +
+ + + +
+ )} +
+ ); + } +); diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index d20c542b7e..dd88a62bbd 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -122,7 +122,8 @@ class EditorComponent extends React.Component { apps: [], queryConfirmationList: [], isSourceSelected: false, - selectionInProgress: false, + isSaving: false, + isUnsavedQueriesAvailable: false, scrollOptions: {}, currentPageId: defaultPageId, pages: {}, @@ -642,10 +643,10 @@ class EditorComponent extends React.Component { }; removeComponents = () => { - if (!this.props.isVersionReleased && this.state?.selectedComponents?.length > 1) { - let newDefinition = cloneDeep(this.state.appDefinition); - const selectedComponents = this.state?.selectedComponents; + const selectedComponents = this.props?.selectedComponents; + if (!this.props.isVersionReleased && selectedComponents?.length > 1) { + let newDefinition = cloneDeep(this.state.appDefinition); removeSelectedComponent(this.state.currentPageId, newDefinition, selectedComponents); const platform = navigator?.userAgentData?.platform || navigator?.platform || 'unknown'; if (platform.toLowerCase().indexOf('mac') > -1) { @@ -744,8 +745,8 @@ class EditorComponent extends React.Component { }; handleEditorEscapeKeyPress = () => { - if (this.state?.selectedComponents?.length > 0) { - this.setState({ selectedComponents: [] }); + if (this.props?.selectedComponents?.length > 0) { + this.props.setSelectedComponents([]); this.handleInspectorView(); } }; @@ -754,7 +755,7 @@ class EditorComponent extends React.Component { let appDefinition = JSON.parse(JSON.stringify(this.state.appDefinition)); let newComponents = appDefinition.pages[this.state.currentPageId].components; - for (const selectedComponent of this.state.selectedComponents) { + for (const selectedComponent of this.props.selectedComponents) { newComponents = produce(newComponents, (draft) => { let top = draft[selectedComponent.id].layouts[this.props.currentLayout].top; let left = draft[selectedComponent.id].layouts[this.props.currentLayout].left; @@ -835,20 +836,16 @@ class EditorComponent extends React.Component { }; setSelectedComponent = (id, component, multiSelect = false) => { - if (this.state.selectedComponents.length === 0 || !multiSelect) { + if (this.props.selectedComponents.length === 0 || !multiSelect) { this.switchSidebarTab(1); } else { this.switchSidebarTab(2); } - const isAlreadySelected = this.state.selectedComponents.find((component) => component.id === id); + const isAlreadySelected = this.props.selectedComponents.find((component) => component.id === id); if (!isAlreadySelected) { - this.setState((prevState) => { - return { - selectedComponents: [...(multiSelect ? prevState.selectedComponents : []), { id, component }], - }; - }); + this.props.setSelectedComponents([...(multiSelect ? this.props.selectedComponents : []), { id, component }]); } }; @@ -944,19 +941,9 @@ class EditorComponent extends React.Component { }; handleComponentClick = (id, component) => { - this.setState({ - selectedComponent: { id, component }, - }); this.switchSidebarTab(1); }; - handleComponentHover = (id) => { - if (this.state.selectionInProgress) return; - this.setState({ - hoveredComponent: id, - }); - }; - sideBarDebugger = { error: (data) => { debuggerActions.error(this, data); @@ -982,20 +969,17 @@ class EditorComponent extends React.Component { runQuery = (queryId, queryName) => runQuery(this, queryId, queryName); onAreaSelectionStart = (e) => { - const isMultiSelect = e.inputEvent.shiftKey || this.state.selectedComponents.length > 0; - this.setState((prevState) => { - return { - selectionInProgress: true, - selectedComponents: [...(isMultiSelect ? prevState.selectedComponents : [])], - }; - }); + const isMultiSelect = e.inputEvent.shiftKey || this.props.selectedComponents.length > 0; + this.props.setSelectionInProgress(true); + this.props.setSelectedComponents([...(isMultiSelect ? this.props.selectedComponents : [])]); }; onAreaSelection = (e) => { e.added.forEach((el) => { el.classList.add('resizer-select'); }); - if (this.state.selectionInProgress) { + + if (this.props.selectionInProgress) { e.removed.forEach((el) => { el.classList.remove('resizer-select'); }); @@ -1004,7 +988,7 @@ class EditorComponent extends React.Component { onAreaSelectionEnd = (e) => { const currentPageId = this.state.currentPageId; - this.setState({ selectionInProgress: false }); + this.props.setSelectionInProgress(false); e.selected.forEach((el, index) => { const id = el.getAttribute('widgetid'); const component = this.state.appDefinition.pages[currentPageId].components[id].component; @@ -1024,13 +1008,13 @@ class EditorComponent extends React.Component { onAreaSelectionDrag = (e) => { if (this.selectionDragRef.current) { e.stop(); - this.state.selectionInProgress && this.setState({ selectionInProgress: false }); + this.props.selectionInProgress && this.props.setSelectionInProgress(false); } }; onAreaSelectionDragEnd = () => { this.selectionDragRef.current = false; - this.state.selectionInProgress && this.setState({ selectionInProgress: false }); + this.props.selectionInProgress && this.props.setSelectionInProgress(false); }; addNewPage = ({ name, handle }) => { @@ -1476,13 +1460,18 @@ class EditorComponent extends React.Component { return defaultCanvasMinWidth; } }; + handleCanvasContainerMouseUp = (e) => { + if (['real-canvas', 'modal'].includes(e.target.className)) { + this.props.setSelectedComponents([]); + this.setState({ currentSidebarTab: 2 }); + } + }; handleEditorMarginLeftChange = (value) => this.setState({ editorMarginLeft: value }); render() { const { currentSidebarTab, - selectedComponents = [], appDefinition, appId, slug, @@ -1493,9 +1482,9 @@ class EditorComponent extends React.Component { deviceWindowWidth, apps, defaultComponentStateComputed, - hoveredComponent, queryConfirmationList, } = this.state; + const selectedComponents = this?.props?.selectedComponents; const currentState = this.props?.currentState; const editingVersion = this.props?.editingVersion; const appVersionPreviewLink = editingVersion @@ -1624,11 +1613,7 @@ class EditorComponent extends React.Component { height: this.computeCanvasContainerHeight(), background: !this.props.darkMode && '#f4f6fa', }} - onMouseUp={(e) => { - if (['real-canvas', 'modal'].includes(e.target.className)) { - this.setState({ selectedComponents: [], currentSidebarTab: 2, hoveredComponent: false }); - } - }} + onMouseUp={this.handleCanvasContainerMouseUp} ref={this.canvasContainerRef} onScroll={() => { this.selectionRef.current.checkScroll(); @@ -1692,7 +1677,6 @@ class EditorComponent extends React.Component { mode={'edit'} zoomLevel={zoomLevel} deviceWindowWidth={deviceWindowWidth} - selectedComponents={selectedComponents} appLoading={isLoading} onEvent={this.handleEvent} onComponentOptionChanged={this.handleOnComponentOptionChanged} @@ -1702,8 +1686,6 @@ class EditorComponent extends React.Component { handleRedo={this.handleRedo} removeComponent={this.removeComponent} onComponentClick={this.handleComponentClick} - onComponentHover={this.handleComponentHover} - hoveredComponent={hoveredComponent} sideBarDebugger={this.sideBarDebugger} currentPageId={this.state.currentPageId} /> @@ -1787,10 +1769,21 @@ class EditorComponent extends React.Component { } const withStore = (Component) => (props) => { - const { showComments, currentLayout } = useEditorStore( + const { + showComments, + currentLayout, + selectionInProgress, + setSelectionInProgress, + selectedComponents, + setSelectedComponents, + } = useEditorStore( (state) => ({ showComments: state?.showComments, currentLayout: state?.currentLayout, + selectionInProgress: state?.selectionInProgress, + setSelectionInProgress: state?.actions?.setSelectionInProgress, + selectedComponents: state?.selectedComponents, + setSelectedComponents: state?.actions?.setSelectedComponents, }), shallow ); @@ -1798,17 +1791,20 @@ const withStore = (Component) => (props) => { (state) => ({ isVersionReleased: state.isVersionReleased, editingVersion: state.editingVersion }), shallow ); - const currentState = useCurrentState(); return ( ); }; diff --git a/frontend/src/Editor/LeftSidebar/index.jsx b/frontend/src/Editor/LeftSidebar/index.jsx index cf9dd3f2ae..8f8dc6d502 100644 --- a/frontend/src/Editor/LeftSidebar/index.jsx +++ b/frontend/src/Editor/LeftSidebar/index.jsx @@ -28,7 +28,6 @@ export const LeftSidebar = forwardRef((props, ref) => { dataSourcesChanged, globalDataSourcesChanged, dataQueriesChanged, - appDefinition, setSelectedComponent, removeComponent, diff --git a/frontend/src/Editor/SubContainer.jsx b/frontend/src/Editor/SubContainer.jsx index b610c189ba..74b3dc24dc 100644 --- a/frontend/src/Editor/SubContainer.jsx +++ b/frontend/src/Editor/SubContainer.jsx @@ -544,9 +544,6 @@ export const SubContainer = ({ setSelectedComponent={setSelectedComponent} selectedComponent={selectedComponent} deviceWindowWidth={deviceWindowWidth} - isSelectedComponent={ - mode === 'edit' ? selectedComponents.find((component) => component.id === key) : false - } removeComponent={customRemoveComponent} canvasWidth={_containerCanvasWidth} readOnly={readOnly} @@ -556,7 +553,6 @@ export const SubContainer = ({ hoveredComponent={hoveredComponent} parentId={parentComponent?.name} sideBarDebugger={sideBarDebugger} - isMultipleComponentsSelected={selectedComponents?.length > 1 ? true : false} exposedVariables={exposedVariables ?? {}} childComponents={childComponents[key]} containerProps={{ diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js index d8dd78b29f..39bf68afaa 100644 --- a/frontend/src/_helpers/appUtils.js +++ b/frontend/src/_helpers/appUtils.js @@ -32,6 +32,7 @@ import { useDataQueriesStore } from '@/_stores/dataQueriesStore'; import { useQueryPanelStore } from '@/_stores/queryPanelStore'; import { useCurrentStateStore, getCurrentState } from '@/_stores/currentStateStore'; import { useAppVersionStore } from '@/_stores/appVersionStore'; +import { useEditorStore } from '@/_stores/editorStore'; const ERROR_TYPES = Object.freeze({ ReferenceError: 'ReferenceError', @@ -1323,7 +1324,8 @@ const updateNewComponents = (pageId, appDefinition, newComponents, updateAppDefi }; export const cloneComponents = (_ref, updateAppDefinition, isCloning = true, isCut = false) => { - const { selectedComponents, appDefinition, currentPageId } = _ref.state; + const { appDefinition, currentPageId } = _ref.state; + const selectedComponents = useEditorStore.getState().selectedComponents; if (selectedComponents.length < 1) return getSelectedText(); const { components: allComponents } = appDefinition.pages[currentPageId]; let newDefinition = _.cloneDeep(appDefinition); diff --git a/frontend/src/_stores/editorStore.js b/frontend/src/_stores/editorStore.js index bb4b83ba8c..ba1a1b5269 100644 --- a/frontend/src/_stores/editorStore.js +++ b/frontend/src/_stores/editorStore.js @@ -1,8 +1,23 @@ import { create, zustandDevTools } from './utils'; +const STORE_NAME = 'Editor'; + +const ACTIONS = { + SET_SHOW_COMMENTS: 'SET_SHOW_COMMENTS', + SET_HOVERED_COMPONENT: 'SET_HOVERED_COMPONENT', + TOGGLE_COMMENTS: 'TOGGLE_COMMENTS', + TOGGLE_CURRENT_LAYOUT: 'TOGGLE_CURRENT_LAYOUT', + SET_SELECTION_IN_PROGRESS: 'SET_SELECTION_IN_PROGRESS', + SET_SELECTED_COMPONENTS: 'SET_SELECTED_COMPONENTS', + SET_IS_EDITOR_ACTIVE: 'SET_IS_EDITOR_ACTIVE', +}; + const initialState = { currentLayout: 'desktop', showComments: false, + hoveredComponent: '', + selectionInProgress: false, + selectedComponents: [], isEditorActive: false, }; @@ -11,12 +26,46 @@ export const useEditorStore = create( (set, get) => ({ ...initialState, actions: { - setShowComments: (showComments) => set({ showComments }), - toggleComments: () => set({ showComments: !get().showComments }), - toggleCurrentLayout: (currentLayout) => set({ currentLayout }), + setShowComments: (showComments) => + set({ showComments }, false, { + type: ACTIONS.SET_HOVERED_COMPONENT, + showComments, + }), + toggleComments: () => + set({ showComments: !get().showComments }, false, { + type: ACTIONS.TOGGLE_COMMENTS, + }), + toggleCurrentLayout: (currentLayout) => + set({ currentLayout }, false, { + type: ACTIONS.TOGGLE_CURRENT_LAYOUT, + currentLayout, + }), setIsEditorActive: (isEditorActive) => set(() => ({ isEditorActive })), + setHoveredComponent: (hoveredComponent) => + set({ hoveredComponent }, false, { + type: ACTIONS.SET_HOVERED_COMPONENT, + hoveredComponent, + }), + setSelectionInProgress: (isSelectionInProgress) => { + set( + { + isSelectionInProgress, + }, + false, + { type: ACTIONS.SET_SELECTION_IN_PROGRESS } + ); + }, + setSelectedComponents: (selectedComponents) => { + set( + { + selectedComponents, + }, + false, + { type: ACTIONS.SET_SELECTED_COMPONENTS } + ); + }, }, }), - { name: 'Editor Store' } + { name: STORE_NAME } ) ); From cadb581f59b2d84709bc0b5a9cc7afee04f03ed0 Mon Sep 17 00:00:00 2001 From: Johnson Cherian Date: Thu, 31 Aug 2023 13:45:14 +0530 Subject: [PATCH 09/41] fix: fallback message added if componentname not passed into CodeHinter for error msg (#7276) --- frontend/src/Editor/CodeBuilder/CodeHinter.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/Editor/CodeBuilder/CodeHinter.jsx b/frontend/src/Editor/CodeBuilder/CodeHinter.jsx index 3af118afa9..2cf221b7e6 100644 --- a/frontend/src/Editor/CodeBuilder/CodeHinter.jsx +++ b/frontend/src/Editor/CodeBuilder/CodeHinter.jsx @@ -221,7 +221,9 @@ export function CodeHinter({ if (error) { const err = String(error); - const errorMessage = err.includes('.run()') ? `${err} in ${componentName.split('::')[0]}'s field` : err; + const errorMessage = err.includes('.run()') + ? `${err} in ${componentName ? componentName.split('::')[0] + "'s" : 'fx'} field` + : err; return (
From dd7801d4a635fdd8cde9a025bcad9bf484a6b157 Mon Sep 17 00:00:00 2001 From: Johnson Cherian Date: Thu, 31 Aug 2023 13:49:52 +0530 Subject: [PATCH 10/41] Fixed minor ui issues in query manager (#7318) * fix: removed duplicate tooltips * fix: break codehinter preview content if text is too long * fix: removed unused imports --- frontend/src/Editor/QueryPanel/QueryCard.jsx | 30 +++++++------------- frontend/src/_styles/theme.scss | 1 + 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/frontend/src/Editor/QueryPanel/QueryCard.jsx b/frontend/src/Editor/QueryPanel/QueryCard.jsx index 9e147729ed..aadc1df88c 100644 --- a/frontend/src/Editor/QueryPanel/QueryCard.jsx +++ b/frontend/src/Editor/QueryPanel/QueryCard.jsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import { Tooltip } from 'react-tooltip'; import { checkExistingQueryName } from '@/_helpers/appUtils'; import { Confirm } from '../Viewer/Confirm'; @@ -95,24 +94,17 @@ export const QueryCard = ({ dataQuery, darkMode = false, editorRef, appId }) => }} /> ) : ( - {dataQuery.name}} - > -
- - {dataQuery.name} - {' '} - - {!isQueryRunnable(dataQuery) && Draft} -
-
+
+ + {dataQuery.name} + {' '} + + {!isQueryRunnable(dataQuery) && Draft} +
)}
diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss index 6e0350441b..861626ab85 100644 --- a/frontend/src/_styles/theme.scss +++ b/frontend/src/_styles/theme.scss @@ -3113,6 +3113,7 @@ input:focus-visible { border-bottom-right-radius: 3px; box-sizing: border-box; font-family: "Source Code Pro", monospace; + word-break: break-word; .heading { font-weight: 700; From 31bd09eefbe5462c5941b5e83f2e473d4eb268be Mon Sep 17 00:00:00 2001 From: Syed Abdul Rahman <137684137+S-Abdul-Rahman@users.noreply.github.com> Date: Thu, 31 Aug 2023 14:27:14 +0530 Subject: [PATCH 11/41] fixed checkbox and toogle color (#7359) --- frontend/src/_styles/theme.scss | 56 +++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss index 861626ab85..1398083681 100644 --- a/frontend/src/_styles/theme.scss +++ b/frontend/src/_styles/theme.scss @@ -2103,7 +2103,7 @@ button { } .form-check-input:checked { - background-color: var(--indigo9) !important; + background-color: var(--indigo9); border-color: rgba(101, 109, 119, 0.24); } @@ -6000,33 +6000,33 @@ div#driver-page-overlay { -webkit-appearance: none; -moz-appearance: none; appearance: none; - transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; + transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; &:hover { - background: var(--slate1); - border: 1px solid var(--slate8); - -webkit-box-shadow: none; - box-shadow: none; - outline: none; + background: var(--slate1); + border: 1px solid var(--slate8); + -webkit-box-shadow: none; + box-shadow: none; + outline: none; } &:focus-visible { - background: var(--slate1); - border: 1px solid var(--slate8); - outline: none; + background: var(--slate1); + border: 1px solid var(--slate8); + outline: none; } &:active { - background: var(--indigo2); - border: 1px solid var(--indigo9); - box-shadow: none; + background: var(--indigo2); + border: 1px solid var(--indigo9); + box-shadow: none; } &:disabled { - background: var(--slate3); - border: 1px solid var(--slate8); - color: var(--slate9); - cursor: not-allowed; + background: var(--slate3); + border: 1px solid var(--slate8); + color: var(--slate9); + cursor: not-allowed; } } @@ -8966,7 +8966,8 @@ tbody { } .workspace-variable-header { - width: 880px;; + width: 880px; + ; margin: 0 auto; display: flex; padding: 0; @@ -9460,7 +9461,8 @@ tbody { } -.manage-groups-permission-apps, .apps-constant-permission-wrap { +.manage-groups-permission-apps, +.apps-constant-permission-wrap { border-bottom: 1px solid var(--slate5); } @@ -9485,7 +9487,8 @@ tbody { height: 72px; } -.apps-folder-permission-wrap, .apps-variable-permission-wrap { +.apps-folder-permission-wrap, +.apps-variable-permission-wrap { height: 44px; border-bottom: 1px solid var(--slate5); } @@ -9536,13 +9539,14 @@ tbody { border-radius: 6px; align-items: center; - + .select-item:hover { background-color: var(--slate3); } .item-renderer { align-items: center; + span { font-size: 12px; color: var(--slate12) @@ -9615,7 +9619,8 @@ tbody { .item-renderer { - align-items: center!important; + align-items: center !important; + span { font-size: 12px; color: var(--slate12) @@ -10696,6 +10701,7 @@ tbody { .workspace-variable-table-card { height: calc(100vh - 208px); } + .workspace-constant-table-card { margin: 0 auto; width: 880px; @@ -10729,6 +10735,7 @@ tbody { border-width: 0px !important; } } + .constant-table-wrapper { tr { border-width: 0px !important; @@ -11125,15 +11132,18 @@ tbody { } -#tooltip-for-org-constant-cell, #tooltip-for-org-input-disabled { +#tooltip-for-org-constant-cell, +#tooltip-for-org-input-disabled { padding: 12px 16px !important; white-space: pre-line !important; max-width: 500px !important; z-index: 1 !important; + .react-tooltip-arrow { background: inherit !important; } } + .query-rename-input { &:focus, From aabe7bb49287b7a125de46aac7b3529d8b0d6cf2 Mon Sep 17 00:00:00 2001 From: Kiran Ashok Date: Fri, 1 Sep 2023 13:55:03 +0530 Subject: [PATCH 12/41] Feature :: Exposing CSA for child widgets of form and listview (#7142) * feat :: exposing child widget of form and listview , removing register action * feat :: exposing only components and not child items * fix :: removing value of funcation info * lint fixes * reverted * test commit :: lint fix * fix :: initial render showing all components * fix :: data and children values are same * kanban breaking fix * fix for exposed variable not being accessible in initial render * comments * fix :: all components showing up in inspector during initial render for form and listview widget * reduce rerenders in button due to exposed vars * combining useeffects for lesser rerenders * fix showing of child widgets of list and form in inspector during intial render * fix :: ui break * combining useeffects in form * test commit :: reducing rerenders fromsetexposed variable called multipole time * fix :: reduce rerenders by combining multiple setExosedvariable calls * Revert "fix :: reduce rerenders by combining multiple setExosedvariable calls" This reverts commit 78c9e469444843fbbb7b0632632d54f30254c4ee. * bugfix * bugfix :: events not firing * fixes and revertions * fix :: perf checking for parent in box in case of form and listview * fix :: initial render bug * fix :: removed csa functions from listview data key * fix bugs , actions not working * fix :: dependencies * fix :: testing bugs * tabs:: event firing bug * fix :: multiselect not firing event * bugfix :: multiselect csa not triggering * fix :: kanban csa * fix :: table csa , kanban move card * fix :: bug filepicker csa --- frontend/src/Editor/Box.jsx | 28 +--- .../Components/BoundedBox/RenderEditor.jsx | 3 +- .../Components/BoundedBox/RenderHighlight.jsx | 6 +- frontend/src/Editor/Components/Button.jsx | 47 ++---- .../src/Editor/Components/ButtonGroup.jsx | 12 +- frontend/src/Editor/Components/Checkbox.jsx | 24 +-- .../src/Editor/Components/ColorPicker.jsx | 21 +-- frontend/src/Editor/Components/Datepicker.jsx | 5 +- frontend/src/Editor/Components/DropDown.jsx | 21 ++- frontend/src/Editor/Components/FilePicker.jsx | 13 +- frontend/src/Editor/Components/Form/Form.jsx | 28 ++-- frontend/src/Editor/Components/Icon.jsx | 30 ++-- .../Components/Kanban/Components/Item.jsx | 7 +- .../Editor/Components/Kanban/KanbanBoard.jsx | 93 +++++------ .../Editor/Components/KanbanBoard/Board.jsx | 9 +- .../Editor/Components/KanbanBoard/Card.jsx | 6 +- frontend/src/Editor/Components/Link.jsx | 16 +- frontend/src/Editor/Components/Listview.jsx | 30 ++-- frontend/src/Editor/Components/Map/Map.jsx | 14 +- frontend/src/Editor/Components/Modal.jsx | 23 +-- .../src/Editor/Components/Multiselect.jsx | 48 +++--- .../src/Editor/Components/PasswordInput.jsx | 3 +- .../src/Editor/Components/RadioButton.jsx | 17 +- frontend/src/Editor/Components/Steps.jsx | 3 +- .../Components/Table/AddNewRowComponent.jsx | 6 +- .../src/Editor/Components/Table/Table.jsx | 155 ++++++++---------- frontend/src/Editor/Components/Tabs.jsx | 25 ++- frontend/src/Editor/Components/Text.jsx | 32 +--- frontend/src/Editor/Components/TextArea.jsx | 24 +-- frontend/src/Editor/Components/TextInput.jsx | 72 ++++---- frontend/src/Editor/Components/TreeSelect.jsx | 7 +- frontend/src/Editor/DraggableBox.jsx | 2 + .../src/Editor/Inspector/EventManager.jsx | 14 +- frontend/src/Editor/SubContainer.jsx | 30 +++- frontend/src/_helpers/appUtils.js | 28 +++- .../src/_ui/JSONTreeViewer/JSONNodeValue.jsx | 23 +-- 36 files changed, 443 insertions(+), 482 deletions(-) diff --git a/frontend/src/Editor/Box.jsx b/frontend/src/Editor/Box.jsx index 1e43625484..a612bc35de 100644 --- a/frontend/src/Editor/Box.jsx +++ b/frontend/src/Editor/Box.jsx @@ -148,6 +148,7 @@ export const Box = memo( sideBarDebugger, readOnly, childComponents, + parentKey, }) => { const { t } = useTranslation(); const backgroundColor = yellow ? 'yellow' : ''; @@ -203,6 +204,12 @@ export const Box = memo( const componentActions = useRef(new Set()); + useEffect(() => { + onComponentOptionChanged && onComponentOptionChanged(component, 'id', id); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); /*computeComponentState was not getting the id on initial render therefore exposed variables were not set. + computeComponentState was being executed before addNewWidgetToTheEditor was completed.*/ + useEffect(() => { const currentPage = currentState?.page; @@ -312,27 +319,6 @@ export const Box = memo( styles={{ ...validatedStyles, boxShadow: validatedGeneralStyles?.boxShadow }} setExposedVariable={(variable, value) => onComponentOptionChanged(component, variable, value, id)} setExposedVariables={(variableSet) => onComponentOptionsChanged(component, Object.entries(variableSet))} - registerAction={(actionName, func, dependencies = []) => { - if ( - Object.keys(currentState?.components ?? {}).includes(component.name) && - currentState?.components[component.name].id === id - ) { - if (!Object.keys(exposedVariables).includes(actionName)) { - func.dependencies = dependencies; - componentActions.current.add(actionName); - return onComponentOptionChanged(component, actionName, func); - } else if (exposedVariables[actionName]?.dependencies?.length === 0) { - return Promise.resolve(); - } else if ( - JSON.stringify(dependencies) !== JSON.stringify(exposedVariables[actionName]?.dependencies) || - !componentActions.current.has(actionName) - ) { - func.dependencies = dependencies; - componentActions.current.add(actionName); - return onComponentOptionChanged(component, actionName, func); - } - } - }} fireEvent={fireEvent} validate={validate} parentId={parentId} diff --git a/frontend/src/Editor/Components/BoundedBox/RenderEditor.jsx b/frontend/src/Editor/Components/BoundedBox/RenderEditor.jsx index d1a153ec32..fd272e00a0 100644 --- a/frontend/src/Editor/Components/BoundedBox/RenderEditor.jsx +++ b/frontend/src/Editor/Components/BoundedBox/RenderEditor.jsx @@ -46,7 +46,8 @@ export const RenderEditor = ({ }, }); - setExposedVariable('annotations', getExposedAnnotations(annotations)).then(() => fireEvent('onChange')); + setExposedVariable('annotations', getExposedAnnotations(annotations)); + fireEvent('onChange'); return annotations; }); }} diff --git a/frontend/src/Editor/Components/BoundedBox/RenderHighlight.jsx b/frontend/src/Editor/Components/BoundedBox/RenderHighlight.jsx index 60f281d363..e4b68330a2 100644 --- a/frontend/src/Editor/Components/BoundedBox/RenderHighlight.jsx +++ b/frontend/src/Editor/Components/BoundedBox/RenderHighlight.jsx @@ -51,7 +51,8 @@ export const RenderHighlight = ({ const annotations = prevState.filter((annotation) => { return annotation.data.id !== data.id; }); - setExposedVariable('annotations', getExposedAnnotations(annotations)).then(() => fireEvent('onChange')); + setExposedVariable('annotations', getExposedAnnotations(annotations)); + fireEvent('onChange'); return annotations; }); }} @@ -95,7 +96,8 @@ export const RenderHighlight = ({ } return acc; }, []); - setExposedVariable('annotations', getExposedAnnotations(annotations)).then(() => fireEvent('onChange')); + setExposedVariable('annotations', getExposedAnnotations(annotations)); + fireEvent('onChange'); return annotations; }); diff --git a/frontend/src/Editor/Components/Button.jsx b/frontend/src/Editor/Components/Button.jsx index fa54e57ee0..635b994416 100644 --- a/frontend/src/Editor/Components/Button.jsx +++ b/frontend/src/Editor/Components/Button.jsx @@ -3,7 +3,7 @@ import cx from 'classnames'; const tinycolor = require('tinycolor2'); export const Button = function Button(props) { - const { height, properties, styles, fireEvent, registerAction, id, dataCy, setExposedVariable } = props; + const { height, properties, styles, fireEvent, id, dataCy, setExposedVariable } = props; const { backgroundColor, textColor, borderRadius, loaderColor, disabledState, borderColor, boxShadow } = styles; const [label, setLabel] = useState(properties.text); @@ -45,48 +45,31 @@ export const Button = function Button(props) { boxShadow: boxShadow, }; - registerAction( - 'click', - async function () { + useEffect(() => { + setExposedVariable('click', async function () { if (!disable) { fireEvent('onClick'); } - }, - [disable] - ); - - registerAction( - 'setText', - async function (text) { + }); + setExposedVariable('setText', async function (text) { setLabel(text); setExposedVariable('buttonText', text); - }, - [setLabel] - ); + }); - registerAction( - 'disable', - async function (value) { + setExposedVariable('disable', async function (value) { setDisable(value); - }, - [setDisable] - ); + }); - registerAction( - 'visibility', - async function (value) { + setExposedVariable('visibility', async function (value) { setVisibility(value); - }, - [setVisibility] - ); + }); - registerAction( - 'loading', - async function (value) { + setExposedVariable('loading', async function (value) { setLoading(value); - }, - [setLoading] - ); + }); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [disable, setLabel, setDisable, setVisibility, setLoading]); const hasCustomBackground = backgroundColor?.charAt() === '#'; if (hasCustomBackground) { diff --git a/frontend/src/Editor/Components/ButtonGroup.jsx b/frontend/src/Editor/Components/ButtonGroup.jsx index 665cbebb1a..9711820bc7 100644 --- a/frontend/src/Editor/Components/ButtonGroup.jsx +++ b/frontend/src/Editor/Components/ButtonGroup.jsx @@ -65,16 +65,20 @@ export const ButtonGroup = function Button({ const copyDefaultActive = defaultActive; copyDefaultActive?.splice(copyDefaultActive?.indexOf(values[index]), 1); setDefaultActive(copyDefaultActive); - setExposedVariable('selected', copyDefaultActive.join(',')).then(() => fireEvent('onClick')); + setExposedVariable('selected', copyDefaultActive.join(',')); + fireEvent('onClick'); } else if (multiSelection) { - setExposedVariable('selected', [...defaultActive, values[index]].join(',')).then(() => fireEvent('onClick')); + setExposedVariable('selected', [...defaultActive, values[index]].join(',')); + fireEvent('onClick'); setDefaultActive([...defaultActive, values[index]]); } else if (!multiSelection) { - setExposedVariable('selected', [values[index]]).then(() => fireEvent('onClick')); + setExposedVariable('selected', [values[index]]); + fireEvent('onClick'); setDefaultActive([values[index]]); } if (values?.length == 0) { - setExposedVariable('selected', []).then(() => fireEvent('onClick')); + setExposedVariable('selected', []); + fireEvent('onClick'); } }; return ( diff --git a/frontend/src/Editor/Components/Checkbox.jsx b/frontend/src/Editor/Components/Checkbox.jsx index e118e2788b..5601faf9d9 100644 --- a/frontend/src/Editor/Components/Checkbox.jsx +++ b/frontend/src/Editor/Components/Checkbox.jsx @@ -6,7 +6,6 @@ export const Checkbox = function Checkbox({ styles, fireEvent, setExposedVariable, - registerAction, darkMode, dataCy, }) { @@ -28,20 +27,23 @@ export const Checkbox = function Checkbox({ } } useEffect(() => { + const setCheckedAndNotify = async (status) => { + await setExposedVariable('value', status); + if (status) { + fireEvent('onCheck'); + } else { + fireEvent('onUnCheck'); + } + setChecked(status); + }; + setExposedVariable('value', defaultValueFromProperties); setDefaultvalue(defaultValueFromProperties); setChecked(defaultValueFromProperties); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [defaultValueFromProperties]); + setExposedVariable('setChecked', setCheckedAndNotify); - registerAction( - 'setChecked', - async function (status) { - setExposedVariable('value', status).then(() => (status ? fireEvent('onCheck') : fireEvent('onUnCheck'))); - setChecked(status); - }, - [setChecked] - ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [defaultValueFromProperties, setChecked]); return (
{ + setExposedVariable('setColor', async function (colorCode) { if (/^#(([\dA-Fa-f]{3}){1,2}|([\dA-Fa-f]{4}){1,2})$/.test(colorCode)) { if (colorCode !== color) { setColor(colorCode); setExposedVariable('selectedColorHex', `${colorCode}`); setExposedVariable('selectedColorRGB', hexToRgb(colorCode)); - setExposedVariable('selectedColorRGBA', hexToRgba(colorCode)).then(() => fireEvent('onChange')); + setExposedVariable('selectedColorRGBA', hexToRgba(colorCode)); + fireEvent('onChange'); } } else { setExposedVariable('selectedColorHex', 'undefined'); setExposedVariable('selectedColorRGB', 'undefined'); - setExposedVariable('selectedColorRGBA', 'undefined').then(() => fireEvent('onChange')); + setExposedVariable('selectedColorRGBA', 'undefined'); + fireEvent('onChange'); setColor('Invalid Color'); } - }, - [setColor] - ); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setColor]); useEffect(() => { if (/^#(([\dA-Fa-f]{3}){1,2}|([\dA-Fa-f]{4}){1,2})$/.test(defaultColor)) { @@ -90,7 +90,8 @@ export const ColorPicker = function ({ setColor(hexColor); setExposedVariable('selectedColorHex', `${hexColor}`); setExposedVariable('selectedColorRGB', `rgb(${r},${g},${b})`); - setExposedVariable('selectedColorRGBA', `rgb(${r},${g},${b},${a})`).then(() => fireEvent('onChange')); + setExposedVariable('selectedColorRGBA', `rgb(${r},${g},${b},${a})`); + fireEvent('onChange'); } }; //background color style for the div dispaying box filled by selected color diff --git a/frontend/src/Editor/Components/Datepicker.jsx b/frontend/src/Editor/Components/Datepicker.jsx index 2b5f238a72..6e71e40582 100644 --- a/frontend/src/Editor/Components/Datepicker.jsx +++ b/frontend/src/Editor/Components/Datepicker.jsx @@ -41,9 +41,8 @@ export const Datepicker = function Datepicker({ setShowValidationError(true); setDate(date); const dateString = computeDateString(date); - setExposedVariable('value', dateString).then(() => { - fireEvent('onSelect'); - }); + setExposedVariable('value', dateString); + fireEvent('onSelect'); }; useEffect(() => { diff --git a/frontend/src/Editor/Components/DropDown.jsx b/frontend/src/Editor/Components/DropDown.jsx index 831e2e9e0d..380c4345be 100644 --- a/frontend/src/Editor/Components/DropDown.jsx +++ b/frontend/src/Editor/Components/DropDown.jsx @@ -13,7 +13,6 @@ export const DropDown = function DropDown({ id, component, exposedVariables, - registerAction, dataCy, }) { let { label, value, advanced, schema, placeholder, display_values, values } = properties; @@ -22,6 +21,9 @@ export const DropDown = function DropDown({ const { value: exposedValue } = exposedVariables; const [showValidationError, setShowValidationError] = useState(false); + const validationData = validate(value); + const { isValid, validationError } = validationData; + function findDefaultItem(schema) { const foundItem = schema?.find((item) => item?.default === true); return !hasVisibleFalse(foundItem?.value) ? foundItem?.value : undefined; @@ -73,16 +75,12 @@ export const DropDown = function DropDown({ } } - registerAction( - 'selectOption', - async function (value) { + useEffect(() => { + setExposedVariable('selectOption', async function (value) { selectOption(value); - }, - [JSON.stringify(values), setCurrentValue, JSON.stringify(display_values)] - ); - - const validationData = validate(value); - const { isValid, validationError } = validationData; + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify(values), setCurrentValue, JSON.stringify(display_values)]); useEffect(() => { setExposedVariable('isValid', isValid); @@ -248,7 +246,8 @@ export const DropDown = function DropDown({ setShowValidationError(true); if (actionProps.action === 'select-option') { setCurrentValue(selectedOption.value); - setExposedVariable('value', selectedOption.value).then(() => fireEvent('onSelect')); + setExposedVariable('value', selectedOption.value); + fireEvent('onSelect'); setExposedVariable('selectedOptionLabel', selectedOption.label); } }} diff --git a/frontend/src/Editor/Components/FilePicker.jsx b/frontend/src/Editor/Components/FilePicker.jsx index 6492e549ad..517acdf545 100644 --- a/frontend/src/Editor/Components/FilePicker.jsx +++ b/frontend/src/Editor/Components/FilePicker.jsx @@ -14,7 +14,7 @@ export const FilePicker = ({ onEvent, darkMode, styles, - registerAction, + setExposedVariable, dataCy, }) => { const currentState = useCurrentState(); @@ -294,17 +294,12 @@ export const FilePicker = ({ setShowSelectedFiles(false); } onComponentOptionChanged(component, 'file', selectedFiles, id); + setExposedVariable('clearFiles', async function () { + setSelectedFiles([]); + }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedFiles]); - registerAction( - 'clearFiles', - async function () { - setSelectedFiles([]); - }, - [setSelectedFiles] - ); - return (
diff --git a/frontend/src/Editor/Components/Form/Form.jsx b/frontend/src/Editor/Components/Form/Form.jsx index 59fa0c043e..ad5cdbaf2c 100644 --- a/frontend/src/Editor/Components/Form/Form.jsx +++ b/frontend/src/Editor/Components/Form/Form.jsx @@ -7,7 +7,7 @@ import _, { omit } from 'lodash'; import { Box } from '@/Editor/Box'; import { generateUIComponents } from './FormUtils'; import { useMounted } from '@/_hooks/use-mount'; - +import { removeFunctionObjects } from '@/_helpers/appUtils'; export const Form = function Form(props) { const { id, @@ -22,7 +22,6 @@ export const Form = function Form(props) { currentState, fireEvent, properties, - registerAction, resetComponent, childComponents, onEvent, @@ -51,20 +50,20 @@ export const Form = function Form(props) { const [isValid, setValidation] = useState(true); const [uiComponents, setUIComponents] = useState([]); const mounted = useMounted(); - registerAction('resetForm', async function () { - resetComponent(); - }); - registerAction( - 'submitForm', - async function () { + + useEffect(() => { + setExposedVariable('resetForm', async function () { + resetComponent(); + }); + setExposedVariable('submitForm', async function () { if (isValid) { onEvent('onSubmit', { component }).then(() => resetComponent()); } else { fireEvent('onInvalid'); } - }, - [isValid] - ); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isValid]); const extractData = (data) => { const result = {}; @@ -87,7 +86,6 @@ export const Form = function Form(props) { }; useEffect(() => { - // resetComponent() if (mounted) resetComponent(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(JSONSchema)]); @@ -108,6 +106,7 @@ export const Form = function Form(props) { if (childComponents === null) { setExposedVariable('data', formattedChildData); + setExposedVariable('children', formattedChildData); setExposedVariable('isValid', childValidation); return setValidation(childValidation); } @@ -127,8 +126,9 @@ export const Form = function Form(props) { // eslint-disable-next-line no-unused-vars Object.entries(formattedChildData).map(([key, { keyValue, ...rest }]) => [key, rest]) ); - - setExposedVariable('data', formattedChildData); + const formattedChildDataClone = _.cloneDeep(formattedChildData); + setExposedVariable('children', formattedChildDataClone); + setExposedVariable('data', removeFunctionObjects(formattedChildData)); setExposedVariable('isValid', childValidation); setValidation(childValidation); diff --git a/frontend/src/Editor/Components/Icon.jsx b/frontend/src/Editor/Components/Icon.jsx index 67a0c4cabb..4c82ad1542 100644 --- a/frontend/src/Editor/Components/Icon.jsx +++ b/frontend/src/Editor/Components/Icon.jsx @@ -3,7 +3,17 @@ import React, { useState, useEffect } from 'react'; import * as Icons from '@tabler/icons-react'; import cx from 'classnames'; -export const Icon = ({ properties, styles, fireEvent, width, height, registerAction, darkMode, dataCy, component }) => { +export const Icon = ({ + properties, + styles, + fireEvent, + width, + height, + setExposedVariable, + darkMode, + dataCy, + component, +}) => { const { icon } = properties; const { iconColor, visibility, boxShadow } = styles; // eslint-disable-next-line import/namespace @@ -20,17 +30,15 @@ export const Icon = ({ properties, styles, fireEvent, width, height, registerAct // eslint-disable-next-line react-hooks/exhaustive-deps }, [visibility]); - registerAction( - 'setVisibility', - async function (visibility) { + useEffect(() => { + setExposedVariable('setVisibility', async function (visibility) { setIconVisibility(visibility); - }, - [setIconVisibility] - ); - - registerAction('click', async function () { - fireEvent('onClick'); - }); + }); + setExposedVariable('click', async function () { + fireEvent('onClick'); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setIconVisibility]); return (
{ - setShowModal(true); - fireEvent('onCardSelected'); - }); + setExposedVariable('lastSelectedCard', cardDataAsObj[value]); + setShowModal(true); + fireEvent('onCardSelected'); }} >
{ if (shouldUpdateData.current) { shouldUpdateData.current = false; - setExposedVariable('updatedCardData', getData(cardDataAsObj)).then(() => { - // fireEvent('onUpdate'); - }); + setExposedVariable('updatedCardData', getData(cardDataAsObj)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [shouldUpdateData.current, JSON.stringify(cardDataAsObj)]); @@ -89,15 +86,14 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef }) { droppableItemsColumnId.current = containers.find((container) => items[container]?.length > 0); }, [items, containers]); - registerAction( - 'updateCardData', - async function (cardId, value) { + useEffect(() => { + setExposedVariable('updateCardData', async function (cardId, value) { if (cardDataAsObj[cardId] === undefined) return toast.error('Card not found'); const cardToBeUpdated = { ...cardDataAsObj[cardId] }; cardDataAsObj[cardId] = value; const diffKeys = Object.keys(diff(cardToBeUpdated, value)); if (lastSelectedCard?.id === cardId) { - return setExposedVariables({ + setExposedVariables({ lastSelectedCard: cardDataAsObj[cardId], lastUpdatedCard: cardDataAsObj[cardId], lastCardUpdate: diffKeys.map((key) => { @@ -106,20 +102,17 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef }) { }; }), updatedCardData: getData(cardDataAsObj), - }).then(() => { - fireEvent('onUpdate'); }); - } - setExposedVariable('updatedCardData', getData(cardDataAsObj)).then(() => { fireEvent('onUpdate'); - }); - }, - [lastSelectedCard, JSON.stringify(cardDataAsObj)] - ); + } + setExposedVariable('updatedCardData', getData(cardDataAsObj)); + fireEvent('onUpdate'); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [lastSelectedCard, JSON.stringify(cardDataAsObj)]); - registerAction( - 'moveCard', - async function (cardId, columnId) { + useEffect(() => { + setExposedVariable('moveCard', async function (cardId, columnId) { if (cardDataAsObj[cardId] === undefined) return toast.error('Card not found'); if (cardDataAsObj[cardId]['columnId'] === columnId) return; const cardToBeMoved = { ...cardDataAsObj[cardId] }; @@ -128,7 +121,7 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef }) { setItems((items) => ({ ...items, [originColumnId]: items[originColumnId].filter((id) => id !== cardId), - [columnId]: [cardId, ...items[columnId]], + [columnId]: items[columnId] && [cardId, ...items[columnId]], })); cardDataAsObj[cardId] = { ...cardDataAsObj[cardId], columnId: columnId }; const lastCardMovement = { @@ -138,14 +131,14 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef }) { destinationIndex: 0, cardDetails: { ...cardDataAsObj[cardId] }, }; - setExposedVariable('lastCardMovement', lastCardMovement).then(() => fireEvent('onCardMoved')); - }, - [items, JSON.stringify(cardDataAsObj)] - ); + setExposedVariable('lastCardMovement', lastCardMovement); + fireEvent('onCardMoved'); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [items, JSON.stringify(cardDataAsObj)]); - registerAction( - 'addCard', - async function (cardDetails) { + useEffect(() => { + setExposedVariable('addCard', async function (cardDetails) { if (cardDataAsObj[cardDetails.id]) return toast.error('Card already exists'); if (cardDetails?.columnId === undefined || items[cardDetails?.columnId] === undefined) return toast.error('Column Id not found'); @@ -156,17 +149,14 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef }) { [columnId]: [...items[columnId], cardDetails.id], })); - setExposedVariables({ lastAddedCard: { ...cardDetails }, updatedCardData: getData(cardDataAsObj) }).then(() => { - fireEvent('onCardAdded'); - // fireEvent('onUpdate'); - }); - }, - [items, JSON.stringify(cardDataAsObj)] - ); + setExposedVariables({ lastAddedCard: { ...cardDetails }, updatedCardData: getData(cardDataAsObj) }); + fireEvent('onCardAdded'); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [items, JSON.stringify(cardDataAsObj)]); - registerAction( - 'deleteCard', - async function (cardId) { + useEffect(() => { + setExposedVariable('deleteCard', async function (cardId) { if (cardDataAsObj[cardId] === undefined) return toast.error('Card not found'); const columnId = cardDataAsObj[cardId]['columnId']; const deletedCard = cardDataAsObj[cardId]; @@ -176,13 +166,11 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef }) { ...items, [columnId]: items[columnId].filter((id) => id !== cardId), })); - setExposedVariables({ lastRemovedCard: { ...deletedCard }, updatedCardData: getData(cardDataAsObj) }).then(() => { - fireEvent('onCardRemoved'); - // fireEvent('onUpdate'); - }); - }, - [showModal, JSON.stringify(cardDataAsObj)] - ); + setExposedVariables({ lastRemovedCard: { ...deletedCard }, updatedCardData: getData(cardDataAsObj) }); + fireEvent('onCardRemoved'); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [showModal, JSON.stringify(cardDataAsObj)]); const [clonedItems, setClonedItems] = useState(null); const sensors = useSensors( @@ -292,9 +280,8 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef }) { ...items, [activeContainer]: items[activeContainer].filter((id) => id !== activeId), })); - setExposedVariable('lastRemovedCard', { ...deletedCard }).then(() => { - fireEvent('onCardRemoved'); - }); + setExposedVariable('lastRemovedCard', { ...deletedCard }); + fireEvent('onCardRemoved'); setActiveId(null); return; } @@ -319,15 +306,15 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef }) { destinationIndex: overIndex, cardDetails: { ...cardDataAsObj[active.id] }, }; - setExposedVariable('lastCardMovement', lastCardMovement).then(() => fireEvent('onCardMoved')); + setExposedVariable('lastCardMovement', lastCardMovement); + fireEvent('onCardMoved'); } else if (cardMovementRef.current !== null) { const { cardDetails, destinationColumnId } = cardMovementRef.current; if (cardDetails?.id === over?.id && destinationColumnId === overContainer) { shouldUpdateData.current = true; - setExposedVariable('lastCardMovement', { ...cardMovementRef.current }).then(() => { - cardMovementRef.current = null; - fireEvent('onCardMoved'); - }); + setExposedVariable('lastCardMovement', { ...cardMovementRef.current }); + cardMovementRef.current = null; + fireEvent('onCardMoved'); } } } diff --git a/frontend/src/Editor/Components/KanbanBoard/Board.jsx b/frontend/src/Editor/Components/KanbanBoard/Board.jsx index e1557cf194..86a7e9408a 100644 --- a/frontend/src/Editor/Components/KanbanBoard/Board.jsx +++ b/frontend/src/Editor/Components/KanbanBoard/Board.jsx @@ -31,7 +31,8 @@ function Board({ height, state, colStyles, setState, fireEvent, setExposedVariab if (!newState[keyIndex]['cards']) [(newState[keyIndex]['cards'] = [])]; newState[keyIndex]['cards'].push(newItem); setState(newState); - setExposedVariable('lastAddedCard', newItem).then(() => fireEvent('onCardAdded')); + setExposedVariable('lastAddedCard', newItem); + fireEvent('onCardAdded'); }; function onDragEnd(result) { @@ -71,7 +72,8 @@ function Board({ height, state, colStyles, setState, fireEvent, setExposedVariab destinationCardIndex: dInd, cardDetails, }; - setExposedVariable('lastCardMovement', movementDetails).then(() => fireEvent('onCardMoved')); + setExposedVariable('lastCardMovement', movementDetails); + fireEvent('onCardMoved'); } } @@ -91,7 +93,8 @@ function Board({ height, state, colStyles, setState, fireEvent, setExposedVariab const newState = state.map((column, index) => (index === columnIndex ? updatedColumn : column)); setState(newState); - setExposedVariable('lastUpdatedCard', updatedCard).then(() => fireEvent('onCardUpdated')); + setExposedVariable('lastUpdatedCard', updatedCard); + fireEvent('onCardUpdated'); }; return ( diff --git a/frontend/src/Editor/Components/KanbanBoard/Card.jsx b/frontend/src/Editor/Components/KanbanBoard/Card.jsx index febcdae996..0642987029 100644 --- a/frontend/src/Editor/Components/KanbanBoard/Card.jsx +++ b/frontend/src/Editor/Components/KanbanBoard/Card.jsx @@ -34,7 +34,8 @@ export const Card = ({ const newState = [...state]; const removedCard = newState[colIndex]['cards'].splice(cardIndex, 1)[0]; updateCb(newState); - setExposedVariable('lastRemovedCard', removedCard).then(() => fireEvent('onCardRemoved')); + setExposedVariable('lastRemovedCard', removedCard); + fireEvent('onCardRemoved'); }; const draggableId = item.id ?? uuidv4(); @@ -54,7 +55,8 @@ export const Card = ({ const handleCardClick = (event) => { handleEventPopoverOptions(event); - setExposedVariable('selectedCard', item).then(() => fireEvent('onCardSelected')); + setExposedVariable('selectedCard', item); + fireEvent('onCardSelected'); }; const target = React.useRef(null); diff --git a/frontend/src/Editor/Components/Link.jsx b/frontend/src/Editor/Components/Link.jsx index a859022cbb..ff993acc17 100644 --- a/frontend/src/Editor/Components/Link.jsx +++ b/frontend/src/Editor/Components/Link.jsx @@ -1,7 +1,7 @@ -import React, { useRef } from 'react'; +import React, { useRef, useEffect } from 'react'; import cx from 'classnames'; -export const Link = ({ height, properties, styles, fireEvent, registerAction, dataCy }) => { +export const Link = ({ height, properties, styles, fireEvent, setExposedVariable, dataCy }) => { const { linkTarget, linkText, targetType } = properties; const { textColor, textSize, underline, visibility, boxShadow } = styles; const clickRef = useRef(); @@ -12,13 +12,13 @@ export const Link = ({ height, properties, styles, fireEvent, registerAction, da boxShadow, }; - registerAction( - 'click', - async function () { + useEffect(() => { + setExposedVariable('click', async function () { clickRef.current.click(); - }, - [] - ); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return (
{ if (columns < 1) { @@ -73,12 +72,9 @@ export const Listview = function Listview({ }, [columns]); useEffect(() => { - setExposedVariable('data', {}); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - setExposedVariable('data', childrenData); + const childrenDataClone = _.cloneDeep(childrenData); + setExposedVariable('data', removeFunctionObjects(childrenDataClone)); + setExposedVariable('children', childrenData); if (selectedRowIndex != undefined) { setExposedVariable('selectedRowId', selectedRowIndex); setExposedVariable('selectedRow', childrenData[selectedRowIndex]); diff --git a/frontend/src/Editor/Components/Map/Map.jsx b/frontend/src/Editor/Components/Map/Map.jsx index f20c91b7e6..a721bbf040 100644 --- a/frontend/src/Editor/Components/Map/Map.jsx +++ b/frontend/src/Editor/Components/Map/Map.jsx @@ -1,3 +1,4 @@ +/* eslint-disable react-hooks/exhaustive-deps */ import React, { useState, useCallback, useEffect } from 'react'; import { GoogleMap, LoadScript, Marker, Autocomplete } from '@react-google-maps/api'; import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils'; @@ -16,8 +17,7 @@ export const Map = function Map({ onComponentOptionsChanged, onEvent, styles, - // canvasWidth, - registerAction, + setExposedVariable, dataCy, }) { const center = component.definition.properties.initialLocation.value; @@ -127,13 +127,11 @@ export const Map = function Map({ setAutoComplete(autocompleteInstance); } - registerAction( - 'setLocation', - async function (lat, lng) { + useEffect(() => { + setExposedVariable('setLocation', async function (lat, lng) { if (lat && lng) setMapCenter(resolveReferences({ lat, lng }, currentState)); - }, - [setMapCenter] - ); + }); + }, [setMapCenter]); return (
{ + setExposedVariable('open', async function () { setExposedVariable('show', true); setShowModal(true); - }, - [setShowModal] - ); - registerAction( - 'close', - async function () { + }); + setExposedVariable('close', async function () { setShowModal(false); setExposedVariable('show', false); - }, - [setShowModal] - ); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setShowModal]); useEffect(() => { const canShowModal = exposedVariables.show ?? false; @@ -129,7 +123,8 @@ export const Modal = function Modal({ function hideModal() { setShowModal(false); - setExposedVariable('show', false).then(() => fireEvent('onClose')); + setExposedVariable('show', false); + fireEvent('onClose'); } const backwardCompatibilityCheck = height == '34' || modalHeight != undefined ? true : false; diff --git a/frontend/src/Editor/Components/Multiselect.jsx b/frontend/src/Editor/Components/Multiselect.jsx index d00d9c2c31..6eb881324d 100644 --- a/frontend/src/Editor/Components/Multiselect.jsx +++ b/frontend/src/Editor/Components/Multiselect.jsx @@ -20,7 +20,6 @@ export const Multiselect = function Multiselect({ onComponentClick, darkMode, fireEvent, - registerAction, dataCy, }) { const { label, value, values, display_values, showAllOption } = properties; @@ -70,12 +69,12 @@ export const Multiselect = function Multiselect({ setExposedVariable( 'values', items.map((item) => item.value) - ).then(() => fireEvent('onSelect')); + ); + fireEvent('onSelect'); }; - registerAction( - 'selectOption', - async function (value) { + useEffect(() => { + setExposedVariable('selectOption', async function (value) { if ( selectOptions.some((option) => option.value === value) && !selected.some((option) => option.value === value) @@ -91,14 +90,15 @@ export const Multiselect = function Multiselect({ setExposedVariable( 'values', newSelected.map((item) => item.value) - ).then(() => fireEvent('onSelect')); + ); + fireEvent('onSelect'); } - }, - [selected, setSelected] - ); - registerAction( - 'deselectOption', - async function (value) { + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selected, setSelected]); + + useEffect(() => { + setExposedVariable('deselectOption', async function (value) { if (selectOptions.some((option) => option.value === value) && selected.some((option) => option.value === value)) { const newSelected = [ ...selected.filter(function (item) { @@ -109,21 +109,23 @@ export const Multiselect = function Multiselect({ setExposedVariable( 'values', newSelected.map((item) => item.value) - ).then(() => fireEvent('onSelect')); + ); + fireEvent('onSelect'); } - }, - [selected, setSelected] - ); - registerAction( - 'clearSelections', - async function () { + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selected, setSelected]); + + useEffect(() => { + setExposedVariable('clearSelections', async function () { if (selected.length >= 1) { setSelected([]); - setExposedVariable('values', []).then(() => fireEvent('onSelect')); + setExposedVariable('values', []); + fireEvent('onSelect'); } - }, - [selected, setSelected] - ); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selected, setSelected]); return (
{ setPasswordValue(e.target.value); - setExposedVariable('value', e.target.value).then(() => fireEvent('onChange')); + setExposedVariable('value', e.target.value); + fireEvent('onChange'); setShowValidationError(true); }} type={'password'} diff --git a/frontend/src/Editor/Components/RadioButton.jsx b/frontend/src/Editor/Components/RadioButton.jsx index f3999ae5fa..9e2bece680 100644 --- a/frontend/src/Editor/Components/RadioButton.jsx +++ b/frontend/src/Editor/Components/RadioButton.jsx @@ -8,7 +8,6 @@ export const RadioButton = function RadioButton({ styles, fireEvent, setExposedVariable, - registerAction, darkMode, dataCy, }) { @@ -32,21 +31,17 @@ export const RadioButton = function RadioButton({ function onSelect(selection) { setValue(selection); - setExposedVariable('value', selection).then(() => fireEvent('onSelectionChange')); + setExposedVariable('value', selection); + fireEvent('onSelectionChange'); } useEffect(() => { setExposedVariable('value', value); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [value]); - - registerAction( - 'selectOption', - async function (option) { + setExposedVariable('selectOption', async function (option) { onSelect(option); - }, - [setValue] - ); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [value, setValue]); return (
{ const active = steps.filter((item) => item.id == id); - setExposedVariable('currentStepId', active[0].id).then(() => fireEvent('onSelect')); + setExposedVariable('currentStepId', active[0].id); + fireEvent('onSelect'); setActiveStep(active[0].id); }; diff --git a/frontend/src/Editor/Components/Table/AddNewRowComponent.jsx b/frontend/src/Editor/Components/Table/AddNewRowComponent.jsx index 228cb1970e..f841a87a00 100644 --- a/frontend/src/Editor/Components/Table/AddNewRowComponent.jsx +++ b/frontend/src/Editor/Components/Table/AddNewRowComponent.jsx @@ -49,7 +49,7 @@ export function AddNewRowComponent({ accumulator[index] = nestedData; return accumulator; }, {}); - setExposedVariable('newRows', newRowsState).then(() => { + setExposedVariable('newRows', newRowsState)?.then(() => { mergeToAddNewRowsDetails({ newRowsDataUpdates: newRowDataUpdates }); }); } @@ -131,7 +131,7 @@ export function AddNewRowComponent({ accumulator.push(newRowDataUpdates[row]); return accumulator; }, []); - setExposedVariable('newRows', newRowAddedExposedVar).then(() => { + setExposedVariable('newRows', newRowAddedExposedVar)?.then(() => { mergeToAddNewRowsDetails({ newRowsDataUpdates: newRowDataUpdates }); setNewRowsState(rowData); }); @@ -157,7 +157,7 @@ export function AddNewRowComponent({