diff --git a/frontend/src/Editor/BoxUI.jsx b/frontend/src/Editor/BoxUI.jsx index e6dd2dda75..1272302ce5 100644 --- a/frontend/src/Editor/BoxUI.jsx +++ b/frontend/src/Editor/BoxUI.jsx @@ -6,7 +6,7 @@ import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import '@/_styles/custom.scss'; import { EditorContext } from './Context/EditorContextWrapper'; import { validateWidget } from '@/_helpers/utils'; -import { useCurrentState, useCurrentStateStore } from '@/_stores/currentStateStore'; +import { useCurrentState } from '@/_stores/currentStateStore'; import { useAppDataStore } from '@/_stores/appDataStore'; import _ from 'lodash'; @@ -186,7 +186,6 @@ const BoxUI = (props) => { currentPageId={currentPageId} getContainerProps={component.component === 'Form' ? getContainerProps : null} childComponents={childComponents} - isEditorReady={isEditorReady} /> diff --git a/frontend/src/Editor/CodeEditor/utils.js b/frontend/src/Editor/CodeEditor/utils.js index 3ce951f644..26762fcaa1 100644 --- a/frontend/src/Editor/CodeEditor/utils.js +++ b/frontend/src/Editor/CodeEditor/utils.js @@ -188,7 +188,7 @@ function getDynamicVariables(text) { const resolveMultiDynamicReferences = (code, lookupTable, queryHasJSCode) => { let resolvedValue = code; - const isComponentValue = code.includes('components.') || false; + const isComponentValue = code.includes('components.') || code.includes('queries.') || false; const allDynamicVariables = getDynamicVariables(code) || []; let isJSCodeResolver = queryHasJSCode && (allDynamicVariables.length === 1 || allDynamicVariables.length === 0); @@ -301,10 +301,16 @@ export const resolveReferences = (query, validationSchema, customResolvers = {}) resolvedValue = lookupTable.resolvedRefs.get(idToLookUp); if (jsExpression) { - let jscode = value.replace(toResolveReference, resolvedValue); - jscode = value.replace(toResolveReference, `'${resolvedValue}'`); + let jscode = value; + if (!Array.isArray(resolvedValue) && typeof resolvedValue !== 'object' && resolvedValue !== null) { + jscode = value.replace(toResolveReference, resolvedValue).replace(toResolveReference, `'${resolvedValue}'`); + resolvedValue = resolveCode(jscode, customResolvers); + } else { + const [resolvedCode, errorRef] = resolveCode(value, customResolvers, true, [], true); - resolvedValue = resolveCode(jscode, customResolvers); + resolvedValue = resolvedCode; + error = errorRef || null; + } } } else { const [resolvedCode, errorRef] = resolveCode(value, customResolvers, true, [], true); diff --git a/frontend/src/Editor/Components/Button.jsx b/frontend/src/Editor/Components/Button.jsx index 040b329544..ab32b219f3 100644 --- a/frontend/src/Editor/Components/Button.jsx +++ b/frontend/src/Editor/Components/Button.jsx @@ -5,9 +5,7 @@ import * as Icons from '@tabler/icons-react'; import Loader from '@/ToolJetUI/Loader/Loader'; export const Button = function Button(props) { - const { height, properties, styles, fireEvent, id, dataCy, setExposedVariable, setExposedVariables, isEditorReady } = - props; - + const { height, properties, styles, fireEvent, id, dataCy, setExposedVariable, setExposedVariables } = props; const { backgroundColor, textColor, @@ -95,33 +93,31 @@ export const Button = function Button(props) { }; useEffect(() => { - if (isEditorReady) { - const exposedVariables = { - click: async function () { - if (!disable) { - fireEvent('onClick'); - } - }, - setText: async function (text) { - setLabel(text); - setExposedVariable('buttonText', text); - }, - disable: async function (value) { - setDisable(value); - }, - visibility: async function (value) { - setVisibility(value); - }, - loading: async function (value) { - setLoading(value); - }, - }; + const exposedVariables = { + click: async function () { + if (!disable) { + fireEvent('onClick'); + } + }, + setText: async function (text) { + setLabel(text); + setExposedVariable('buttonText', text); + }, + disable: async function (value) { + setDisable(value); + }, + visibility: async function (value) { + setVisibility(value); + }, + loading: async function (value) { + setLoading(value); + }, + }; - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [disable, isEditorReady]); + }, [disable]); useEffect(() => { setExposedVariable('setLoading', async function (loading) { diff --git a/frontend/src/Editor/Components/Chart.jsx b/frontend/src/Editor/Components/Chart.jsx index e77000cd85..3b83b0f3e4 100644 --- a/frontend/src/Editor/Components/Chart.jsx +++ b/frontend/src/Editor/Components/Chart.jsx @@ -18,7 +18,6 @@ export const Chart = function Chart({ setExposedVariable, setExposedVariables, dataCy, - isEditorReady, }) { const [loadingState, setLoadingState] = useState(false); @@ -81,9 +80,8 @@ export const Chart = function Chart({ xAxisTitle: xAxisTitle, yAxisTitle: yAxisTitle, }; - if (isEditorReady) setExposedVariables(exposedVariables); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(chartLayout, chartTitle), isEditorReady]); + setExposedVariables(exposedVariables); + }, [JSON.stringify(chartLayout, chartTitle)]); const layout = { width: width - 4, diff --git a/frontend/src/Editor/Components/Checkbox.jsx b/frontend/src/Editor/Components/Checkbox.jsx index 9e7842d976..bce29bad1f 100644 --- a/frontend/src/Editor/Components/Checkbox.jsx +++ b/frontend/src/Editor/Components/Checkbox.jsx @@ -14,7 +14,6 @@ export const Checkbox = ({ component, validate, width, - isEditorReady, }) => { const defaultValueFromProperties = properties.defaultValue ?? false; const [defaultValue, setDefaultValue] = useState(defaultValueFromProperties); @@ -69,10 +68,10 @@ export const Checkbox = ({ setDefaultValue(defaultValueFromProperties); setChecked(defaultValueFromProperties); setValue(defaultValueFromProperties); - if (isEditorReady) setExposedVariables(exposedVariables); + setExposedVariables(exposedVariables); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [defaultValueFromProperties, isEditorReady]); + }, [defaultValueFromProperties]); useEffect(() => { if (disable !== disabledState) setDisable(properties.disabledState); diff --git a/frontend/src/Editor/Components/ColorPicker.jsx b/frontend/src/Editor/Components/ColorPicker.jsx index eeadc3c339..6e4c3d682a 100644 --- a/frontend/src/Editor/Components/ColorPicker.jsx +++ b/frontend/src/Editor/Components/ColorPicker.jsx @@ -11,7 +11,6 @@ export const ColorPicker = function ({ height, fireEvent, dataCy, - isEditorReady, }) { const { visibility, boxShadow } = styles; const defaultColor = properties.defaultColor; @@ -58,9 +57,8 @@ export const ColorPicker = function ({ selectedColorRGB: hexToRgb(colorCode), selectedColorRGBA: hexToRgba(colorCode), }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); + fireEvent('onChange'); } } else { @@ -69,15 +67,14 @@ export const ColorPicker = function ({ selectedColorRGB: undefined, selectedColorRGBA: undefined, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); + fireEvent('onChange'); setColor('Invalid Color'); } }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setColor, isEditorReady]); + }, [setColor]); useEffect(() => { let exposedVariables = {}; @@ -88,9 +85,8 @@ export const ColorPicker = function ({ selectedColorRGB: hexToRgb(defaultColor), selectedColorRGBA: hexToRgba(defaultColor), }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); + setColor(defaultColor); } } else { @@ -99,13 +95,12 @@ export const ColorPicker = function ({ selectedColorRGB: undefined, selectedColorRGBA: undefined, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); + setColor(`Invalid Color`); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [defaultColor, isEditorReady]); + }, [defaultColor]); const handleColorChange = (colorCode) => { let exposedVariables = {}; @@ -118,9 +113,7 @@ export const ColorPicker = function ({ selectedColorRGB: `rgb(${r},${g},${b})`, selectedColorRGBA: `rgb(${r},${g},${b},${a})`, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); fireEvent('onChange'); } }; diff --git a/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx b/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx index 0699eb34a6..c0a922b0a2 100644 --- a/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx +++ b/frontend/src/Editor/Components/DropdownV2/DropdownV2.jsx @@ -58,7 +58,6 @@ export const DropdownV2 = ({ component, exposedVariables, dataCy, - isEditorReady, }) => { const { label, value, advanced, schema, placeholder, loadingState: dropdownLoadingState, disabledState } = properties; const { @@ -218,11 +217,8 @@ export const DropdownV2 = ({ setIsDropdownDisabled(value); }, }; - - if (isEditorReady) { - setExposedVariables(exposedVariables); - } - }, [isEditorReady]); + setExposedVariables(exposedVariables); + }, []); const customStyles = { container: (base) => ({ diff --git a/frontend/src/Editor/Components/Form/Form.jsx b/frontend/src/Editor/Components/Form/Form.jsx index 51babaa340..7426dd365d 100644 --- a/frontend/src/Editor/Components/Form/Form.jsx +++ b/frontend/src/Editor/Components/Form/Form.jsx @@ -38,7 +38,6 @@ export const Form = function Form(props) { getContainerProps, containerProps, childComponents, - isEditorReady, } = props; const { events: allAppEvents } = useAppInfo(); @@ -133,9 +132,7 @@ export const Form = function Form(props) { ...(!advanced && { children: formattedChildData }), }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); return setValidation(childValidation); } @@ -161,10 +158,9 @@ export const Form = function Form(props) { isValid: childValidation, }; setValidation(childValidation); - if (isEditorReady) { - setExposedVariables(exposedVariables); - } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [childrenData, childComponents, advanced, JSON.stringify(JSONSchema), isEditorReady]); + setExposedVariables(exposedVariables); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [childrenData, childComponents, advanced, JSON.stringify(JSONSchema)]); useEffect(() => { const childIds = Object.keys(childrenData); diff --git a/frontend/src/Editor/Components/Icon.jsx b/frontend/src/Editor/Components/Icon.jsx index 6edcf75477..cec0bbb348 100644 --- a/frontend/src/Editor/Components/Icon.jsx +++ b/frontend/src/Editor/Components/Icon.jsx @@ -9,11 +9,11 @@ export const Icon = ({ fireEvent, width, height, + setExposedVariable, setExposedVariables, darkMode, dataCy, component, - isEditorReady, }) => { const { icon } = properties; const { iconColor, visibility, boxShadow } = styles; @@ -40,11 +40,9 @@ export const Icon = ({ fireEvent('onClick'); }, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setIconVisibility, isEditorReady]); + }, [setIconVisibility]); return (
{ - const { height, width, properties, styles, id, mode, isEditorReady } = props; + const { height, width, properties, styles, id, mode } = props; const { showDeleteButton } = properties; const { visibility, disabledState, boxShadow } = styles; diff --git a/frontend/src/Editor/Components/Kanban/KanbanBoard.jsx b/frontend/src/Editor/Components/Kanban/KanbanBoard.jsx index 9f9beb7265..433eca7ac3 100644 --- a/frontend/src/Editor/Components/Kanban/KanbanBoard.jsx +++ b/frontend/src/Editor/Components/Kanban/KanbanBoard.jsx @@ -38,7 +38,7 @@ const dropAnimation = { const TRASH_ID = 'void'; export function KanbanBoard({ widgetHeight, kanbanProps, parentRef, mode, id }) { - const { properties, fireEvent, setExposedVariable, setExposedVariables, styles, isEditorReady } = kanbanProps; + const { properties, fireEvent, setExposedVariable, setExposedVariables, styles } = kanbanProps; const { columnData, cardData, cardWidth, cardHeight, showDeleteButton, enableAddCard } = properties; const { accentColor } = styles; const [lastSelectedCard, setLastSelectedCard] = useState({}); @@ -112,20 +112,17 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef, mode, id }) cardDataAsObj[cardId] = value; const diffKeys = Object.keys(diff(cardToBeUpdated, value)); if (lastSelectedCard?.id === cardId) { - if (isEditorReady) { - setExposedVariables({ - lastSelectedCard: cardDataAsObj[cardId], - - lastUpdatedCard: cardDataAsObj[cardId], - lastCardUpdate: diffKeys.map((key) => { - return { - [key]: { oldValue: cardToBeUpdated[key], newValue: value[key] }, - }; - }), - updatedCardData: getData(cardDataAsObj), - }); - } + setExposedVariables({ + lastSelectedCard: cardDataAsObj[cardId], + lastUpdatedCard: cardDataAsObj[cardId], + lastCardUpdate: diffKeys.map((key) => { + return { + [key]: { oldValue: cardToBeUpdated[key], newValue: value[key] }, + }; + }), + updatedCardData: getData(cardDataAsObj), + }); fireEvent('onUpdate'); } else { setExposedVariable('updatedCardData', getData(cardDataAsObj)); @@ -133,7 +130,7 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef, mode, id }) } }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [lastSelectedCard, JSON.stringify(cardDataAsObj), isEditorReady]); + }, [lastSelectedCard, JSON.stringify(cardDataAsObj)]); useEffect(() => { setExposedVariable('moveCard', async function (cardId, columnId) { @@ -172,13 +169,11 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef, mode, id }) ...items, [columnId]: [...items[columnId], cardDetails.id], })); - if (isEditorReady) { - setExposedVariables({ lastAddedCard: { ...cardDetails }, updatedCardData: getData(cardDataAsObj) }); - } + setExposedVariables({ lastAddedCard: { ...cardDetails }, updatedCardData: getData(cardDataAsObj) }); fireEvent('onCardAdded'); }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [items, JSON.stringify(cardDataAsObj), isEditorReady]); + }, [items, JSON.stringify(cardDataAsObj)]); useEffect(() => { setExposedVariable('deleteCard', async function (cardId) { @@ -191,13 +186,11 @@ export function KanbanBoard({ widgetHeight, kanbanProps, parentRef, mode, id }) ...items, [columnId]: items[columnId].filter((id) => id !== cardId), })); - if (isEditorReady) { - setExposedVariables({ lastRemovedCard: { ...deletedCard }, updatedCardData: getData(cardDataAsObj) }); - } + setExposedVariables({ lastRemovedCard: { ...deletedCard }, updatedCardData: getData(cardDataAsObj) }); fireEvent('onCardRemoved'); }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [showModal, JSON.stringify(cardDataAsObj), isEditorReady]); + }, [showModal, JSON.stringify(cardDataAsObj)]); const [clonedItems, setClonedItems] = useState(null); const sensors = useSensors( diff --git a/frontend/src/Editor/Components/Listview.jsx b/frontend/src/Editor/Components/Listview.jsx index f7f4673ab3..99efcf9cca 100644 --- a/frontend/src/Editor/Components/Listview.jsx +++ b/frontend/src/Editor/Components/Listview.jsx @@ -19,7 +19,6 @@ export const Listview = function Listview({ darkMode, dataCy, childComponents, - isEditorReady, }) { const fallbackProperties = { height: 100, showBorder: false, data: [] }; const fallbackStyles = { visibility: true, disabledState: false }; @@ -59,9 +58,7 @@ export const Listview = function Listview({ selectedRecordId: index, selectedRecord: childrenData[index], }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); fireEvent('onRecordClicked'); // eslint-disable-next-line react-hooks/exhaustive-deps } @@ -71,9 +68,7 @@ export const Listview = function Listview({ selectedRowId: index, selectedRow: childrenData[index], }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); fireEvent('onRowClicked'); // eslint-disable-next-line react-hooks/exhaustive-deps } @@ -90,20 +85,16 @@ export const Listview = function Listview({ data: removeFunctionObjects(childrenDataClone), children: childrenData, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); if (selectedRowIndex != undefined) { const exposedVariables = { selectedRowId: selectedRowIndex, selectedRow: childrenData[selectedRowIndex], }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [childrenData, childComponents, isEditorReady]); + }, [childrenData, childComponents]); function filterComponents() { if (!childrenData || childrenData.length === 0) { diff --git a/frontend/src/Editor/Components/Modal.jsx b/frontend/src/Editor/Components/Modal.jsx index 66f6565343..e63f25a141 100644 --- a/frontend/src/Editor/Components/Modal.jsx +++ b/frontend/src/Editor/Components/Modal.jsx @@ -20,7 +20,6 @@ export const Modal = function Modal({ dataCy, height, mode, - isEditorReady, }) { const [showModal, setShowModal] = useState(false); @@ -77,10 +76,9 @@ export const Modal = function Modal({ setExposedVariable('show', false); }, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setShowModal, isEditorReady]); + setExposedVariables(exposedVariables); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setShowModal]); const isInitialRender = useRef(true); const prevShowValue = useRef(exposedVariables.show); @@ -108,6 +106,7 @@ export const Modal = function Modal({ function hideModal() { setShowModal(false); setExposedVariable('show', false); + console.log('Trigger close event =>', exposedVariables.show); fireEvent('onClose'); } useEffect(() => { diff --git a/frontend/src/Editor/Components/Multiselect.jsx b/frontend/src/Editor/Components/Multiselect.jsx index 8b2db437ca..2c57edf8aa 100644 --- a/frontend/src/Editor/Components/Multiselect.jsx +++ b/frontend/src/Editor/Components/Multiselect.jsx @@ -22,7 +22,6 @@ export const Multiselect = function Multiselect({ darkMode, fireEvent, dataCy, - isEditorReady, }) { const { label, value, values, display_values, showAllOption } = properties; const { borderRadius, visibility, disabledState, boxShadow } = styles; @@ -125,11 +124,10 @@ export const Multiselect = function Multiselect({ }, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); + // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selected, setSelected, isEditorReady]); + }, [selected, setSelected]); const filterOptions = (options, filter) => { setSearched(filter); diff --git a/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx b/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx index 1049532c4b..abd3de16ed 100644 --- a/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx +++ b/frontend/src/Editor/Components/MultiselectV2/MultiselectV2.jsx @@ -27,7 +27,7 @@ export const MultiselectV2 = ({ darkMode, fireEvent, validate, - isEditorReady, + width, }) => { let { label, @@ -179,10 +179,8 @@ export const MultiselectV2 = ({ setIsMultiSelectDisabled(value); }, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } - }, [isEditorReady]); + setExposedVariables(exposedVariables); + }, []); useEffect(() => { // Expose selectOption diff --git a/frontend/src/Editor/Components/RadioButton.jsx b/frontend/src/Editor/Components/RadioButton.jsx index a5f74cde19..51d19d8cbb 100644 --- a/frontend/src/Editor/Components/RadioButton.jsx +++ b/frontend/src/Editor/Components/RadioButton.jsx @@ -11,7 +11,6 @@ export const RadioButton = function RadioButton({ setExposedVariables, darkMode, dataCy, - isEditorReady, }) { const { label, value, values, display_values } = properties; const { visibility, disabledState, activeColor, boxShadow } = styles; @@ -44,10 +43,9 @@ export const RadioButton = function RadioButton({ onSelect(option); }, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [value, setValue, isEditorReady]); + setExposedVariables(exposedVariables); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [value, setValue]); return (
{ - if (isEditorReady) { - setExposedVariables({ - currentData: tableData, - updatedData: tableData, - }); - } - }, [JSON.stringify(tableData), isEditorReady]); + setExposedVariables({ + currentData: tableData, + updatedData: tableData, + }); + }, [JSON.stringify(tableData)]); const columnDataForAddNewRows = generateColumnsData({ columnProperties: useDynamicColumn ? generatedColumn : component.definition.properties.columns.value, @@ -778,7 +775,7 @@ export function Table({ fireEvent('onRowClicked'); } }); - }, [JSON.stringify(tableData), JSON.stringify(tableDetails.selectedRow), isEditorReady]); + }, [JSON.stringify(tableData), JSON.stringify(tableDetails.selectedRow)]); useEffect(() => { setExposedVariable('deselectRow', async function () { @@ -790,7 +787,7 @@ export function Table({ } return; }); - }, [JSON.stringify(tableData), JSON.stringify(tableDetails.selectedRow), isEditorReady]); + }, [JSON.stringify(tableData), JSON.stringify(tableDetails.selectedRow)]); useEffect(() => { setExposedVariable('discardChanges', async function () { @@ -802,7 +799,7 @@ export function Table({ mergeToTableDetails({ dataUpdates: {}, changeSet: {} }); } }); - }, [JSON.stringify(tableData), JSON.stringify(tableDetails.changeSet), isEditorReady]); + }, [JSON.stringify(tableData), JSON.stringify(tableDetails.changeSet)]); useEffect(() => { setExposedVariable('discardNewlyAddedRows', async function () { @@ -821,7 +818,6 @@ export function Table({ JSON.stringify(tableDetails.addNewRowsDetails.newRowsChangeSet), tableDetails.addNewRowsDetails.addingNewRows, JSON.stringify(tableDetails.addNewRowsDetails.newRowsDataUpdates), - isEditorReady, ]); useEffect(() => { @@ -845,7 +841,7 @@ export function Table({ setExposedVariables({ selectedRow, selectedRowId }); mergeToTableDetails({ selectedRow, selectedRowId }); } - }, [selectedFlatRows.length, selectedFlatRows, isEditorReady]); + }, [selectedFlatRows.length, selectedFlatRows]); useEffect(() => { setExposedVariable('downloadTableData', async function (format) { @@ -859,7 +855,7 @@ export function Table({ mergeToTableDetails({ selectedRowsDetails: [], selectedRow: {}, selectedRowId: null }); toggleAllRowsSelected(false); } - }, [showBulkSelector, highlightSelectedRow, allowSelection, isEditorReady]); + }, [showBulkSelector, highlightSelectedRow, allowSelection]); React.useEffect(() => { if (enablePagination) { @@ -975,7 +971,7 @@ export function Table({ } //hack : in the initial render, data is undefined since, upon feeding data to the table from some query, query inside current state is {}. Hence we added data in the dependency array, now question is should we add data or rows? - }, [JSON.stringify(defaultSelectedRow), JSON.stringify(data), isEditorReady]); + }, [JSON.stringify(defaultSelectedRow), JSON.stringify(data)]); useEffect(() => { // csa for select all rows in table diff --git a/frontend/src/Editor/Components/Tabs.jsx b/frontend/src/Editor/Components/Tabs.jsx index 716cb9b66e..6d67108d89 100644 --- a/frontend/src/Editor/Components/Tabs.jsx +++ b/frontend/src/Editor/Components/Tabs.jsx @@ -17,7 +17,6 @@ export const Tabs = function Tabs({ styles, darkMode, dataCy, - isEditorReady, }) { const { tabWidth, boxShadow } = styles; @@ -104,11 +103,10 @@ export const Tabs = function Tabs({ }, currentTab: currentTab, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); + // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setCurrentTab, currentTab, isEditorReady]); + }, [setCurrentTab, currentTab]); const renderTabContent = (id, tab) => (
{ const exposedVariables = { @@ -189,9 +189,9 @@ export const TextInput = function TextInput({ fireEvent('onChange'); }, }; - if (isEditorReady) setExposedVariables(exposedVariables); + setExposedVariables(exposedVariables); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [setValue, isEditorReady]); + }, [setValue]); const iconName = styles.icon; // Replace with the name of the icon you want // eslint-disable-next-line import/namespace const IconElement = Icons[iconName] == undefined ? Icons['IconHome2'] : Icons[iconName]; diff --git a/frontend/src/Editor/Components/TreeSelect.jsx b/frontend/src/Editor/Components/TreeSelect.jsx index 08f7456bb1..c66202637b 100644 --- a/frontend/src/Editor/Components/TreeSelect.jsx +++ b/frontend/src/Editor/Components/TreeSelect.jsx @@ -14,7 +14,6 @@ export const TreeSelect = ({ fireEvent, darkMode, dataCy, - isEditorReady, }) => { const { label } = properties; const { visibility, disabledState, checkboxColor, boxShadow } = styles; @@ -52,11 +51,10 @@ export const TreeSelect = ({ checkedPathStrings: checkedPathString, checked: checkedArr, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); + // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(checkedData), JSON.stringify(data), isEditorReady]); + }, [JSON.stringify(checkedData), JSON.stringify(data)]); useEffect(() => { setExposedVariable('expanded', expandedData); @@ -92,9 +90,8 @@ export const TreeSelect = ({ checkedPathStrings: checkedPathString, checked: checked, }; - if (isEditorReady) { - setExposedVariables(exposedVariables); - } + setExposedVariables(exposedVariables); + updatedNode.checked ? fireEvent('onCheck') : fireEvent('onUnCheck'); fireEvent('onChange'); setChecked(checked); diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx index 6cc2efa620..7a0a9804eb 100644 --- a/frontend/src/Editor/Container.jsx +++ b/frontend/src/Editor/Container.jsx @@ -19,7 +19,7 @@ import { calculateMoveableBoxHeight, } from '@/_helpers/appUtils'; import { useAppVersionStore } from '@/_stores/appVersionStore'; -import { useEditorStore } from '@/_stores/editorStore'; +import { useEditorStore, flushComponentsToRender } from '@/_stores/editorStore'; import { useAppInfo } from '@/_stores/appDataStore'; import { shallow } from 'zustand/shallow'; import _, { isEmpty } from 'lodash'; @@ -878,12 +878,6 @@ export const Container = ({ }) .filter(([, box]) => isEmpty(box?.component?.parent)) .map(([id, box]) => { - const canShowInCurrentLayout = - box.component.definition.others[currentLayout === 'mobile' ? 'showOnMobile' : 'showOnDesktop'].value; - - if (box.parent || !resolveWidgetFieldValue(canShowInCurrentLayout)) { - return ''; - } return ( { const isGhostComponent = id === 'resizingComponentId'; const { component: { parent }, layouts, } = widget; - const { isSelected, isHovered } = useEditorStore((state) => { + const { isSelected, isHovered, shouldRerender } = useEditorStore((state) => { const isSelected = !!(state.selectedComponents || []).find((selected) => selected?.id === id); const isHovered = state?.hoveredComponent == id; - return { isSelected, isHovered }; + /* + `shouldRerender` is added only for re-rendering the component when visibility/showOnMobile/showOnDesktop + updates since these attributes need update or WidgetWrapper rather than actual Widget itself + */ + const shouldRerender = state.componentsNeedsUpdateOnNextRender.some((compId) => compId === id); + return { isSelected, isHovered, shouldRerender }; }, shallow); const isDragging = useGridStore((state) => state?.draggingComponentId === id); + const canShowInCurrentLayout = otherDefinition[currentLayout === 'mobile' ? 'showOnMobile' : 'showOnDesktop'].value; + + if (parent || !resolveWidgetFieldValue(canShowInCurrentLayout)) { + /* + Remove the component from the re-render queue + This is necessary because child components are not rendered, + so their flush functions won't be called from ControlledComponentToRender + */ + shouldRerender && flushComponentsToRender(id); + return ''; + } + let layoutData = layouts?.[currentLayout]; if (isEmpty(layoutData)) { layoutData = layouts?.['desktop']; diff --git a/frontend/src/Editor/ControlledComponentToRender.jsx b/frontend/src/Editor/ControlledComponentToRender.jsx index 0471bef462..226bdcd5a9 100644 --- a/frontend/src/Editor/ControlledComponentToRender.jsx +++ b/frontend/src/Editor/ControlledComponentToRender.jsx @@ -1,7 +1,7 @@ import React, { useState, useCallback } from 'react'; import { getComponentToRender } from '@/_helpers/editorHelpers'; import _ from 'lodash'; -import { getComponentsToRenders } from '@/_stores/editorStore'; +import { getComponentsToRenders, flushComponentsToRender } from '@/_stores/editorStore'; function deepEqualityCheckusingLoDash(obj1, obj2) { return _.isEqual(obj1, obj2); @@ -16,6 +16,7 @@ export const shouldUpdate = (prevProps, nextProps) => { if (componentId) { const componentToRender = listToRender.find((item) => item === componentId); + const parentReRendered = listToRender.find((item) => item === prevProps?.parentId); const grandParentReRendered = listToRender.find((item) => item === prevProps?.grandParentId); @@ -25,6 +26,9 @@ export const shouldUpdate = (prevProps, nextProps) => { } } + // Flushing the component after the function is called from ControlledComponentToRender component + if (nextProps?.componentName) flushComponentsToRender([prevProps?.id]); + // Added to render the default child components if (prevProps?.childComponents === null && nextProps?.childComponents) return false; @@ -38,7 +42,6 @@ export const shouldUpdate = (prevProps, nextProps) => { prevProps?.height === nextProps?.height && prevProps?.darkMode === nextProps?.darkMode && prevProps?.childComponents === nextProps?.childComponents && - prevProps?.isEditorReady === nextProps?.isEditorReady && !needToRender ); }; diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index fc20c1caa1..75799e827d 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -28,6 +28,7 @@ import { buildComponentMetaDefinition, getAllChildComponents, runQueries, + updateSuggestionsFromCurrentState, } from '@/_helpers/appUtils'; import { Confirm } from './Viewer/Confirm'; // eslint-disable-next-line import/no-unresolved @@ -308,14 +309,8 @@ const EditorComponent = (props) => { const isPageSwitched = useResolveStore.getState().isPageSwitched; if (isPageSwitched) { - const currentStateObj = useCurrentStateStore.getState(); - handleLowPriorityWork(() => { - useResolveStore.getState().actions.addAppSuggestions({ - queries: currentStateObj.queries, - components: currentStateObj.components, - page: currentStateObj.page, - }); + updateSuggestionsFromCurrentState(); useResolveStore.getState().actions.pageSwitched(false); }); } @@ -733,6 +728,7 @@ const EditorComponent = (props) => { await processNewAppDefinition(appData, startingPageHandle, false, ({ homePageId }) => { handleLowPriorityWork(() => { + updateSuggestionsFromCurrentState(); useResolveStore.getState().actions.updateLastUpdatedRefs(['constants', 'client']); commonLowPriorityActions(events, { homePageId }); }); @@ -825,6 +821,7 @@ const EditorComponent = (props) => { }); processNewAppDefinition(appData, null, true, ({ homePageId }) => { handleLowPriorityWork(async () => { + updateSuggestionsFromCurrentState(); await fetchDataSources(editing_version?.id); commonLowPriorityActions(events, homePageId); }); diff --git a/frontend/src/Editor/Viewer.jsx b/frontend/src/Editor/Viewer.jsx index 841fc95cd4..1989261728 100644 --- a/frontend/src/Editor/Viewer.jsx +++ b/frontend/src/Editor/Viewer.jsx @@ -21,6 +21,7 @@ import { runQuery, computeComponentState, buildAppDefinition, + updateSuggestionsFromCurrentState, } from '@/_helpers/appUtils'; import queryString from 'query-string'; import ViewerLogoIcon from './Icons/viewer-logo.svg'; @@ -45,7 +46,7 @@ import MobileHeader from './Viewer/MobileHeader'; import DesktopHeader from './Viewer/DesktopHeader'; import './Viewer/viewer.scss'; import { useResolveStore } from '@/_stores/resolverStore'; -import { findComponentsWithReferences } from '@/_helpers/editorHelpers'; +import { findComponentsWithReferences, handleLowPriorityWork } from '@/_helpers/editorHelpers'; import { findAllEntityReferences } from '@/_stores/utils'; import { dfs } from '@/_stores/handleReferenceTransactions'; import useAppDarkMode from '@/_hooks/useAppDarkMode'; @@ -265,6 +266,7 @@ class ViewerComponent extends React.Component { useCurrentStateStore.getState().actions.setEditorReady(true); if (loadType === 'appload') { + updateSuggestionsFromCurrentState(); this.runQueries(dataQueries); } @@ -807,6 +809,8 @@ class ViewerComponent extends React.Component { isSwitchingPage: true, }, }); + + useResolveStore.getState().actions.pageSwitched(true); this.onViewerLoadUpdateEntityReferences(id, 'page-switch'); }; @@ -1093,6 +1097,10 @@ const withStore = (Component) => (props) => { if (isPageSwitched) { const currentComponentsDef = appDefinition?.pages?.[currentPageId]?.components || {}; const currentComponents = Object.keys(currentComponentsDef); + handleLowPriorityWork(() => { + updateSuggestionsFromCurrentState(); + useResolveStore.getState().actions.pageSwitched(false); + }); setTimeout(() => { if (currentComponents.length > 0) { diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js index a111e434b0..335785cf2a 100644 --- a/frontend/src/_helpers/appUtils.js +++ b/frontend/src/_helpers/appUtils.js @@ -89,6 +89,8 @@ const debouncedChange = _.debounce((duplicateCurrentState) => { }, 100); export function onComponentOptionsChanged(component, options, id) { + const resolveStoreActions = useResolveStore.getState().actions; + options.forEach((option) => resolveStoreActions.resetHintsByKey([`components.${component?.name}.${option[0]}`])); let componentName = component.name; const { isEditorReady, page } = useCurrentStateStore.getState(); @@ -183,6 +185,8 @@ export function onComponentOptionsChanged(component, options, id) { } export function onComponentOptionChanged(component, option_name, value, id) { + const resolveStoreActions = useResolveStore.getState().actions; + resolveStoreActions.resetHintsByKey(`components.${component?.name}.${option_name}`); if (!useEditorStore.getState()?.appDefinition?.pages[getCurrentState()?.page?.id]?.components) return; let componentName = component.name; @@ -633,12 +637,15 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { const value = resolveReferences(event.value, state, undefined, customVariables); const customAppVariables = { ...state.variables }; customAppVariables[key] = value; + const resp = useCurrentStateStore.getState().actions.setCurrentState({ + variables: customAppVariables, + }); + useResolveStore.getState().actions.addAppSuggestions({ variables: customAppVariables, }); - return useCurrentStateStore.getState().actions.setCurrentState({ - variables: customAppVariables, - }); + + return resp; } case 'get-custom-variable': { @@ -1144,7 +1151,7 @@ export function runQuery( //for resetting the hints when the query is run for large number of items if (mode == 'edit') { const resolveStoreActions = useResolveStore.getState().actions; - resolveStoreActions.resetHintsByQueryName(queryName); + resolveStoreActions.resetHintsByKey(`queries.${queryName}`); } let parameters = userSuppliedParameters; @@ -2342,3 +2349,12 @@ export const calculateMoveableBoxHeight = (componentType, layoutData, stylesDefi return newHeight; }; + +export const updateSuggestionsFromCurrentState = () => { + const currentStateObj = useCurrentStateStore.getState(); + useResolveStore.getState().actions.addAppSuggestions({ + queries: currentStateObj.queries, + components: currentStateObj.components, + page: currentStateObj.page, + }); +}; diff --git a/frontend/src/_helpers/editorHelpers.js b/frontend/src/_helpers/editorHelpers.js index 2869a072ac..ddc7604bd7 100644 --- a/frontend/src/_helpers/editorHelpers.js +++ b/frontend/src/_helpers/editorHelpers.js @@ -166,6 +166,19 @@ function convertToBracketNotation(base, accessors) { } function verifyDotAndBracketNotations(jsString) { + if ( + !( + jsString.includes('components.') || + jsString.includes('globals.') || + jsString.includes('queries.') || + jsString.includes('page.') || + jsString.includes('variables.') || + jsString.includes('constants.') + ) + ) { + return false; + } + const notations = findNotations(jsString); for (const { base, accessors } of notations) { diff --git a/frontend/src/_stores/resolverStore.js b/frontend/src/_stores/resolverStore.js index 57b4533a81..ce4b03d1d1 100644 --- a/frontend/src/_stores/resolverStore.js +++ b/frontend/src/_stores/resolverStore.js @@ -79,19 +79,21 @@ export const useResolveStore = create( resetStore: () => { set(() => ({ ...initialState, referenceMapper: new ReferencesBiMap() })); }, - resetHintsByQueryName: (queryName) => { + resetHintsByKey: (hintKey) => { set((state) => { // Filter out app hints related to the specified query - const newAppHints = state.suggestions.appHints.filter( - (hint) => !hint.hint.startsWith(`queries.${queryName}.`) - ); + const newAppHints = state.suggestions.appHints.filter((hint) => !hint.hint.startsWith(`${hintKey}.`)); + + if (!isIterable(state.lookupTable.hints) || !isIterable(state.lookupTable.resolvedRefs)) { + return { ...state }; + } const newHints = new Map(state.lookupTable.hints); const newResolvedRefs = new Map(state.lookupTable.resolvedRefs); // Remove entries from hints and resolvedRefs for (const [key, value] of newHints) { - if (key.startsWith(`queries.${queryName}.`)) { + if (key.startsWith(`${hintKey}.`)) { newHints.delete(key); newResolvedRefs.delete(value); } @@ -106,7 +108,7 @@ export const useResolveStore = create( hints: newHints, resolvedRefs: newResolvedRefs, }, - lastUpdatedRefs: state.lastUpdatedRefs.filter((ref) => !ref.startsWith(`queries.${queryName}.`)), + lastUpdatedRefs: state.lastUpdatedRefs.filter((ref) => !ref.startsWith(`${hintKey}.`)), }; }); }, @@ -425,10 +427,14 @@ async function batchUpdateComponents(componentIds) { useEditorStore.getState().actions.updateComponentsNeedsUpdateOnNextRender(batch); } - - // Flush only updated components - - flushComponentsToRender(updatedComponentIds); } export const useResolverStoreActions = () => useResolveStore.getState().actions; + +function isIterable(obj) { + // checks for null and undefined + if (obj == null) { + return false; + } + return typeof obj[Symbol.iterator] === 'function'; +}