chore: Fix layout issues for UI package editor (#40349)
## Description 🏗 Architecture - Moved mainCanvasReducer from Community Edition (CE) to Enterprise Edition (EE) structure - Added proper export structure in EE to maintain backward compatibility - Added RESET_EDITOR_REQUEST handler to reset canvas state when needed - 🐛 Bug Fixes - Fixed widget tag grouping in groupWidgetCardsByTags to properly handle tags that aren't predefined - Implemented support for overrideRenderMode in both LayoutSystemBasedCanvas and withWidgetProps - Added emptyMessage prop in UIEntitySidebar for improved user experience PR for https://github.com/appsmithorg/appsmith-ee/pull/7245 ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/14610885832> > Commit: 54916f79b3009451298496513e0a3a8ee7bccdac > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=14610885832&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Wed, 23 Apr 2025 07:02:40 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for customizing the empty message in the widget sidebar when no widgets are found. - Introduced an option to override the widget render mode in both the canvas and widget components, enabling more flexible widget behavior in the editor. - **Bug Fixes** - Improved grouping of widget cards by tags to prevent potential errors when encountering new tags. - **Refactor** - Updated internal reducer structure and export patterns for improved maintainability. - Adjusted import paths for better modularization and separation between core and enterprise code. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
4fbac4d68f
commit
76b1ff1406
|
|
@ -48,7 +48,7 @@ import type { CrudInfoModalReduxState } from "reducers/uiReducers/crudInfoModalR
|
||||||
import type { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationReducer";
|
import type { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationReducer";
|
||||||
import type { widgetReflow } from "reducers/uiReducers/reflowReducer";
|
import type { widgetReflow } from "reducers/uiReducers/reflowReducer";
|
||||||
import type { AppThemingState } from "reducers/uiReducers/appThemingReducer";
|
import type { AppThemingState } from "reducers/uiReducers/appThemingReducer";
|
||||||
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
|
import type { MainCanvasReduxState } from "ee/reducers/uiReducers/mainCanvasReducer";
|
||||||
import type { SettingsReduxState } from "ee/reducers/settingsReducer";
|
import type { SettingsReduxState } from "ee/reducers/settingsReducer";
|
||||||
import SettingsReducer from "ee/reducers/settingsReducer";
|
import SettingsReducer from "ee/reducers/settingsReducer";
|
||||||
import type { TriggerValuesEvaluationState } from "reducers/evaluationReducers/triggerReducer";
|
import type { TriggerValuesEvaluationState } from "reducers/evaluationReducers/triggerReducer";
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import crudInfoModalReducer from "reducers/uiReducers/crudInfoModalReducer";
|
||||||
import { widgetReflowReducer } from "reducers/uiReducers/reflowReducer";
|
import { widgetReflowReducer } from "reducers/uiReducers/reflowReducer";
|
||||||
import jsObjectNameReducer from "reducers/uiReducers/jsObjectNameReducer";
|
import jsObjectNameReducer from "reducers/uiReducers/jsObjectNameReducer";
|
||||||
import appThemingReducer from "reducers/uiReducers/appThemingReducer";
|
import appThemingReducer from "reducers/uiReducers/appThemingReducer";
|
||||||
import mainCanvasReducer from "reducers/uiReducers/mainCanvasReducer";
|
import mainCanvasReducer from "ee/reducers/uiReducers/mainCanvasReducer";
|
||||||
import focusHistoryReducer from "reducers/uiReducers/focusHistoryReducer";
|
import focusHistoryReducer from "reducers/uiReducers/focusHistoryReducer";
|
||||||
import { editorContextReducer } from "ee/reducers/uiReducers/editorContextReducer";
|
import { editorContextReducer } from "ee/reducers/uiReducers/editorContextReducer";
|
||||||
import libraryReducer from "reducers/uiReducers/libraryReducer";
|
import libraryReducer from "reducers/uiReducers/libraryReducer";
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,16 @@ import {
|
||||||
} from "constants/WidgetConstants";
|
} from "constants/WidgetConstants";
|
||||||
import type { UpdateCanvasLayoutPayload } from "actions/controlActions";
|
import type { UpdateCanvasLayoutPayload } from "actions/controlActions";
|
||||||
import type { UpdateCanvasPayload } from "actions/pageActions";
|
import type { UpdateCanvasPayload } from "actions/pageActions";
|
||||||
|
import { klona } from "klona";
|
||||||
|
|
||||||
const initialState: MainCanvasReduxState = {
|
export const initialState: MainCanvasReduxState = {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mainCanvasReducer = createImmerReducer(initialState, {
|
export const handlers = {
|
||||||
[ReduxActionTypes.INIT_CANVAS_LAYOUT]: (
|
[ReduxActionTypes.INIT_CANVAS_LAYOUT]: (
|
||||||
state: MainCanvasReduxState,
|
state: MainCanvasReduxState,
|
||||||
action: ReduxAction<UpdateCanvasPayload>,
|
action: ReduxAction<UpdateCanvasPayload>,
|
||||||
|
|
@ -38,7 +39,12 @@ const mainCanvasReducer = createImmerReducer(initialState, {
|
||||||
state.isMobile =
|
state.isMobile =
|
||||||
action.payload.width <= layoutConfigurations.MOBILE.maxWidth;
|
action.payload.width <= layoutConfigurations.MOBILE.maxWidth;
|
||||||
},
|
},
|
||||||
});
|
[ReduxActionTypes.RESET_EDITOR_REQUEST]: () => {
|
||||||
|
return klona(initialState);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mainCanvasReducer = createImmerReducer(initialState, handlers);
|
||||||
|
|
||||||
export interface MainCanvasReduxState {
|
export interface MainCanvasReduxState {
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
|
|
@ -126,7 +126,7 @@ import { getPageList } from "ee/selectors/entitiesSelector";
|
||||||
import { setPreviewModeAction } from "actions/editorActions";
|
import { setPreviewModeAction } from "actions/editorActions";
|
||||||
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
|
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
|
||||||
import { toast } from "@appsmith/ads";
|
import { toast } from "@appsmith/ads";
|
||||||
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
|
import type { MainCanvasReduxState } from "ee/reducers/uiReducers/mainCanvasReducer";
|
||||||
import { UserCancelledActionExecutionError } from "sagas/ActionExecution/errorUtils";
|
import { UserCancelledActionExecutionError } from "sagas/ActionExecution/errorUtils";
|
||||||
import { getInstanceId } from "ee/selectors/organizationSelectors";
|
import { getInstanceId } from "ee/selectors/organizationSelectors";
|
||||||
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
|
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from "ce/reducers/uiReducers/mainCanvasReducer";
|
||||||
|
import { default as CE_mainCanvasReducer } from "ce/reducers/uiReducers/mainCanvasReducer";
|
||||||
|
export default CE_mainCanvasReducer;
|
||||||
|
|
@ -16,9 +16,19 @@ import { getAppThemeSettings } from "ee/selectors/applicationSelectors";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const LayoutSystemBasedCanvas = memo((props: WidgetProps) => {
|
const LayoutSystemBasedCanvas = memo((props: WidgetProps) => {
|
||||||
const renderMode = useSelector(getRenderMode);
|
let renderMode = useSelector(getRenderMode);
|
||||||
const themeSetting = useSelector(getAppThemeSettings);
|
const themeSetting = useSelector(getAppThemeSettings);
|
||||||
|
|
||||||
|
// This is primarily used by UI modules in app editor where it wants to load all the underlying
|
||||||
|
// widgets in page mode as they are not editable and mimics the behavior of view mode.
|
||||||
|
// Since in app's edit mode the default render mode is canvas and due to this some widgets do not behave
|
||||||
|
// properly.
|
||||||
|
// Ideally the renderMode from props should be used instead of the one from the selector but that needs
|
||||||
|
// to be handled properly as it needs a bigger change and more testing.
|
||||||
|
if (props.overrideRenderMode) {
|
||||||
|
renderMode = props.overrideRenderMode;
|
||||||
|
}
|
||||||
|
|
||||||
const layoutSystemType = useSelector(getLayoutSystemType);
|
const layoutSystemType = useSelector(getLayoutSystemType);
|
||||||
const { canvasSystem } = useMemo(
|
const { canvasSystem } = useMemo(
|
||||||
() => getLayoutSystem(renderMode, layoutSystemType),
|
() => getLayoutSystem(renderMode, layoutSystemType),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import UIEntitySidebar from "pages/Editor/widgetSidebar/UIEntitySidebar";
|
||||||
import {
|
import {
|
||||||
createMessage,
|
createMessage,
|
||||||
UI_ELEMENT_PANEL_SEARCH_TEXT,
|
UI_ELEMENT_PANEL_SEARCH_TEXT,
|
||||||
|
WIDGET_PANEL_EMPTY_MESSAGE,
|
||||||
} from "ee/constants/messages";
|
} from "ee/constants/messages";
|
||||||
|
|
||||||
interface WidgetsListProps {
|
interface WidgetsListProps {
|
||||||
|
|
@ -17,6 +18,7 @@ function WidgetsList({ focusSearchInput }: WidgetsListProps) {
|
||||||
return (
|
return (
|
||||||
<UIEntitySidebar
|
<UIEntitySidebar
|
||||||
cards={cards}
|
cards={cards}
|
||||||
|
emptyMessage={createMessage(WIDGET_PANEL_EMPTY_MESSAGE)}
|
||||||
entityLoading={entityLoading}
|
entityLoading={entityLoading}
|
||||||
focusSearchInput={focusSearchInput}
|
focusSearchInput={focusSearchInput}
|
||||||
groupedCards={groupedCards}
|
groupedCards={groupedCards}
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,8 @@ export const groupWidgetCardsByTags = (widgetCards: WidgetCardProps[]) => {
|
||||||
item.tags.forEach((tag) => {
|
item.tags.forEach((tag) => {
|
||||||
if (groupedCards[tag]) {
|
if (groupedCards[tag]) {
|
||||||
groupedCards[tag].push(item);
|
groupedCards[tag].push(item);
|
||||||
|
} else {
|
||||||
|
groupedCards[tag] = [item];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
import {
|
|
||||||
WIDGET_PANEL_EMPTY_MESSAGE,
|
|
||||||
createMessage,
|
|
||||||
} from "ee/constants/messages";
|
|
||||||
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
|
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
|
||||||
import { ENTITY_EXPLORER_SEARCH_ID } from "constants/Explorer";
|
import { ENTITY_EXPLORER_SEARCH_ID } from "constants/Explorer";
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -23,10 +19,12 @@ interface UIEntitySidebarProps {
|
||||||
entityLoading?: Partial<Record<WidgetTags, boolean>>;
|
entityLoading?: Partial<Record<WidgetTags, boolean>>;
|
||||||
groupedCards: WidgetCardsGroupedByTags;
|
groupedCards: WidgetCardsGroupedByTags;
|
||||||
searchPlaceholderText?: string;
|
searchPlaceholderText?: string;
|
||||||
|
emptyMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function UIEntitySidebar({
|
function UIEntitySidebar({
|
||||||
cards,
|
cards,
|
||||||
|
emptyMessage,
|
||||||
entityLoading,
|
entityLoading,
|
||||||
focusSearchInput,
|
focusSearchInput,
|
||||||
groupedCards,
|
groupedCards,
|
||||||
|
|
@ -133,8 +131,7 @@ function UIEntitySidebar({
|
||||||
renderAs="p"
|
renderAs="p"
|
||||||
style={{ marginBottom: "15px" }}
|
style={{ marginBottom: "15px" }}
|
||||||
>
|
>
|
||||||
{createMessage(WIDGET_PANEL_EMPTY_MESSAGE)} `
|
{emptyMessage} `{searchInputRef.current?.value}`
|
||||||
{searchInputRef.current?.value}`
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ import {
|
||||||
getIsAutoLayoutMobileBreakPoint,
|
getIsAutoLayoutMobileBreakPoint,
|
||||||
getMainCanvasProps,
|
getMainCanvasProps,
|
||||||
} from "selectors/editorSelectors";
|
} from "selectors/editorSelectors";
|
||||||
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
|
import type { MainCanvasReduxState } from "ee/reducers/uiReducers/mainCanvasReducer";
|
||||||
import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions";
|
import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions";
|
||||||
import convertDSLtoAuto from "layoutSystems/common/DSLConversions/fixedToAutoLayout";
|
import convertDSLtoAuto from "layoutSystems/common/DSLConversions/fixedToAutoLayout";
|
||||||
import { convertNormalizedDSLToFixed } from "layoutSystems/common/DSLConversions/autoToFixedLayout";
|
import { convertNormalizedDSLToFixed } from "layoutSystems/common/DSLConversions/autoToFixedLayout";
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import type {
|
||||||
CanvasWidgetsReduxState,
|
CanvasWidgetsReduxState,
|
||||||
FlattenedWidgetProps,
|
FlattenedWidgetProps,
|
||||||
} from "ee/reducers/entityReducers/canvasWidgetsReducer";
|
} from "ee/reducers/entityReducers/canvasWidgetsReducer";
|
||||||
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
|
import type { MainCanvasReduxState } from "ee/reducers/uiReducers/mainCanvasReducer";
|
||||||
import { all, call, put, select, takeLatest } from "redux-saga/effects";
|
import { all, call, put, select, takeLatest } from "redux-saga/effects";
|
||||||
import { addAndMoveBuildingBlockToCanvasSaga } from "sagas/BuildingBlockSagas/BuildingBlockAdditionSagas";
|
import { addAndMoveBuildingBlockToCanvasSaga } from "sagas/BuildingBlockSagas/BuildingBlockAdditionSagas";
|
||||||
import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas";
|
import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas";
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import {
|
||||||
getLoadingEntities,
|
getLoadingEntities,
|
||||||
getConfigTree,
|
getConfigTree,
|
||||||
} from "selectors/dataTreeSelectors";
|
} from "selectors/dataTreeSelectors";
|
||||||
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
|
import type { MainCanvasReduxState } from "ee/reducers/uiReducers/mainCanvasReducer";
|
||||||
|
|
||||||
import { getActionEditorSavingMap } from "PluginActionEditor/store";
|
import { getActionEditorSavingMap } from "PluginActionEditor/store";
|
||||||
import {
|
import {
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) {
|
||||||
getMainCanvasProps(state),
|
getMainCanvasProps(state),
|
||||||
);
|
);
|
||||||
const googleMapsApiKey = useSelector(getGoogleMapsApiKey);
|
const googleMapsApiKey = useSelector(getGoogleMapsApiKey);
|
||||||
const renderMode = useSelector(getRenderMode);
|
let renderMode = useSelector(getRenderMode);
|
||||||
|
|
||||||
const widgetName = canvasWidget?.widgetName || metaWidget?.widgetName;
|
const widgetName = canvasWidget?.widgetName || metaWidget?.widgetName;
|
||||||
|
|
||||||
|
|
@ -155,6 +155,14 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) {
|
||||||
|
|
||||||
const widget = metaWidget || canvasWidget;
|
const widget = metaWidget || canvasWidget;
|
||||||
|
|
||||||
|
// This property is primarily used by UI modules in app editor where it wants to load all the underlying
|
||||||
|
// widgets in page mode as they are not editable and mimics the behavior of view mode.
|
||||||
|
// Since in app's edit mode the default render mode is canvas and due to this some widgets do not behave
|
||||||
|
// properly.
|
||||||
|
if (widget?.overrideRenderMode) {
|
||||||
|
renderMode = widget.overrideRenderMode;
|
||||||
|
}
|
||||||
|
|
||||||
if (!skipWidgetPropsHydration) {
|
if (!skipWidgetPropsHydration) {
|
||||||
const canvasWidgetProps = (() => {
|
const canvasWidgetProps = (() => {
|
||||||
if (widgetId === MAIN_CONTAINER_WIDGET_ID) {
|
if (widgetId === MAIN_CONTAINER_WIDGET_ID) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user