mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-21 13:37:28 +00:00
Hotfix [LTS] - Bugfixes (#10146)
* fixes: child components on listView are not updating correctly * bumped version * Special handling for 'window' keyword in multiline code editors - Updated validation logic to allow 'window.x' while treating standalone 'window' as a reserved keyword. - Refined keyword matching to ensure keywords are not part of a string or a comment and are exact matches. - Applied a hotfix to both CE-LTS and the latest non-LTS CE versions, effective by end of day. - Note: This update is specifically for multiline code editors; single line editors will continue to support 'window.x' or any usage of 'window' as it breaks the app otherwise. * fixes: update the query refs for failed query * fixes: on form submit the children's data should be accessable from queries events * fixes: event execution * adds a debounce for form reset on submit * for onPageLoad events, we need to execute the actions after the page is loaded
This commit is contained in:
parent
5666442f8f
commit
a1b38f4a2f
10 changed files with 82 additions and 37 deletions
2
.version
2
.version
|
|
@ -1 +1 @@
|
|||
2.50.6
|
||||
2.50.7
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2.50.6
|
||||
2.50.7
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { SubCustomDragLayer } from '@/Editor/SubCustomDragLayer';
|
|||
import { SubContainer } from '@/Editor/SubContainer';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { diff } from 'deep-object-diff';
|
||||
import _, { omit } from 'lodash';
|
||||
import _, { debounce, omit } from 'lodash';
|
||||
import { Box } from '@/Editor/Box';
|
||||
import { generateUIComponents } from './FormUtils';
|
||||
import { useMounted } from '@/_hooks/use-mount';
|
||||
|
|
@ -189,7 +189,9 @@ export const Form = function Form(props) {
|
|||
};
|
||||
const fireSubmissionEvent = () => {
|
||||
if (isValid) {
|
||||
onEvent('onSubmit', formEvents).then(() => resetComponent());
|
||||
onEvent('onSubmit', formEvents).then(() => {
|
||||
debounce(() => resetComponent(), 100)();
|
||||
});
|
||||
} else {
|
||||
fireEvent('onInvalid');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,16 +258,18 @@ export const Container = ({
|
|||
return;
|
||||
}
|
||||
|
||||
if (!appDefinition.pages[currentPageId]?.components) return;
|
||||
const definition = useEditorStore.getState().appDefinition;
|
||||
|
||||
if (!definition.pages[currentPageId]?.components) return;
|
||||
|
||||
const newDefinition = {
|
||||
...appDefinition,
|
||||
...definition,
|
||||
pages: {
|
||||
...appDefinition.pages,
|
||||
...definition.pages,
|
||||
[currentPageId]: {
|
||||
...appDefinition.pages[currentPageId],
|
||||
...definition.pages[currentPageId],
|
||||
components: {
|
||||
...appDefinition.pages[currentPageId]?.components,
|
||||
...definition.pages[currentPageId]?.components,
|
||||
...boxes,
|
||||
},
|
||||
},
|
||||
|
|
@ -276,7 +278,7 @@ export const Container = ({
|
|||
|
||||
//need to check if a new component is added or deleted
|
||||
|
||||
const oldComponents = appDefinition.pages[currentPageId]?.components ?? {};
|
||||
const oldComponents = definition.pages[currentPageId]?.components ?? {};
|
||||
const newComponents = boxes;
|
||||
|
||||
const componendAdded = Object.keys(newComponents).length > Object.keys(oldComponents).length;
|
||||
|
|
@ -289,7 +291,8 @@ export const Container = ({
|
|||
opts.componentAdded = true;
|
||||
}
|
||||
|
||||
const shouldUpdate = !_.isEmpty(diff(appDefinition, newDefinition));
|
||||
const shouldUpdate = !_.isEmpty(diff(definition, newDefinition));
|
||||
|
||||
if (shouldUpdate) {
|
||||
appDefinitionChanged(newDefinition, opts);
|
||||
}
|
||||
|
|
@ -1045,15 +1048,16 @@ const WidgetWrapper = ({
|
|||
const isWidgetActive = (isSelected || isDragging) && mode !== 'view';
|
||||
|
||||
const { label = { value: null } } = propertiesDefinition ?? {};
|
||||
const visibility = propertiesDefinition?.visibility?.value ?? stylesDefinition?.visibility?.value ?? null;
|
||||
const resolvedVisibility = resolveWidgetFieldValue(visibility);
|
||||
|
||||
const styles = {
|
||||
width: width + 'px',
|
||||
height: calculateMoveableBoxHeight() + 'px',
|
||||
height: resolvedVisibility ? calculateMoveableBoxHeight() + 'px' : '10px',
|
||||
transform: `translate(${layoutData.left * gridWidth}px, ${layoutData.top}px)`,
|
||||
...(isGhostComponent ? { opacity: 0.5 } : {}),
|
||||
...(isWidgetActive ? { zIndex: 3 } : {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export const shouldUpdate = (prevProps, nextProps) => {
|
|||
return (
|
||||
deepEqualityCheckusingLoDash(prevProps?.id, nextProps?.id) &&
|
||||
deepEqualityCheckusingLoDash(prevProps?.component?.definition, nextProps?.component?.definition) &&
|
||||
deepEqualityCheckusingLoDash(prevProps?.customResolvables, nextProps?.customResolvables) &&
|
||||
prevProps?.width === nextProps?.width &&
|
||||
prevProps?.height === nextProps?.height &&
|
||||
prevProps?.darkMode === nextProps?.darkMode &&
|
||||
|
|
|
|||
|
|
@ -190,22 +190,24 @@ export const SubContainer = ({
|
|||
}, [containerWidth]);
|
||||
|
||||
useEffect(() => {
|
||||
if (appDefinitionChanged) {
|
||||
const definition = useEditorStore.getState().appDefinition;
|
||||
|
||||
if (definition) {
|
||||
const newDefinition = {
|
||||
...appDefinition,
|
||||
...definition,
|
||||
pages: {
|
||||
...appDefinition.pages,
|
||||
...definition.pages,
|
||||
[currentPageId]: {
|
||||
...appDefinition.pages[currentPageId],
|
||||
...definition.pages[currentPageId],
|
||||
components: {
|
||||
...appDefinition.pages[currentPageId].components,
|
||||
...definition.pages[currentPageId].components,
|
||||
...childWidgets,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const oldComponents = appDefinition.pages[currentPageId]?.components ?? {};
|
||||
const oldComponents = definition.pages[currentPageId]?.components ?? {};
|
||||
const newComponents = newDefinition.pages[currentPageId]?.components ?? {};
|
||||
|
||||
const componendAdded = Object.keys(newComponents).length > Object.keys(oldComponents).length;
|
||||
|
|
@ -216,7 +218,7 @@ export const SubContainer = ({
|
|||
opts.componentAdded = true;
|
||||
}
|
||||
|
||||
const shouldUpdate = !_.isEmpty(diff(appDefinition, newDefinition));
|
||||
const shouldUpdate = !_.isEmpty(diff(definition, newDefinition));
|
||||
|
||||
if (shouldUpdate) {
|
||||
appDefinitionChanged(newDefinition, opts);
|
||||
|
|
@ -716,11 +718,19 @@ const SubWidgetWrapper = ({
|
|||
|
||||
const isDragging = useGridStore((state) => state?.draggingComponentId === id);
|
||||
|
||||
const isComponentVisible = () => {
|
||||
const visibility =
|
||||
widget.component.definition?.properties?.visibility?.value ??
|
||||
widget.component.definition?.styles?.visibility?.value ??
|
||||
null;
|
||||
return resolveWidgetFieldValue(visibility);
|
||||
};
|
||||
|
||||
let width = (canvasWidth * layoutData.width) / 43;
|
||||
width = width > canvasWidth ? canvasWidth : width; //this handles scenarios where the width is set more than canvas for older components
|
||||
const styles = {
|
||||
width: width + 'px',
|
||||
height: layoutData.height + 'px',
|
||||
height: isComponentVisible() ? layoutData.height + 'px' : '10px',
|
||||
transform: `translate(${layoutData.left * gridWidth}px, ${layoutData.top}px)`,
|
||||
...(isGhostComponent ? { opacity: 0.5 } : {}),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -105,15 +105,22 @@ export function onComponentOptionsChanged(component, options, id) {
|
|||
componentData = deepClone(componentData) || {};
|
||||
|
||||
const shouldUpdateResolvedRefsOfHints = [];
|
||||
|
||||
const isListviewOrKanbaComponent = component.component === 'Listview' || component.component === 'Kanban';
|
||||
const isFromComponent = component.component === 'Form';
|
||||
for (const option of options) {
|
||||
componentData[option[0]] = option[1];
|
||||
|
||||
const isListviewOrKanbaComponent = component.component === 'Listview' || component.component === 'Kanban';
|
||||
|
||||
let path = null;
|
||||
if (isListviewOrKanbaComponent) {
|
||||
path = `components.${componentName}`;
|
||||
} else if (isFromComponent) {
|
||||
const basePath = `components.${componentName}.${option[0]}`;
|
||||
|
||||
useResolveStore.getState().actions.addAppSuggestions({
|
||||
[basePath]: option[1],
|
||||
});
|
||||
|
||||
shouldUpdateResolvedRefsOfHints.push({ hint: basePath, newRef: componentData[option[1]] });
|
||||
} else {
|
||||
path = `components.${componentName}.${option[0]}`;
|
||||
}
|
||||
|
|
@ -405,7 +412,7 @@ export async function executeActionsForEventId(_ref, eventId, events = [], mode,
|
|||
const filteredEvents = events?.filter((event) => event?.event.eventId === eventId)?.sort((a, b) => a.index - b.index);
|
||||
|
||||
for (const event of filteredEvents) {
|
||||
await executeAction(_ref, event.event, mode, customVariables); // skipcq: JS-0032
|
||||
await executeActionWithDebounce(_ref, event.event, mode, customVariables);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -745,9 +752,8 @@ export async function onEvent(_ref, eventName, events, options = {}, mode = 'edi
|
|||
|
||||
const { customVariables } = options;
|
||||
if (eventName === 'onPageLoad') {
|
||||
return _.debounce(async () => {
|
||||
await executeActionsForEventId(_ref, 'onPageLoad', events, mode, customVariables);
|
||||
}, 10);
|
||||
// for onPageLoad events, we need to execute the actions after the page is loaded
|
||||
handleLowPriorityWork(() => executeActionsForEventId(_ref, 'onPageLoad', events, mode, customVariables));
|
||||
}
|
||||
|
||||
if (eventName === 'onTrigger') {
|
||||
|
|
@ -1221,6 +1227,16 @@ export function runQuery(
|
|||
});
|
||||
resolve(data);
|
||||
onEvent(_self, 'onDataQueryFailure', queryEvents);
|
||||
|
||||
const toUpdateRefs = [
|
||||
{ hint: `queries.${queryName}.isLoading`, newRef: false },
|
||||
{
|
||||
hint: `queries.${queryName}.data`,
|
||||
newRef: [],
|
||||
},
|
||||
];
|
||||
|
||||
useResolveStore.getState().actions.updateResolvedRefsOfHints(toUpdateRefs);
|
||||
if (mode !== 'view') {
|
||||
const err = query.kind == 'tooljetdb' ? data?.error || data : data;
|
||||
toast.error(err?.message ? err?.message : 'Something went wrong');
|
||||
|
|
@ -1652,7 +1668,7 @@ export const cloneComponents = (
|
|||
});
|
||||
};
|
||||
|
||||
const getAllChildComponents = (allComponents, parentId) => {
|
||||
export const getAllChildComponents = (allComponents, parentId) => {
|
||||
const childComponents = [];
|
||||
|
||||
Object.keys(allComponents).forEach((componentId) => {
|
||||
|
|
@ -1660,7 +1676,8 @@ const getAllChildComponents = (allComponents, parentId) => {
|
|||
|
||||
const isParentTabORCalendar =
|
||||
allComponents[parentId]?.component?.component === 'Tabs' ||
|
||||
allComponents[parentId]?.component?.component === 'Calendar';
|
||||
allComponents[parentId]?.component?.component === 'Calendar' ||
|
||||
allComponents[parentId]?.component?.component === 'Kanban';
|
||||
|
||||
if (componentParentId && isParentTabORCalendar) {
|
||||
const childComponent = allComponents[componentId];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useResolveStore } from '@/_stores/resolverStore';
|
|||
import _ from 'lodash';
|
||||
|
||||
export function validateMultilineCode(code) {
|
||||
const reservedKeyword = ['app', 'window', 'this']; // Case-sensitive reserved keywords
|
||||
const reservedKeyword = ['app', 'this']; // Case-sensitive reserved keywords except 'window'
|
||||
const keywordRegex = new RegExp(`\\b(${reservedKeyword.join('|')})\\b`, 'i');
|
||||
let inString = false;
|
||||
let inComment = false;
|
||||
|
|
@ -33,6 +33,18 @@ export function validateMultilineCode(code) {
|
|||
|
||||
// If we are not within a string or a comment, check for keywords
|
||||
if (!inString && !inComment) {
|
||||
// Special handling for 'window'
|
||||
if (code.substring(i, i + 6) === 'window' && (code[i + 6] === undefined || code[i + 6] !== '.')) {
|
||||
return {
|
||||
status: 'failed',
|
||||
data: {
|
||||
message: `Code contains reserved keyword 'window'`,
|
||||
description:
|
||||
'Cannot resolve code with reserved keyword "window" in it unless it is followed by a dot. Please remove it and try again.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const restOfCode = code.substring(i);
|
||||
const match = restOfCode.match(keywordRegex);
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2.50.6
|
||||
2.50.7
|
||||
|
|
|
|||
|
|
@ -108,13 +108,12 @@ export class ComponentsService {
|
|||
return acc;
|
||||
}, {});
|
||||
|
||||
// Update the component with merged data
|
||||
await manager.update(Component, componentId, newComponentsData);
|
||||
return;
|
||||
} else {
|
||||
// Update the component directly if definition is not changed
|
||||
await manager.update(Component, componentId, component);
|
||||
}
|
||||
|
||||
await manager.update(Component, componentId, component);
|
||||
|
||||
return;
|
||||
}
|
||||
}, appVersionId);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue