[fix]: Added components exposed values to the suggestions (#10462)

* commit: Added suggestion from the currentState to add all the hints to the resolved store. This is added because the hints were missing for few of the component values

* fix: rerender component on visibility change

* fix: remove unused logs

* fix: remove duplicate hook call

* Fix: Update reference resolution logic for query variables (#10434)

* Fix: Update reference resolution logic for query variables

* Fix: Update reference resolution logic for query variables

this fix extends changes in commit 8e3f2ec474 to queries as well along with components

* fix: rerender component on visibility change (#10493)

* fix: rerender component on visibility change

* fix: remove unused logs

* fix: remove duplicate hook call

* fix: reset hints when component option changes (#10425)

* fix : multiple close events getting triggered in modal

* fix: removed extra event trigger from component unmount

* chore: added extra log for debugging

* fix: reset hints related to an option whenever a component option changes

* refactor: remove resolvestate functions with duplicate logic

* refactor: remove unused logs

* refactor: remove unused code

* fix: correct wrong function call for resetting keys

---------

Co-authored-by: stepinfwd <stepinfwd@gmail.com>

* fix: removed already declared variables

* fix: resolving the variables. This is fixed by moving the flushComponents function to component. The flush will happen after making sure the re-render reaches the component

* fix: replacing the value with object type is causing an issue. Checked if the resolvedValue is an object type and if so, the value is resolved with the data from currentState without replace

* chore: removed the commented lines

* Revert "fix : Exposed actions become unavailable on reload (#10379)"

This reverts commit 0070c22042.

* fix: flush componenttorerender after rendering parent

---------

Co-authored-by: Johnson Cherian <johnsonc.dev@gmail.com>
Co-authored-by: stepinfwd <stepinfwd@gmail.com>
This commit is contained in:
Kavin Venkatachalam 2024-07-31 12:19:04 +05:30 committed by GitHub
parent df7f774b12
commit 7c62cf032a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 210 additions and 206 deletions

View file

@ -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}
/>
</div>
</OverlayTrigger>

View file

@ -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);

View file

@ -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) {

View file

@ -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,

View file

@ -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);

View file

@ -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');
}
};

View file

@ -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) => ({

View file

@ -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);

View file

@ -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 (
<div

View file

@ -3,7 +3,7 @@ import React, { useRef } from 'react';
import { KanbanBoard } from './KanbanBoard';
export const Kanban = (props) => {
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;

View file

@ -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(

View file

@ -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) {

View file

@ -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(() => {

View file

@ -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);

View file

@ -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

View file

@ -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 (
<div

View file

@ -108,7 +108,6 @@ export function Table({
setProperty,
mode,
exposedVariables,
isEditorReady,
}) {
const {
color,
@ -511,13 +510,11 @@ export function Table({
}, [JSON.stringify([tableData, transformations, currentState])]);
useEffect(() => {
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

View file

@ -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) => (
<div

View file

@ -7,7 +7,6 @@ export const TextArea = function TextArea({
setExposedVariable,
setExposedVariables,
dataCy,
isEditorReady,
}) {
const [value, setValue] = useState(properties.value);
@ -29,10 +28,10 @@ export const TextArea = function TextArea({
setExposedVariable('value', '');
},
};
if (isEditorReady) {
setExposedVariables(exposedVariables);
} // eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties.value, setValue, isEditorReady]);
setExposedVariables(exposedVariables);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setValue]);
return (
<textarea

View file

@ -1,5 +1,6 @@
import React, { useEffect, useRef, useState } from 'react';
import { resolveWidgetFieldValue } from '@/_helpers/utils';
import * as Icons from '@tabler/icons-react';
import Loader from '@/ToolJetUI/Loader/Loader';
const tinycolor = require('tinycolor2');
@ -17,7 +18,6 @@ export const TextInput = function TextInput({
darkMode,
dataCy,
isResizing,
isEditorReady,
}) {
const textInputRef = useRef();
const labelRef = useRef();
@ -172,9 +172,9 @@ export const TextInput = function TextInput({
setVisibility(value);
},
};
if (isEditorReady) setExposedVariables(exposedVariables);
setExposedVariables(exposedVariables);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEditorReady]);
}, []);
useEffect(() => {
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];

View file

@ -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);

View file

@ -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 (
<WidgetWrapper
isResizing={resizingComponentId === id}
@ -895,6 +889,7 @@ export const Container = ({
mode={mode}
propertiesDefinition={box?.component?.definition?.properties}
stylesDefinition={box?.component?.definition?.styles}
otherDefinition={box?.component?.definition?.others}
componentType={box?.component?.component}
>
<DraggableBox
@ -1018,20 +1013,38 @@ const WidgetWrapper = ({
propertiesDefinition,
stylesDefinition,
componentType,
otherDefinition,
}) => {
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'];

View file

@ -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
);
};

View file

@ -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);
});

View file

@ -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) {

View file

@ -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,
});
};

View file

@ -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) {

View file

@ -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';
}