chore: Block Selections when Canvas is in Side by Side mode (#31587)
## Description
This pull request aims to enhance the user experience within the
application by modifying the canvas behavior when it is displayed in
"Side by Side" mode alongside Queries or JavaScript sections. The key
change is the disabling of direct selections on the canvas, allowing
interactions with canvas elements only through cmd + click or by
clicking on the widget's name. This adjustment is intended to facilitate
a view-only mode for the canvas during Queries or JS editing, thereby
improving layout and user interaction.
Additionally, the PR introduces enhancements to the application's
testing framework, focusing on improving test reliability in scenarios
involving UI interaction and state changes. Notable updates include:
- Improved error tooltip handling in CurrencyInput_spec.js.
- Ensured page state saving before verifying element presence in
Listv2_BasicChildWidgetInteraction_spec.js.
- Replaced cy.wait("@updateLayout") with cy.assertPageSave() and
introduced a delay in Listv2_spec.js to accommodate functionality
changes.
- Implemented visibility checks in
TableV2_Button_Icon_validation_spec.js to prevent timing-related test
failures.
These technical updates collectively aim to bolster the application's
testing framework, enhancing the reliability and accuracy of automated
tests, especially in UI interaction and state change scenarios.
#### PR fixes following issue(s)
Fixes #30864
## Automation
/ok-to-test tags="@tag.Widget"
<!-- This is an auto-generated comment: Cypress test results -->
> [!IMPORTANT]
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/8259916944>
> Commit: `15e1cf937a9d15adaea68e16a55006d993a07cbf`
> Cypress dashboard url: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=8259916944&attempt=1"
target="_blank">Click here!</a>
> All cypress tests have passed 🎉🎉🎉
<!-- end of auto-generated comment: Cypress test results -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added new constants for widget selection and focus management.
- Introduced a new event type for tracking widget selections in code
mode.
- **Tests**
- Enhanced test assertions and interactions for better reliability and
error handling in various widgets.
- **Refactor**
- Improved widget selection logic and URL handling for a more intuitive
user experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
eb828bba1d
commit
e99cc39e47
|
|
@ -5,7 +5,6 @@ import {
|
|||
locators,
|
||||
propPane,
|
||||
} from "../../../../support/Objects/ObjectsCore";
|
||||
import Canvas from "../../../../support/Pages/Canvas";
|
||||
import EditorNavigation, {
|
||||
EntityType,
|
||||
PageLeftPane,
|
||||
|
|
@ -94,12 +93,9 @@ describe(
|
|||
);
|
||||
deployMode.NavigateBacktoEditor();
|
||||
|
||||
// Verify multiple widgets selected groups into single container
|
||||
Canvas.selectMultipleWidgets(["Input1", "Select1", "Text3"]);
|
||||
agHelper.GetElement("body").type(`{${agHelper._modifierKey}}{g}`);
|
||||
agHelper.Sleep(1000);
|
||||
PageLeftPane.assertPresence("Container3");
|
||||
entityExplorer.DeleteWidgetFromEntityExplorer("Container3");
|
||||
entityExplorer.DeleteWidgetFromEntityExplorer("Input1");
|
||||
entityExplorer.DeleteWidgetFromEntityExplorer("Select1");
|
||||
entityExplorer.DeleteWidgetFromEntityExplorer("Text3");
|
||||
});
|
||||
|
||||
it("4. Validate visible toggle", () => {
|
||||
|
|
|
|||
|
|
@ -286,7 +286,7 @@ describe(
|
|||
//Should check that widget input is not showing any errors on input
|
||||
cy.get(widgetInput).type("123456789");
|
||||
cy.focused().then(() => {
|
||||
expect(Cypress.$(themelocators.popover)).not.to.exist;
|
||||
cy.get(".error-tooltip .bp3-popover-content").should("not.exist");
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ describe(
|
|||
it("5. Verify Theme change", () => {
|
||||
agHelper.PressEscape();
|
||||
appSettings.OpenPaneAndChangeTheme("Pacific");
|
||||
agHelper.WaitUntilToastDisappear("Theme Pacific applied");
|
||||
[0, 1, 2].forEach((index) => {
|
||||
agHelper.AssertAttribute(
|
||||
locators._listText,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const containerWidgetSelector = `[type="CONTAINER_WIDGET"]`;
|
|||
function dragAndDropToWidget(widgetType, destinationWidget, { x, y }) {
|
||||
const selector = `.t--widget-card-draggable-${widgetType}`;
|
||||
cy.wait(800);
|
||||
PageLeftPane.switchToAddNew();
|
||||
cy.get(selector)
|
||||
.first()
|
||||
.scrollIntoView()
|
||||
|
|
@ -34,9 +35,9 @@ function deleteAllWidgetsInContainer() {
|
|||
force: true,
|
||||
});
|
||||
cy.get("body").type(`{${modifierKey}}{a}`);
|
||||
cy.get("body").type("{del}");
|
||||
|
||||
cy.wait(200);
|
||||
cy.get("body").type("{del}");
|
||||
cy.get(commonlocators.layoutControls).should("be.visible");
|
||||
}
|
||||
|
||||
function checkSelectedRadioValue(selector, value) {
|
||||
|
|
@ -63,12 +64,13 @@ describe(
|
|||
x: 250,
|
||||
y: 50,
|
||||
});
|
||||
cy.assertPageSave();
|
||||
|
||||
// Verify drop
|
||||
cy.get(publishLocators.inputWidget).should("exist");
|
||||
|
||||
// Type value
|
||||
cy.get(publishLocators.inputWidget).find("input").type("abcd");
|
||||
cy.get(publishLocators.inputWidget).find("input").first().type("abcd");
|
||||
|
||||
// Verify if the value got typed
|
||||
cy.get(publishLocators.inputWidget)
|
||||
|
|
@ -78,7 +80,6 @@ describe(
|
|||
deleteAllWidgetsInContainer();
|
||||
|
||||
// Drop Select widget
|
||||
PageLeftPane.switchToAddNew();
|
||||
dragAndDropToWidget("selectwidget", "containerwidget", {
|
||||
x: 250,
|
||||
y: 50,
|
||||
|
|
@ -115,7 +116,6 @@ describe(
|
|||
deleteAllWidgetsInContainer();
|
||||
|
||||
// Drop Checkbox widget
|
||||
PageLeftPane.switchToAddNew();
|
||||
dragAndDropToWidget("checkboxgroupwidget", "containerwidget", {
|
||||
x: 250,
|
||||
y: 50,
|
||||
|
|
@ -158,7 +158,6 @@ describe(
|
|||
deleteAllWidgetsInContainer();
|
||||
|
||||
// Drop Switch widget
|
||||
PageLeftPane.switchToAddNew();
|
||||
dragAndDropToWidget("switchwidget", "containerwidget", {
|
||||
x: 250,
|
||||
y: 50,
|
||||
|
|
@ -201,9 +200,8 @@ describe(
|
|||
|
||||
_.deployMode.NavigateBacktoEditor();
|
||||
deleteAllWidgetsInContainer();
|
||||
|
||||
cy.wait(800);
|
||||
// Drop Radio widget
|
||||
PageLeftPane.switchToAddNew();
|
||||
dragAndDropToWidget("radiogroupwidget", "containerwidget", {
|
||||
x: 250,
|
||||
y: 50,
|
||||
|
|
|
|||
|
|
@ -68,7 +68,8 @@ describe(
|
|||
entityExplorer.DragDropWidgetNVerify(widget);
|
||||
//cy.dragAndDropToWidget(widget, "listwidgetv2", { x: 350, y: 50 });
|
||||
agHelper.GetNClick(propPane._deleteWidget);
|
||||
cy.wait("@updateLayout");
|
||||
cy.assertPageSave();
|
||||
cy.wait(800);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
@ -88,7 +89,8 @@ describe(
|
|||
cy.assertPageSave();
|
||||
cy.get(`.t--draggable-${widget}`).should("exist");
|
||||
cy.get(widgetsPage.removeWidget).click({ force: true });
|
||||
cy.wait("@updateLayout");
|
||||
cy.assertPageSave();
|
||||
cy.wait(800);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ describe(
|
|||
//cy.createModal("Modal", this.dataSet.ModalName);
|
||||
cy.createModal("Modal", "onRowSelected");
|
||||
cy.isSelectRow(1);
|
||||
cy.get(".bp3-overlay-backdrop").click({ force: true });
|
||||
cy.get(".bp3-overlay-backdrop").last().click({ force: true });
|
||||
cy.isSelectRow(2);
|
||||
cy.get(".bp3-overlay-backdrop").click({ force: true });
|
||||
cy.get(".bp3-overlay-backdrop").last().click({ force: true });
|
||||
});
|
||||
|
||||
it("2. Table widget V2 with button colour change validation", function () {
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@
|
|||
"evaluatedCurrentValue": "div:last-of-type .t--CodeEditor-evaluatedValue > div:last-of-type pre",
|
||||
"entityExplorersearch": "#entity-explorer-search",
|
||||
"saveStatusContainer": ".t--save-status-container",
|
||||
"saveStatusIsSaving": "t--save-status-is-saving",
|
||||
"statusSaving": ".t--save-status-is-saving",
|
||||
"saveStatusError": ".t--save-status-error",
|
||||
"selectWidgetVirtualList": ".menu-virtual-list div",
|
||||
|
|
|
|||
|
|
@ -47,9 +47,15 @@ export const createModalAction = (
|
|||
|
||||
export const focusWidget = (
|
||||
widgetId?: string,
|
||||
): ReduxAction<{ widgetId?: string }> => ({
|
||||
alt?: boolean,
|
||||
): ReduxAction<{ widgetId?: string; alt?: boolean }> => ({
|
||||
type: ReduxActionTypes.FOCUS_WIDGET,
|
||||
payload: { widgetId },
|
||||
payload: { widgetId, alt },
|
||||
});
|
||||
|
||||
export const altFocusWidget = (alt: boolean) => ({
|
||||
type: ReduxActionTypes.ALT_FOCUS_WIDGET,
|
||||
payload: alt,
|
||||
});
|
||||
|
||||
export const showModal = (id: string, shouldSelectModal = true) => {
|
||||
|
|
@ -144,3 +150,10 @@ export const partialExportWidgets = (params: PartialExportParams) => {
|
|||
payload: params,
|
||||
};
|
||||
};
|
||||
|
||||
export const setWidgetSelectionBlock = (payload: boolean) => {
|
||||
return {
|
||||
type: ReduxActionTypes.SET_WIDGET_SELECTION_BLOCK,
|
||||
payload,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -917,6 +917,8 @@ const ActionTypes = {
|
|||
SET_API_PANE_DEBUGGER_STATE: "SET_API_PANE_DEBUGGER_STATE",
|
||||
SET_JS_PANE_DEBUGGER_STATE: "SET_JS_PANE_DEBUGGER_STATE",
|
||||
SET_CANVAS_DEBUGGER_STATE: "SET_CANVAS_DEBUGGER_STATE",
|
||||
SET_WIDGET_SELECTION_BLOCK: "SET_WIDGET_SELECTION_BLOCK",
|
||||
ALT_FOCUS_WIDGET: "ALT_FOCUS_WIDGET",
|
||||
};
|
||||
|
||||
export const ReduxActionTypes = {
|
||||
|
|
|
|||
|
|
@ -385,7 +385,8 @@ export type ONBOARDING_FLOW_EVENTS =
|
|||
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_START_FROM_DATA_PAGE"
|
||||
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_DATASOURCE_FORM_PAGE"
|
||||
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_START_FROM_TEMPLATE_PAGE"
|
||||
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_TEMPLATE_DETAILS_PAGE";
|
||||
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_TEMPLATE_DETAILS_PAGE"
|
||||
| "CODE_MODE_WIDGET_SELECTION";
|
||||
|
||||
export type DATASOURCE_SCHEMA_EVENTS =
|
||||
| "DATASOURCE_SCHEMA_SEARCH"
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ import { getShouldAllowDrag } from "selectors/widgetDragSelectors";
|
|||
import { combinedPreviewModeSelector } from "selectors/editorSelectors";
|
||||
import { getAnvilSpaceDistributionStatus } from "layoutSystems/anvil/integrations/selectors";
|
||||
|
||||
const DraggableWrapper = styled.div`
|
||||
const DraggableWrapper = styled.div<{ draggable: boolean }>`
|
||||
display: block;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
cursor: grab;
|
||||
cursor: ${(props) => (props.draggable ? "grab" : "unset")};
|
||||
`;
|
||||
|
||||
export interface DraggableComponentProps {
|
||||
|
|
@ -37,7 +37,7 @@ export interface DraggableComponentProps {
|
|||
type: string;
|
||||
children: ReactNode;
|
||||
generateDragState: (
|
||||
e: React.DragEvent<Element>,
|
||||
e: React.DragEvent,
|
||||
draggableRef: HTMLElement,
|
||||
) => SetDraggingStateActionPayload;
|
||||
dragDisabled: boolean;
|
||||
|
|
@ -53,7 +53,6 @@ const WidgetBoundaries = styled.div`
|
|||
${(props) => getColorWithOpacity(props.theme.colors.textAnchor, 0.5)};
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
`;
|
||||
|
||||
|
|
@ -100,14 +99,14 @@ function DraggableComponent(props: DraggableComponentProps) {
|
|||
!props.isFlexChild && (isCurrentWidgetDragging || isDraggingSibling);
|
||||
|
||||
// When mouse is over this draggable
|
||||
const handleMouseOver = (e: any) => {
|
||||
const handleMouseOver = (e: React.MouseEvent) => {
|
||||
focusWidget &&
|
||||
!isResizingOrDragging &&
|
||||
!isFocused &&
|
||||
!isDistributingSpace &&
|
||||
!props.resizeDisabled &&
|
||||
!isPreviewMode &&
|
||||
focusWidget(props.widgetId);
|
||||
focusWidget(props.widgetId, e.metaKey);
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import { useCurrentAppState } from "pages/Editor/IDE/hooks";
|
|||
import { getIsAppSettingsPaneWithNavigationTabOpen } from "selectors/appSettingsPaneSelectors";
|
||||
import { getLayoutSystemType } from "selectors/layoutSystemSelectors";
|
||||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import { getWidgetSelectionBlock } from "selectors/ui";
|
||||
import {
|
||||
isAutoHeightEnabledForWidget,
|
||||
isAutoHeightEnabledForWidgetWithLimits,
|
||||
|
|
@ -259,6 +260,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) {
|
|||
);
|
||||
// Are we changing the auto height limits by dragging the signifiers?
|
||||
const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState();
|
||||
const isWidgetSelectionBlocked = useSelector(getWidgetSelectionBlock);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
|
@ -327,7 +329,12 @@ export function DropTargetComponent(props: DropTargetComponentProps) {
|
|||
(e.target as HTMLDivElement).dataset.testid === selectionDiv ||
|
||||
(e.target as HTMLDivElement).dataset.testid === mainCanvasId;
|
||||
|
||||
if (!isResizing && !isDragging && !isAutoHeightWithLimitsChanging) {
|
||||
if (
|
||||
!isResizing &&
|
||||
!isDragging &&
|
||||
!isAutoHeightWithLimitsChanging &&
|
||||
!isWidgetSelectionBlocked
|
||||
) {
|
||||
// Check if Target is the MainCanvas
|
||||
if (isTargetMainCanvas) {
|
||||
goToWidgetAdd();
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import {
|
|||
combinedPreviewModeSelector,
|
||||
snipingModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { getWidgetSelectionBlock } from "../../../selectors/ui";
|
||||
const minSize = 100;
|
||||
|
||||
/**
|
||||
|
|
@ -101,7 +102,9 @@ export const ModalResizableLayer = ({
|
|||
};
|
||||
const isPreviewMode = useSelector(combinedPreviewModeSelector);
|
||||
const isSnipingMode = useSelector(snipingModeSelector);
|
||||
const enableResizing = !isSnipingMode && !isPreviewMode;
|
||||
const isWidgetSelectionBlocked = useSelector(getWidgetSelectionBlock);
|
||||
const enableResizing =
|
||||
!isSnipingMode && !isPreviewMode && !isWidgetSelectionBlocked;
|
||||
return (
|
||||
<ModalResizable
|
||||
allowResize
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ import {
|
|||
computeFinalRowCols,
|
||||
computeFinalAutoLayoutRowCols,
|
||||
} from "layoutSystems/common/resizer/ResizableUtils";
|
||||
import {
|
||||
getAltBlockWidgetSelection,
|
||||
getWidgetSelectionBlock,
|
||||
} from "selectors/ui";
|
||||
|
||||
export type ResizableComponentProps = WidgetProps & {
|
||||
paddingOffset: number;
|
||||
|
|
@ -80,6 +84,8 @@ export const ResizableComponent = memo(function ResizableComponent(
|
|||
const Resizable = isAutoLayout ? AutoLayoutResizable : FixedLayoutResizable;
|
||||
const isSnipingMode = useSelector(snipingModeSelector);
|
||||
const isPreviewMode = useSelector(combinedPreviewModeSelector);
|
||||
const isWidgetSelectionBlock = useSelector(getWidgetSelectionBlock);
|
||||
const isAltWidgetSelectionBlock = useSelector(getAltBlockWidgetSelection);
|
||||
const isAppSettingsPaneWithNavigationTabOpen = useSelector(
|
||||
getIsAppSettingsPaneWithNavigationTabOpen,
|
||||
);
|
||||
|
|
@ -325,6 +331,7 @@ export const ResizableComponent = memo(function ResizableComponent(
|
|||
!props.resizeDisabled &&
|
||||
!isSnipingMode &&
|
||||
!isPreviewMode &&
|
||||
!isWidgetSelectionBlock &&
|
||||
!isAppSettingsPaneWithNavigationTabOpen;
|
||||
const { updateDropTargetRows } = useContext(DropTargetContext);
|
||||
|
||||
|
|
@ -387,6 +394,7 @@ export const ResizableComponent = memo(function ResizableComponent(
|
|||
!isPreviewMode &&
|
||||
!isAppSettingsPaneWithNavigationTabOpen &&
|
||||
!isDragging &&
|
||||
!isAltWidgetSelectionBlock &&
|
||||
(isHovered || isSelected);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { Classes, Tooltip } from "@blueprintjs/core";
|
||||
import { Colors } from "constants/Colors";
|
||||
import type { CSSProperties } from "react";
|
||||
import React from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { snipingModeSelector } from "selectors/editorSelectors";
|
||||
import styled from "styled-components";
|
||||
import { Icon } from "design-system";
|
||||
import { Icon, Text, Tooltip } from "design-system";
|
||||
|
||||
// I honestly can't think of a better name for this enum
|
||||
export enum Activities {
|
||||
HOVERING,
|
||||
|
|
@ -13,13 +13,7 @@ export enum Activities {
|
|||
ACTIVE,
|
||||
NONE,
|
||||
}
|
||||
const StyledTooltip = styled(Tooltip)<{
|
||||
children?: React.ReactNode;
|
||||
}>`
|
||||
.${Classes.POPOVER_TARGET} {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const WidgetNameBoundary = 1;
|
||||
const BORDER_RADIUS = 4;
|
||||
const SettingsWrapper = styled.div<{ widgetWidth: number; inverted: boolean }>`
|
||||
|
|
@ -60,10 +54,6 @@ const WidgetName = styled.span`
|
|||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledErrorIcon = styled(Icon)`
|
||||
margin-right: ${(props) => props.theme.spaces[1]}px;
|
||||
`;
|
||||
|
||||
interface SettingsControlProps {
|
||||
toggleSettings: (e: any) => void;
|
||||
activity: Activities;
|
||||
|
|
@ -111,17 +101,17 @@ const getStyles = (
|
|||
|
||||
export function SettingsControl(props: SettingsControlProps) {
|
||||
const isSnipingMode = useSelector(snipingModeSelector);
|
||||
const errorIcon = <StyledErrorIcon name="warning" size="sm" />;
|
||||
const errorIcon = <Icon name="warning" size="sm" />;
|
||||
|
||||
return (
|
||||
<StyledTooltip
|
||||
<Tooltip
|
||||
content={
|
||||
isSnipingMode
|
||||
? `Bind to widget ${props.name}`
|
||||
: "Edit widget properties"
|
||||
<Text color="var(--ads-v2-color-white)">
|
||||
{isSnipingMode ? `Bind to widget ${props.name}` : `Edit widget`}
|
||||
</Text>
|
||||
}
|
||||
hoverOpenDelay={500}
|
||||
position="top-right"
|
||||
mouseEnterDelay={0}
|
||||
placement="topRight"
|
||||
>
|
||||
<SettingsWrapper
|
||||
className="t--widget-propertypane-toggle"
|
||||
|
|
@ -143,7 +133,7 @@ export function SettingsControl(props: SettingsControlProps) {
|
|||
{isSnipingMode ? `Bind to ${props.name}` : props.name}
|
||||
</WidgetName>
|
||||
</SettingsWrapper>
|
||||
</StyledTooltip>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import { getAbsolutePixels } from "utils/helpers";
|
|||
import type { XYCord } from "layoutSystems/common/canvasArenas/ArenaTypes";
|
||||
import { useCanvasDragToScroll } from "layoutSystems/common/canvasArenas/useCanvasDragToScroll";
|
||||
import { StickyCanvasArena } from "layoutSystems/common/canvasArenas/StickyCanvasArena";
|
||||
import { getWidgetSelectionBlock } from "../../../../selectors/ui";
|
||||
|
||||
export interface SelectedArenaDimensions {
|
||||
top: number;
|
||||
|
|
@ -71,6 +72,7 @@ export function CanvasSelectionArena({
|
|||
);
|
||||
const appMode = useSelector(getAppMode);
|
||||
const isPreviewMode = useSelector(combinedPreviewModeSelector);
|
||||
const isWidgetSelectionBlocked = useSelector(getWidgetSelectionBlock);
|
||||
const isAppSettingsPaneWithNavigationTabOpen = useSelector(
|
||||
getIsAppSettingsPaneWithNavigationTabOpen,
|
||||
);
|
||||
|
|
@ -501,6 +503,7 @@ export function CanvasSelectionArena({
|
|||
!(
|
||||
isDragging ||
|
||||
isPreviewMode ||
|
||||
isWidgetSelectionBlocked ||
|
||||
isAppSettingsPaneWithNavigationTabOpen ||
|
||||
dropDisabled
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { CANVAS_ART_BOARD } from "constants/componentClassNameConstants";
|
|||
import { renderAppsmithCanvas } from "layoutSystems/CanvasFactory";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
import { getAppThemeSettings } from "@appsmith/selectors/applicationSelectors";
|
||||
import CodeModeTooltip from "pages/Editor/WidgetsEditor/CodeModeTooltip";
|
||||
|
||||
interface CanvasProps {
|
||||
widgetsStructure: CanvasWidgetStructure;
|
||||
|
|
@ -83,20 +84,22 @@ const Canvas = (props: CanvasProps) => {
|
|||
|
||||
const renderChildren = () => {
|
||||
return (
|
||||
<Wrapper
|
||||
$enableMainCanvasResizer={!!props.enableMainCanvasResizer}
|
||||
background={isWDSEnabled ? "" : backgroundForCanvas}
|
||||
className={`relative t--canvas-artboard ${paddingBottomClass} transition-all duration-400 ${marginHorizontalClass} ${getViewportClassName(
|
||||
canvasWidth,
|
||||
)}`}
|
||||
data-testid={"t--canvas-artboard"}
|
||||
id={CANVAS_ART_BOARD}
|
||||
ref={isWDSEnabled ? undefined : focusRef}
|
||||
width={canvasWidth}
|
||||
>
|
||||
{props.widgetsStructure.widgetId &&
|
||||
renderAppsmithCanvas(props.widgetsStructure as WidgetProps)}
|
||||
</Wrapper>
|
||||
<CodeModeTooltip>
|
||||
<Wrapper
|
||||
$enableMainCanvasResizer={!!props.enableMainCanvasResizer}
|
||||
background={isWDSEnabled ? "" : backgroundForCanvas}
|
||||
className={`relative t--canvas-artboard ${paddingBottomClass} transition-all duration-400 ${marginHorizontalClass} ${getViewportClassName(
|
||||
canvasWidth,
|
||||
)}`}
|
||||
data-testid={"t--canvas-artboard"}
|
||||
id={CANVAS_ART_BOARD}
|
||||
ref={isWDSEnabled ? undefined : focusRef}
|
||||
width={canvasWidth}
|
||||
>
|
||||
{props.widgetsStructure.widgetId &&
|
||||
renderAppsmithCanvas(props.widgetsStructure as WidgetProps)}
|
||||
</Wrapper>
|
||||
</CodeModeTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import { Route, Switch, useRouteMatch } from "react-router";
|
|||
import * as Sentry from "@sentry/react";
|
||||
import useRoutes from "@appsmith/pages/Editor/IDE/MainPane/useRoutes";
|
||||
import EditorTabs from "pages/Editor/IDE/EditorTabs/FullScreenTabs";
|
||||
import { useWidgetSelectionBlockListener } from "pages/Editor/IDE/hooks";
|
||||
|
||||
const SentryRoute = Sentry.withSentryRouting(Route);
|
||||
export const MainPane = (props: { id: string }) => {
|
||||
const { path } = useRouteMatch();
|
||||
const routes = useRoutes(path);
|
||||
useWidgetSelectionBlockListener();
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from "@appsmith/entities/IDE/constants";
|
||||
import { useLocation } from "react-router";
|
||||
import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getIDEViewMode, getIsSideBySideEnabled } from "selectors/ideSelectors";
|
||||
import { getPropertyPaneWidth } from "selectors/propertyPaneSelectors";
|
||||
import { getCurrentPageId } from "@appsmith/selectors/entitiesSelector";
|
||||
|
|
@ -28,6 +28,8 @@ import {
|
|||
} from "constants/AppConstants";
|
||||
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
|
||||
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
|
||||
import { getIsAltFocusWidget, getWidgetSelectionBlock } from "selectors/ui";
|
||||
import { altFocusWidget, setWidgetSelectionBlock } from "actions/widgetActions";
|
||||
|
||||
export const useCurrentAppState = () => {
|
||||
const [appState, setAppState] = useState(EditorState.EDITOR);
|
||||
|
|
@ -215,3 +217,41 @@ export const useIsEditorPaneSegmentsEnabled = () => {
|
|||
|
||||
return isEditorSegmentsReleaseEnabled || isEditorSegmentsRolloutEnabled;
|
||||
};
|
||||
|
||||
export function useWidgetSelectionBlockListener() {
|
||||
const { pathname } = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const currentFocus = identifyEntityFromPath(pathname);
|
||||
const isAltFocused = useSelector(getIsAltFocusWidget);
|
||||
const widgetSelectionIsBlocked = useSelector(getWidgetSelectionBlock);
|
||||
|
||||
useEffect(() => {
|
||||
const inUIMode = [
|
||||
FocusEntity.CANVAS,
|
||||
FocusEntity.PROPERTY_PANE,
|
||||
FocusEntity.WIDGET_LIST,
|
||||
].includes(currentFocus.entity);
|
||||
dispatch(setWidgetSelectionBlock(!inUIMode));
|
||||
}, [currentFocus]);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener("keyup", handleKeyUp);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
window.removeEventListener("keyup", handleKeyUp);
|
||||
};
|
||||
}, [isAltFocused, widgetSelectionIsBlocked]);
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!isAltFocused && widgetSelectionIsBlocked && e.metaKey) {
|
||||
dispatch(altFocusWidget(e.metaKey));
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyUp = (e: KeyboardEvent) => {
|
||||
if (!e.metaKey && widgetSelectionIsBlocked) {
|
||||
dispatch(altFocusWidget(e.metaKey));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
import { Tooltip } from "design-system";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { modText } from "utils/helpers";
|
||||
import { useSelector } from "react-redux";
|
||||
import { getWidgetSelectionBlock } from "selectors/ui";
|
||||
import { retrieveCodeWidgetNavigationUsed } from "utils/storage";
|
||||
|
||||
const CodeModeTooltip = (props: { children: React.ReactElement }) => {
|
||||
const isWidgetSelectionBlock = useSelector(getWidgetSelectionBlock);
|
||||
const [shouldShow, setShouldShow] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
retrieveCodeWidgetNavigationUsed()
|
||||
.then((timesUsed) => {
|
||||
if (timesUsed < 2) {
|
||||
setShouldShow(true);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setShouldShow(true);
|
||||
});
|
||||
}, [isWidgetSelectionBlock]);
|
||||
if (!isWidgetSelectionBlock) return props.children;
|
||||
return (
|
||||
<Tooltip
|
||||
content={`💡 ${modText()} click a widget to navigate to UI mode.`}
|
||||
isDisabled={!shouldShow}
|
||||
placement={"bottom"}
|
||||
showArrow={false}
|
||||
trigger={"hover"}
|
||||
>
|
||||
{props.children}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeModeTooltip;
|
||||
|
|
@ -22,6 +22,8 @@ const initialState: WidgetDragResizeState = {
|
|||
isDistributingSpace: false,
|
||||
},
|
||||
isDraggingDisabled: false,
|
||||
blockSelection: false,
|
||||
altFocus: false,
|
||||
};
|
||||
|
||||
export const widgetDraggingReducer = createImmerReducer(initialState, {
|
||||
|
|
@ -100,11 +102,20 @@ export const widgetDraggingReducer = createImmerReducer(initialState, {
|
|||
},
|
||||
[ReduxActionTypes.FOCUS_WIDGET]: (
|
||||
state: WidgetDragResizeState,
|
||||
action: ReduxAction<{ widgetId?: string }>,
|
||||
action: ReduxAction<{ widgetId?: string; alt?: boolean }>,
|
||||
) => {
|
||||
if (state.focusedWidget !== action.payload.widgetId) {
|
||||
state.focusedWidget = action.payload.widgetId;
|
||||
}
|
||||
if (state.altFocus !== action.payload.alt) {
|
||||
state.altFocus = !!action.payload.alt;
|
||||
}
|
||||
},
|
||||
[ReduxActionTypes.ALT_FOCUS_WIDGET]: (
|
||||
state: WidgetDragResizeState,
|
||||
action: ReduxAction<boolean>,
|
||||
) => {
|
||||
state.altFocus = action.payload;
|
||||
},
|
||||
[ReduxActionTypes.SET_SELECTED_WIDGET_ANCESTRY]: (
|
||||
state: WidgetDragResizeState,
|
||||
|
|
@ -118,6 +129,12 @@ export const widgetDraggingReducer = createImmerReducer(initialState, {
|
|||
) => {
|
||||
state.entityExplorerAncestry = action.payload;
|
||||
},
|
||||
[ReduxActionTypes.SET_WIDGET_SELECTION_BLOCK]: (
|
||||
state: WidgetDragResizeState,
|
||||
action: ReduxAction<boolean>,
|
||||
) => {
|
||||
state.blockSelection = action.payload;
|
||||
},
|
||||
//space distribution redux
|
||||
[AnvilReduxActionTypes.ANVIL_SPACE_DISTRIBUTION_START]: (
|
||||
state: WidgetDragResizeState,
|
||||
|
|
@ -166,6 +183,8 @@ export interface WidgetDragResizeState {
|
|||
selectedWidgets: string[];
|
||||
isAutoCanvasResizing: boolean;
|
||||
isDraggingDisabled: boolean;
|
||||
blockSelection: boolean;
|
||||
altFocus: boolean;
|
||||
}
|
||||
|
||||
export default widgetDraggingReducer;
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidg
|
|||
import { all, call, put, select, take, takeLatest } from "redux-saga/effects";
|
||||
import type { SetSelectionResult } from "sagas/WidgetSelectUtils";
|
||||
import {
|
||||
SelectionRequestType,
|
||||
assertParentId,
|
||||
getWidgetAncestry,
|
||||
isInvalidSelectionRequest,
|
||||
pushPopWidgetSelection,
|
||||
selectAllWidgetsInCanvasSaga,
|
||||
SelectionRequestType,
|
||||
selectMultipleWidgets,
|
||||
selectOneWidget,
|
||||
shiftSelectWidgets,
|
||||
|
|
@ -41,7 +41,11 @@ import {
|
|||
getIsFetchingPage,
|
||||
snipingModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { getLastSelectedWidget, getSelectedWidgets } from "selectors/ui";
|
||||
import {
|
||||
getLastSelectedWidget,
|
||||
getSelectedWidgets,
|
||||
getWidgetSelectionBlock,
|
||||
} from "selectors/ui";
|
||||
import { areArraysEqual } from "utils/AppsmithUtils";
|
||||
import { quickScrollToWidget } from "utils/helpers";
|
||||
import history, { NavigationMethod } from "utils/history";
|
||||
|
|
@ -56,6 +60,11 @@ import { selectFeatureFlags } from "@appsmith/selectors/featureFlagsSelectors";
|
|||
import type { FeatureFlags } from "@appsmith/entities/FeatureFlag";
|
||||
import { getWidgetSelectorByWidgetId } from "selectors/layoutSystemSelectors";
|
||||
import { getAppViewerPageIdFromPath } from "@appsmith/pages/Editor/Explorer/helpers";
|
||||
import AnalyticsUtil from "../utils/AnalyticsUtil";
|
||||
import {
|
||||
retrieveCodeWidgetNavigationUsed,
|
||||
storeCodeWidgetNavigationUsed,
|
||||
} from "../utils/storage";
|
||||
|
||||
// The following is computed to be used in the entity explorer
|
||||
// Every time a widget is selected, we need to expand widget entities
|
||||
|
|
@ -208,9 +217,16 @@ function* appendSelectedWidgetToUrlSaga(
|
|||
invokedBy?: NavigationMethod,
|
||||
) {
|
||||
const isSnipingMode: boolean = yield select(snipingModeSelector);
|
||||
const isWidgetSelectionBlocked: boolean = yield select(
|
||||
getWidgetSelectionBlock,
|
||||
);
|
||||
const timesUsedCodeModeWidgetSelection: number = yield call(
|
||||
retrieveCodeWidgetNavigationUsed,
|
||||
);
|
||||
const appMode: APP_MODE = yield select(getAppMode);
|
||||
const viewMode = appMode === APP_MODE.PUBLISHED;
|
||||
if (isSnipingMode || viewMode) return;
|
||||
|
||||
const { pathname } = window.location;
|
||||
const currentPageId: string = yield select(getCurrentPageId);
|
||||
const currentURL = pathname;
|
||||
|
|
@ -225,6 +241,15 @@ function* appendSelectedWidgetToUrlSaga(
|
|||
persistExistingParams: true,
|
||||
selectedWidgets: [MAIN_CONTAINER_WIDGET_ID],
|
||||
});
|
||||
if (invokedBy === NavigationMethod.CanvasClick && isWidgetSelectionBlocked) {
|
||||
AnalyticsUtil.logEvent("CODE_MODE_WIDGET_SELECTION");
|
||||
if (timesUsedCodeModeWidgetSelection < 2) {
|
||||
yield call(
|
||||
storeCodeWidgetNavigationUsed,
|
||||
timesUsedCodeModeWidgetSelection + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (currentURL !== newUrl) {
|
||||
history.push(newUrl, { invokedBy });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,3 +68,16 @@ export const getIsImportingCurl = (state: AppState) =>
|
|||
|
||||
export const getIsConsolidatedPageLoading = (state: AppState) =>
|
||||
state.ui.consolidatedPageLoad.isLoading;
|
||||
|
||||
export const getIsAltFocusWidget = (state: AppState) =>
|
||||
state.ui.widgetDragResize.altFocus;
|
||||
|
||||
export const getWidgetSelectionBlock = (state: AppState) =>
|
||||
state.ui.widgetDragResize.blockSelection;
|
||||
|
||||
export const getAltBlockWidgetSelection = createSelector(
|
||||
[getWidgetSelectionBlock, getIsAltFocusWidget],
|
||||
(isWidgetSelectionBlock, isAltFocusWidget) => {
|
||||
return isWidgetSelectionBlock ? !isAltFocusWidget : false;
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
combinedPreviewModeSelector,
|
||||
snipingModeSelector,
|
||||
} from "./editorSelectors";
|
||||
import { getWidgetSelectionBlock } from "./ui";
|
||||
|
||||
export const getIsDragging = (state: AppState) =>
|
||||
state.ui.widgetDragResize.isDragging;
|
||||
|
|
@ -25,6 +26,7 @@ export const getShouldAllowDrag = createSelector(
|
|||
combinedPreviewModeSelector,
|
||||
snipingModeSelector,
|
||||
getIsAppSettingsPaneWithNavigationTabOpen,
|
||||
getWidgetSelectionBlock,
|
||||
(
|
||||
isResizing,
|
||||
isDragging,
|
||||
|
|
@ -32,6 +34,7 @@ export const getShouldAllowDrag = createSelector(
|
|||
isPreviewMode,
|
||||
isSnipingMode,
|
||||
isAppSettingsPaneWithNavigationTabOpen,
|
||||
widgetSelectionIsBlocked,
|
||||
) => {
|
||||
return (
|
||||
!isResizing &&
|
||||
|
|
@ -39,7 +42,8 @@ export const getShouldAllowDrag = createSelector(
|
|||
!isDraggingDisabled &&
|
||||
!isSnipingMode &&
|
||||
!isPreviewMode &&
|
||||
!isAppSettingsPaneWithNavigationTabOpen
|
||||
!isAppSettingsPaneWithNavigationTabOpen &&
|
||||
!widgetSelectionIsBlocked
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { getNextEntityName } from "utils/AppsmithUtils";
|
|||
|
||||
import WidgetFactory from "WidgetProvider/factory";
|
||||
import {
|
||||
getAltBlockWidgetSelection,
|
||||
getFocusedWidget,
|
||||
getLastSelectedWidget,
|
||||
getSelectedWidgets,
|
||||
|
|
@ -179,6 +180,7 @@ export const shouldWidgetIgnoreClicksSelector = (widgetId: string) => {
|
|||
getAppMode,
|
||||
combinedPreviewModeSelector,
|
||||
getIsAutoHeightWithLimitsChanging,
|
||||
getAltBlockWidgetSelection,
|
||||
(
|
||||
focusedWidgetId,
|
||||
isTableFilterPaneVisible,
|
||||
|
|
@ -188,6 +190,7 @@ export const shouldWidgetIgnoreClicksSelector = (widgetId: string) => {
|
|||
appMode,
|
||||
isPreviewMode,
|
||||
isAutoHeightWithLimitsChanging,
|
||||
isWidgetSelectionBlock,
|
||||
) => {
|
||||
const isFocused = focusedWidgetId === widgetId;
|
||||
|
||||
|
|
@ -199,7 +202,8 @@ export const shouldWidgetIgnoreClicksSelector = (widgetId: string) => {
|
|||
appMode !== APP_MODE.EDIT ||
|
||||
!isFocused ||
|
||||
isTableFilterPaneVisible ||
|
||||
isAutoHeightWithLimitsChanging
|
||||
isAutoHeightWithLimitsChanging ||
|
||||
isWidgetSelectionBlock
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useSelector } from "react-redux";
|
|||
import { getIsAppSettingsPaneWithNavigationTabOpen } from "selectors/appSettingsPaneSelectors";
|
||||
import { getLayoutSystemType } from "selectors/layoutSystemSelectors";
|
||||
import { LayoutSystemTypes } from "layoutSystems/types";
|
||||
import { getWidgetSelectionBlock } from "../../selectors/ui";
|
||||
|
||||
export const useAllowEditorDragToSelect = () => {
|
||||
// This state tells us whether a `ResizableComponent` is resizing
|
||||
|
|
@ -46,6 +47,8 @@ export const useAllowEditorDragToSelect = () => {
|
|||
getIsAppSettingsPaneWithNavigationTabOpen,
|
||||
);
|
||||
|
||||
const isWidgetSelectionBlocked = useSelector(getWidgetSelectionBlock);
|
||||
|
||||
return (
|
||||
isFixedLayout &&
|
||||
!isAutoCanvasResizing &&
|
||||
|
|
@ -53,6 +56,7 @@ export const useAllowEditorDragToSelect = () => {
|
|||
!isDraggingDisabled &&
|
||||
!isSnipingMode &&
|
||||
!isPreviewMode &&
|
||||
!isAppSettingsPaneWithNavigationTabOpen
|
||||
!isAppSettingsPaneWithNavigationTabOpen &&
|
||||
!isWidgetSelectionBlocked
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { focusWidget } from "actions/widgetActions";
|
||||
import { altFocusWidget, focusWidget } from "actions/widgetActions";
|
||||
import { selectWidgetInitAction } from "actions/widgetSelectionActions";
|
||||
|
||||
import { useCallback } from "react";
|
||||
|
|
@ -23,7 +23,8 @@ export const useWidgetSelection = () => {
|
|||
[dispatch],
|
||||
),
|
||||
focusWidget: useCallback(
|
||||
(widgetId?: string) => dispatch(focusWidget(widgetId)),
|
||||
(widgetId?: string, altFocus?: boolean) =>
|
||||
dispatch(focusWidget(widgetId, altFocus)),
|
||||
[dispatch],
|
||||
),
|
||||
deselectAll: useCallback(
|
||||
|
|
@ -38,5 +39,8 @@ export const useWidgetSelection = () => {
|
|||
[dispatch],
|
||||
),
|
||||
goToWidgetAdd: useCallback(() => history.push(builderURL({})), []),
|
||||
altFocus: useCallback((alt: boolean) => {
|
||||
dispatch(altFocusWidget(alt));
|
||||
}, []),
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export const STORAGE_KEYS: {
|
|||
AI_KNOWLEDGE_BASE: "AI_KNOWLEDGE_BASE",
|
||||
PARTNER_PROGRAM_CALLOUT: "PARTNER_PROGRAM_CALLOUT",
|
||||
IDE_VIEW_MODE: "IDE_VIEW_MODE",
|
||||
CODE_WIDGET_NAVIGATION_USED: "CODE_WIDGET_NAVIGATION_USED",
|
||||
};
|
||||
|
||||
const store = localforage.createInstance({
|
||||
|
|
@ -882,3 +883,26 @@ export const retrieveIDEViewMode = async (): Promise<
|
|||
log.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const storeCodeWidgetNavigationUsed = async (count: number) => {
|
||||
try {
|
||||
await store.setItem(STORAGE_KEYS.CODE_WIDGET_NAVIGATION_USED, count);
|
||||
return true;
|
||||
} catch (error) {
|
||||
log.error("An error occurred while setting CODE_WIDGET_NAVIGATION_USED");
|
||||
log.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const retrieveCodeWidgetNavigationUsed = async (): Promise<number> => {
|
||||
try {
|
||||
const mode = (await store.getItem(
|
||||
STORAGE_KEYS.CODE_WIDGET_NAVIGATION_USED,
|
||||
)) as number;
|
||||
return mode || 0;
|
||||
} catch (error) {
|
||||
log.error("An error occurred while fetching CODE_WIDGET_NAVIGATION_USED");
|
||||
log.error(error);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ import { getTableFilterState } from "selectors/tableFilterSelectors";
|
|||
import { getWidgetMetaProps } from "sagas/selectors";
|
||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
import type { WidgetProps } from "widgets/BaseWidget";
|
||||
import { selectWidgetInitAction } from "actions/widgetSelectionActions";
|
||||
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
|
||||
import { importSvg } from "design-system-old";
|
||||
import { CANVAS_ART_BOARD } from "constants/componentClassNameConstants";
|
||||
|
||||
|
|
@ -155,14 +153,12 @@ const mapDispatchToProps = (dispatch: any) => {
|
|||
position,
|
||||
},
|
||||
});
|
||||
dispatch(selectWidgetInitAction(SelectionRequestType.One, [widgetId]));
|
||||
},
|
||||
hideFilterPane: (widgetId: string) => {
|
||||
dispatch({
|
||||
type: ReduxActionTypes.HIDE_TABLE_FILTER_PANE,
|
||||
payload: { widgetId },
|
||||
});
|
||||
dispatch(selectWidgetInitAction(SelectionRequestType.One, [widgetId]));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user