feat: disallow collapsing sidebar when signposting is enabled (#24938)

This commit is contained in:
akash-codemonk 2023-07-11 17:19:27 +05:30 committed by GitHub
parent 3652e31f90
commit 3cedd9251f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 200 additions and 69 deletions

View File

@ -4,6 +4,7 @@ import {
locators,
draggableWidgets,
installer,
homePage,
} from "../../../../support/Objects/ObjectsCore";
const ExplorerMenu = {
@ -98,6 +99,7 @@ describe("Entity explorer tests related to pinning and unpinning", function () {
});
cy.get(locators._canvas).trigger("mousemove", 500, 400);
agHelper.AssertElementVisible(entityExplorer._entityExplorer);
entityExplorer.PinUnpinEntityExplorer(false);
},
);
@ -122,6 +124,21 @@ describe("Entity explorer tests related to pinning and unpinning", function () {
});
cy.get(locators._canvas).trigger("mousemove", 500, 400);
agHelper.AssertElementVisible(entityExplorer._entityExplorer);
entityExplorer.PinUnpinEntityExplorer(false);
},
);
it("5. Explorer should be visible by default on a new application", function () {
agHelper.AssertElementVisible(entityExplorer._entityExplorer);
entityExplorer.PinUnpinEntityExplorer(true);
agHelper.GetElement(locators._canvas).trigger("mousemove", 500, 100, {
force: true,
});
agHelper
.GetElement(entityExplorer._entityExplorer)
.should("not.be.visible");
homePage.NavigateToHome();
homePage.CreateNewApplication();
agHelper.AssertElementVisible(entityExplorer._entityExplorer);
});
});

View File

@ -0,0 +1,36 @@
import React from "react";
import EditorHeader from "./EditorHeader";
import { render, screen } from "test/testUtils";
import type { AppState } from "@appsmith/reducers";
function renderComponent(initialState?: Partial<AppState>) {
render(<EditorHeader />, {
initialState,
});
}
describe("EditorHeader", () => {
it("Show sidebar nav button by default", () => {
renderComponent();
const wrapper = screen.queryAllByTestId("sidebar-nav-button");
expect(wrapper.length).toBe(1);
});
it("sidebar nav button should be hidden when signposting is enabled", () => {
const initialState: any = {
entities: {
pageList: {
applicationId: "1",
},
},
ui: {
onBoarding: {
firstTimeUserOnboardingApplicationIds: ["1"],
},
},
};
renderComponent(initialState);
const wrapper = screen.queryAllByTestId("sidebar-nav-button");
expect(wrapper.length).toBe(0);
});
});

View File

@ -1,7 +1,6 @@
import React, { useCallback, useEffect, useState, lazy, Suspense } from "react";
import styled, { ThemeProvider } from "styled-components";
import classNames from "classnames";
import type { ApplicationPayload } from "@appsmith/constants/ReduxActionConstants";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import { APPLICATIONS_URL } from "constants/routes";
import AppInviteUsersForm from "pages/workspace/AppInviteUsersForm";
@ -19,6 +18,7 @@ import {
getAllUsers,
getCurrentWorkspaceId,
} from "@appsmith/selectors/workspaceSelectors";
import type { ConnectedProps } from "react-redux";
import { connect, useDispatch, useSelector } from "react-redux";
import DeployLinkButtonDialog from "components/designSystems/appsmith/header/DeployLinkButton";
import { updateApplication } from "@appsmith/actions/applicationActions";
@ -29,7 +29,6 @@ import {
} from "@appsmith/selectors/applicationSelectors";
import EditorAppName from "./EditorAppName";
import { getCurrentUser } from "selectors/usersSelectors";
import type { User } from "constants/userConstants";
import {
EditInteractionKind,
SavingState,
@ -59,7 +58,6 @@ import { EditorSaveIndicator } from "./EditorSaveIndicator";
import { retryPromise } from "utils/AppsmithUtils";
import { fetchUsersForWorkspace } from "@appsmith/actions/workspaceActions";
import type { WorkspaceUser } from "@appsmith/constants/workspaceConstants";
import { getIsGitConnected } from "selectors/gitSyncSelectors";
import {
@ -92,6 +90,7 @@ import EmbedSnippetForm from "@appsmith/pages/Applications/EmbedSnippetTab";
import { getAppsmithConfigs } from "@appsmith/configs";
import { getIsAppSettingsPaneWithNavigationTabOpen } from "selectors/appSettingsPaneSelectors";
import type { NavigationSetting } from "constants/AppConstants";
import { getIsFirstTimeUserOnboardingEnabled } from "selectors/onboardingSelectors";
const { cloudHosting } = getAppsmithConfigs();
@ -195,23 +194,6 @@ const SidebarNavButton = styled(Button)`
}
`;
type EditorHeaderProps = {
pageSaveError?: boolean;
pageName?: string;
pageId: string;
isPublishing: boolean;
publishedTime?: string;
workspaceId: string;
applicationId?: string;
currentApplication?: ApplicationPayload;
isSaving: boolean;
publishApplication: (appId: string) => void;
lastUpdatedTime?: number;
inOnboarding: boolean;
sharedUserList: WorkspaceUser[];
currentUser?: User;
};
const GlobalSearch = lazy(() => {
return retryPromise(
() =>
@ -223,7 +205,10 @@ const GlobalSearch = lazy(() => {
const theme = getTheme(ThemeMode.LIGHT);
export function EditorHeader(props: EditorHeaderProps) {
// Seperating redux props types from props being passed to the component from a parent
type PropsFromRedux = ConnectedProps<typeof connector>;
export function EditorHeader(props: PropsFromRedux) {
const {
applicationId,
currentApplication,
@ -241,6 +226,7 @@ export function EditorHeader(props: EditorHeaderProps) {
const isErroredSavingName = useSelector(getIsErroredSavingAppName);
const applicationList = useSelector(getApplicationList);
const isPreviewMode = useSelector(previewModeSelector);
const signpostingEnabled = useSelector(getIsFirstTimeUserOnboardingEnabled);
const deployLink = useHref(viewerURL, { pageId });
const isAppSettingsPaneWithNavigationTabOpen = useSelector(
getIsAppSettingsPaneWithNavigationTabOpen,
@ -341,59 +327,68 @@ export function EditorHeader(props: EditorHeaderProps) {
data-testid="t--appsmith-editor-header"
>
<HeaderSection className="space-x-2">
<Tooltip
content={
<div className="flex items-center justify-between">
<span>
{!pinned
? createMessage(LOCK_ENTITY_EXPLORER_MESSAGE)
: createMessage(CLOSE_ENTITY_EXPLORER_MESSAGE)}
</span>
<span className="ml-4">{modText()} /</span>
</div>
}
placement="bottomLeft"
>
<SidebarNavButton
className={classNames({
"transition-all transform duration-400": true,
"-translate-x-full opacity-0": isPreviewingApp,
"translate-x-0 opacity-100": !isPreviewingApp,
})}
kind="tertiary"
onClick={onPin}
size="md"
{!signpostingEnabled && (
<Tooltip
content={
<div className="flex items-center justify-between">
<span>
{!pinned
? createMessage(LOCK_ENTITY_EXPLORER_MESSAGE)
: createMessage(CLOSE_ENTITY_EXPLORER_MESSAGE)}
</span>
<span className="ml-4">{modText()} /</span>
</div>
}
placement="bottomLeft"
>
<div
className="t--pin-entity-explorer group relative"
onMouseEnter={onMenuHover}
<SidebarNavButton
className={classNames({
"transition-all transform duration-400": true,
"-translate-x-full opacity-0": isPreviewingApp,
"translate-x-0 opacity-100": !isPreviewingApp,
})}
data-testid="sidebar-nav-button"
kind="tertiary"
onClick={onPin}
size="md"
>
<Icon
className="absolute transition-opacity group-hover:opacity-0"
name="hamburger"
size="md"
/>
{pinned && (
<div
className="t--pin-entity-explorer group relative"
onMouseEnter={onMenuHover}
>
<Icon
className="absolute transition-opacity opacity-0 group-hover:opacity-100"
name="menu-fold"
onClick={onPin}
className="absolute transition-opacity group-hover:opacity-0"
name="hamburger"
size="md"
/>
)}
{!pinned && (
<Icon
className="absolute transition-opacity opacity-0 group-hover:opacity-100"
name="menu-unfold"
onClick={onPin}
size="md"
/>
)}
</div>
</SidebarNavButton>
</Tooltip>
{pinned && (
<Icon
className="absolute transition-opacity opacity-0 group-hover:opacity-100"
name="menu-fold"
onClick={onPin}
size="md"
/>
)}
{!pinned && (
<Icon
className="absolute transition-opacity opacity-0 group-hover:opacity-100"
name="menu-unfold"
onClick={onPin}
size="md"
/>
)}
</div>
</SidebarNavButton>
</Tooltip>
)}
<Tooltip content={createMessage(LOGO_TOOLTIP)} placement="bottomLeft">
<AppsmithLink to={APPLICATIONS_URL}>
<AppsmithLink
className={classNames({
"ml-2": signpostingEnabled,
})}
to={APPLICATIONS_URL}
>
<img
alt="Appsmith logo"
className="t--appsmith-logo"
@ -575,4 +570,5 @@ EditorHeader.whyDidYouRender = {
logOnDifferentValues: false,
};
export default connect(mapStateToProps, mapDispatchToProps)(EditorHeader);
const connector = connect(mapStateToProps, mapDispatchToProps);
export default connector(EditorHeader);

View File

@ -36,6 +36,7 @@ import { SelectionRequestType } from "sagas/WidgetSelectUtils";
import * as widgetActions from "actions/widgetActions";
import * as uiSelectors from "selectors/ui";
import { NavigationMethod } from "../../../utils/history";
import { setExplorerPinnedAction } from "actions/explorerActions";
jest.mock("constants/routes", () => {
return {
@ -531,3 +532,74 @@ describe("cmd + s hotkey", () => {
});
});
});
describe("mod + / hotkey", () => {
it("Should dispatch pin/unpin explorer on mod + /", async () => {
const dispatchSpy = jest.spyOn(store, "dispatch");
const component = render(
<GlobalHotKeys
getMousePosition={() => {
return { x: 0, y: 0 };
}}
>
<div />
</GlobalHotKeys>,
);
dispatchSpy.mockClear();
dispatchTestKeyboardEventWithCode(
component.container,
"keydown",
"/",
191,
false,
true,
);
expect(dispatchSpy).toBeCalledTimes(2);
expect(dispatchSpy).toHaveBeenNthCalledWith(
1,
setExplorerPinnedAction(false),
);
});
it("Shouldn't dispatch pin/unpin explorer on mod + / when signposting is enabled", async () => {
const dispatchSpy = jest.spyOn(store, "dispatch");
const state: any = {
entities: {
pageList: {
applicationId: "1",
},
},
ui: {
onBoarding: {
firstTimeUserOnboardingApplicationIds: ["1"],
},
},
};
const component = render(
<GlobalHotKeys
getMousePosition={() => {
return { x: 0, y: 0 };
}}
>
<div />
</GlobalHotKeys>,
{
initialState: state,
},
);
dispatchSpy.mockClear();
dispatchTestKeyboardEventWithCode(
component.container,
"keydown",
"/",
191,
false,
true,
);
expect(dispatchSpy).toBeCalledTimes(0);
});
});

View File

@ -47,6 +47,7 @@ import { toggleInstaller } from "actions/JSLibraryActions";
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
import { toast } from "design-system";
import { showDebuggerFlag } from "selectors/debuggerSelectors";
import { getIsFirstTimeUserOnboardingEnabled } from "selectors/onboardingSelectors";
type Props = {
copySelectedWidget: () => void;
@ -72,6 +73,7 @@ type Props = {
isPreviewMode: boolean;
setPreviewModeAction: (shouldSet: boolean) => void;
isExplorerPinned: boolean;
isSignpostingEnabled: boolean;
setExplorerPinnedAction: (shouldPinned: boolean) => void;
showCommitModal: () => void;
getMousePosition: () => { x: number; y: number };
@ -332,6 +334,7 @@ class GlobalHotKeys extends React.Component<Props> {
/>
<Hotkey
combo="mod + /"
disabled={this.props.isSignpostingEnabled}
global
label="Pin/Unpin Entity Explorer"
onKeyDown={() => {
@ -363,6 +366,7 @@ const mapStateToProps = (state: AppState) => ({
appMode: getAppMode(state),
isPreviewMode: previewModeSelector(state),
isExplorerPinned: getExplorerPinned(state),
isSignpostingEnabled: getIsFirstTimeUserOnboardingEnabled(state),
});
const mapDispatchToProps = (dispatch: any) => {

View File

@ -45,6 +45,10 @@ import { getSearchQuery, updateSlugNamesInURL } from "utils/helpers";
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions";
import { safeCrashAppRequest } from "../actions/errorActions";
import { resetSnipingMode } from "actions/propertyPaneActions";
import {
setExplorerActiveAction,
setExplorerPinnedAction,
} from "actions/explorerActions";
import {
isEditorPath,
isViewerPath,
@ -132,6 +136,8 @@ function* resetEditorSaga() {
// previously
yield put(setPreviewModeAction(false));
yield put(resetSnipingMode());
yield put(setExplorerActiveAction(true));
yield put(setExplorerPinnedAction(true));
yield put(resetEditorSuccess());
}