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:
Ashit Rath 2025-04-26 20:45:55 +05:30 committed by GitHub
parent 4fbac4d68f
commit 76b1ff1406
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 45 additions and 17 deletions

View File

@ -48,7 +48,7 @@ import type { CrudInfoModalReduxState } from "reducers/uiReducers/crudInfoModalR
import type { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationReducer";
import type { widgetReflow } from "reducers/uiReducers/reflowReducer";
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 SettingsReducer from "ee/reducers/settingsReducer";
import type { TriggerValuesEvaluationState } from "reducers/evaluationReducers/triggerReducer";

View File

@ -33,7 +33,7 @@ import crudInfoModalReducer from "reducers/uiReducers/crudInfoModalReducer";
import { widgetReflowReducer } from "reducers/uiReducers/reflowReducer";
import jsObjectNameReducer from "reducers/uiReducers/jsObjectNameReducer";
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 { editorContextReducer } from "ee/reducers/uiReducers/editorContextReducer";
import libraryReducer from "reducers/uiReducers/libraryReducer";

View File

@ -7,15 +7,16 @@ import {
} from "constants/WidgetConstants";
import type { UpdateCanvasLayoutPayload } from "actions/controlActions";
import type { UpdateCanvasPayload } from "actions/pageActions";
import { klona } from "klona";
const initialState: MainCanvasReduxState = {
export const initialState: MainCanvasReduxState = {
initialized: false,
width: 0,
height: 0,
isMobile: false,
};
const mainCanvasReducer = createImmerReducer(initialState, {
export const handlers = {
[ReduxActionTypes.INIT_CANVAS_LAYOUT]: (
state: MainCanvasReduxState,
action: ReduxAction<UpdateCanvasPayload>,
@ -38,7 +39,12 @@ const mainCanvasReducer = createImmerReducer(initialState, {
state.isMobile =
action.payload.width <= layoutConfigurations.MOBILE.maxWidth;
},
});
[ReduxActionTypes.RESET_EDITOR_REQUEST]: () => {
return klona(initialState);
},
};
const mainCanvasReducer = createImmerReducer(initialState, handlers);
export interface MainCanvasReduxState {
initialized: boolean;

View File

@ -126,7 +126,7 @@ import { getPageList } from "ee/selectors/entitiesSelector";
import { setPreviewModeAction } from "actions/editorActions";
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
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 { getInstanceId } from "ee/selectors/organizationSelectors";
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";

View File

@ -0,0 +1,3 @@
export * from "ce/reducers/uiReducers/mainCanvasReducer";
import { default as CE_mainCanvasReducer } from "ce/reducers/uiReducers/mainCanvasReducer";
export default CE_mainCanvasReducer;

View File

@ -16,9 +16,19 @@ import { getAppThemeSettings } from "ee/selectors/applicationSelectors";
*/
const LayoutSystemBasedCanvas = memo((props: WidgetProps) => {
const renderMode = useSelector(getRenderMode);
let renderMode = useSelector(getRenderMode);
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 { canvasSystem } = useMemo(
() => getLayoutSystem(renderMode, layoutSystemType),

View File

@ -5,6 +5,7 @@ import UIEntitySidebar from "pages/Editor/widgetSidebar/UIEntitySidebar";
import {
createMessage,
UI_ELEMENT_PANEL_SEARCH_TEXT,
WIDGET_PANEL_EMPTY_MESSAGE,
} from "ee/constants/messages";
interface WidgetsListProps {
@ -17,6 +18,7 @@ function WidgetsList({ focusSearchInput }: WidgetsListProps) {
return (
<UIEntitySidebar
cards={cards}
emptyMessage={createMessage(WIDGET_PANEL_EMPTY_MESSAGE)}
entityLoading={entityLoading}
focusSearchInput={focusSearchInput}
groupedCards={groupedCards}

View File

@ -328,6 +328,8 @@ export const groupWidgetCardsByTags = (widgetCards: WidgetCardProps[]) => {
item.tags.forEach((tag) => {
if (groupedCards[tag]) {
groupedCards[tag].push(item);
} else {
groupedCards[tag] = [item];
}
});
}

View File

@ -1,7 +1,3 @@
import {
WIDGET_PANEL_EMPTY_MESSAGE,
createMessage,
} from "ee/constants/messages";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import { ENTITY_EXPLORER_SEARCH_ID } from "constants/Explorer";
import type {
@ -23,10 +19,12 @@ interface UIEntitySidebarProps {
entityLoading?: Partial<Record<WidgetTags, boolean>>;
groupedCards: WidgetCardsGroupedByTags;
searchPlaceholderText?: string;
emptyMessage?: string;
}
function UIEntitySidebar({
cards,
emptyMessage,
entityLoading,
focusSearchInput,
groupedCards,
@ -133,8 +131,7 @@ function UIEntitySidebar({
renderAs="p"
style={{ marginBottom: "15px" }}
>
{createMessage(WIDGET_PANEL_EMPTY_MESSAGE)} `
{searchInputRef.current?.value}`
{emptyMessage} `{searchInputRef.current?.value}`
</Text>
)}
<div>

View File

@ -38,7 +38,7 @@ import {
getIsAutoLayoutMobileBreakPoint,
getMainCanvasProps,
} from "selectors/editorSelectors";
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
import type { MainCanvasReduxState } from "ee/reducers/uiReducers/mainCanvasReducer";
import { updateLayoutForMobileBreakpointAction } from "actions/autoLayoutActions";
import convertDSLtoAuto from "layoutSystems/common/DSLConversions/fixedToAutoLayout";
import { convertNormalizedDSLToFixed } from "layoutSystems/common/DSLConversions/autoToFixedLayout";

View File

@ -26,7 +26,7 @@ import type {
CanvasWidgetsReduxState,
FlattenedWidgetProps,
} 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 { addAndMoveBuildingBlockToCanvasSaga } from "sagas/BuildingBlockSagas/BuildingBlockAdditionSagas";
import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas";

View File

@ -25,7 +25,7 @@ import {
getLoadingEntities,
getConfigTree,
} from "selectors/dataTreeSelectors";
import type { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
import type { MainCanvasReduxState } from "ee/reducers/uiReducers/mainCanvasReducer";
import { getActionEditorSavingMap } from "PluginActionEditor/store";
import {

View File

@ -82,7 +82,7 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) {
getMainCanvasProps(state),
);
const googleMapsApiKey = useSelector(getGoogleMapsApiKey);
const renderMode = useSelector(getRenderMode);
let renderMode = useSelector(getRenderMode);
const widgetName = canvasWidget?.widgetName || metaWidget?.widgetName;
@ -155,6 +155,14 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) {
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) {
const canvasWidgetProps = (() => {
if (widgetId === MAIN_CONTAINER_WIDGET_ID) {