chore: Refactoring editor app name component for modules (#28055)

## Description

Refactoring editor app name component for modules

#### PR fixes following issue(s)
Fixes [#26163](https://github.com/appsmithorg/appsmith/issues/26163)

#### Type of change
- Chore (housekeeping or task changes that don't impact user perception)

## Testing

#### How Has This Been Tested?
- [x] Manual
- [ ] JUnit
- [ ] Jest
- [x] Cypress

## Checklist:
#### Dev activity
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
This commit is contained in:
Ankita Kinger 2023-10-16 14:37:39 +05:30 committed by GitHub
parent 05e729902f
commit b9bc00e1da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 115 additions and 128 deletions

View File

@ -193,6 +193,7 @@ app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/Appsmith
app/client/src/pages/Settings/**/* @ankitakinger
app/client/src/pages/workspace/settings.tsx @ankitakinger
app/client/src/pages/workspace/AppInviteUsersForm.tsx @ankitakinger
app/client/src/pages/workspace/WorkspaceInviteUsersForm.tsx @ankitakinger
app/client/src/components/editorComponents/form/FormDialogComponent.tsx @ankitakinger
app/client/src/ce/pages/AdminSettings/**/* @ankitakinger
app/client/src/ee/pages/AdminSettings/**/* @ankitakinger
@ -202,8 +203,8 @@ app/client/src/ce/pages/Applications/PrivateEmbedSettings.tsx @ankitakinger
app/client/src/ee/pages/Applications/PrivateEmbedSettings.tsx @ankitakinger
app/client/src/ce/pages/workspace/Members.tsx @ankitakinger
app/client/src/ee/pages/workspace/Members.tsx @ankitakinger
app/client/src/ce/pages/workspace/WorkspaceInviteUsersForm.tsx @ankitakinger
app/client/src/ee/pages/workspace/WorkspaceInviteUsersForm.tsx @ankitakinger
app/client/src/ce/pages/workspace/InviteUsersForm.tsx @ankitakinger
app/client/src/ee/pages/workspace/InviteUsersForm.tsx @ankitakinger
app/client/src/ce/pages/Upgrade/**/* @ankitakinger
app/client/src/ee/pages/Auditlogs/**/* @ankitakinger
app/client/cypress/e2e/Regression/Enterprise/**/* @ankitakinger

View File

@ -71,7 +71,7 @@ describe("Admin settings page", function () {
it(
"airgap",
"4. Should test that settings page tab redirects and google maps doesn't exist - airgap",
"4. Should test that settings page tab redirects and developer settings doesn't exist - airgap",
() => {
cy.visit("/applications", { timeout: 60000 });
if (!Cypress.env("AIRGAPPED")) {

View File

@ -252,11 +252,10 @@ export class CommonLocators {
_selectionCanvas = (canvasId: string) => `#div-selection-${canvasId}`;
_sqlKeyword = ".cm-m-sql.cm-keyword";
_appLeveltooltip = (toolTip: string) => `span:contains('${toolTip}')`;
_appEditMenu = "[data-testid='t--application-edit-menu']";
_appEditMenuBtn = "[data-testid='t--application-edit-menu-cta']";
_appEditMenuSettings = "[data-testid='t--application-edit-menu-settings']";
_appEditExportSettings =
"[data-testid='t--application-edit-menu-export-application']";
_appEditMenu = "[data-testid='t--editor-menu']";
_appEditMenuBtn = "[data-testid='t--editor-menu-cta']";
_appEditMenuSettings = "[data-testid='t--editor-menu-settings']";
_appEditExportSettings = "[data-testid='t--editor-menu-export-application']";
_appThemeSettings = "#t--theme-settings-header";
_appChangeThemeBtn = ".t--change-theme-btn";
_appThemeCard = ".t--theme-card";

View File

@ -11,6 +11,9 @@ import pageListReducer from "reducers/entityReducers/pageListReducer";
import pluginsReducer from "reducers/entityReducers/pluginsReducer";
import autoHeightLayoutTreeReducer from "reducers/entityReducers/autoHeightReducers/autoHeightLayoutTreeReducer";
import canvasLevelsReducer from "reducers/entityReducers/autoHeightReducers/canvasLevelsReducer";
/* Reducers which are integrated into the core system when registering a pluggable module
or done so by a module that is designed to be eventually pluggable */
import widgetPositionsReducer from "layoutSystems/anvil/integrations/reducers/widgetPositionsReducer";
export const entityReducerObject = {

View File

@ -97,10 +97,10 @@ const IconScrollWrapper = styled.div`
}
`;
type ModifiedMenuItemProps = MenuItemProps & {
export interface ModifiedMenuItemProps extends MenuItemProps {
key?: string;
"data-testid"?: string;
};
}
const ContextMenuTrigger = styled(Button)<{ isHidden?: boolean }>`
${(props) => props.isHidden && "opacity: 0; visibility: hidden;"}

View File

@ -6,7 +6,9 @@ import {
getApplicationLastDeployedAt,
getCurrentApplicationId,
getCurrentPageId,
getIsPageSaving,
getIsPublishingApplication,
getPageSavingError,
previewModeSelector,
} from "selectors/editorSelectors";
import {
@ -25,7 +27,7 @@ import {
getIsErroredSavingAppName,
getCurrentApplication,
} from "@appsmith/selectors/applicationSelectors";
import EditorAppName from "./EditorAppName";
import EditorName from "./EditorName";
import { EditInteractionKind, SavingState } from "design-system-old";
import {
Button,
@ -90,6 +92,7 @@ import { Omnibar } from "./commons/Omnibar";
import { EditorShareButton } from "./EditorShareButton";
import { HelperBarInHeader } from "./HelpBarInHeader";
import { AppsmithLink } from "./AppsmithLink";
import { GetNavigationMenuData } from "./EditorName/NavigationMenuData";
const { cloudHosting } = getAppsmithConfigs();
@ -111,6 +114,8 @@ export function EditorHeader() {
const isPublishing = useSelector(getIsPublishingApplication);
const pageId = useSelector(getCurrentPageId) as string;
const featureFlags = useSelector(selectFeatureFlags);
const isSaving = useSelector(getIsPageSaving);
const pageSaveError = useSelector(getPageSavingError);
const deployLink = useHref(viewerURL, { pageId });
const isAppSettingsPaneWithNavigationTabOpen = useSelector(
@ -265,7 +270,7 @@ export function EditorHeader() {
placement="bottom"
>
<div>
<EditorAppName
<EditorName
applicationId={applicationId}
className="t--application-name editable-application-name max-w-48"
defaultSavingState={
@ -273,9 +278,11 @@ export function EditorHeader() {
}
defaultValue={currentApplication?.name || ""}
editInteractionKind={EditInteractionKind.SINGLE}
editorName="Application"
fill
getNavigationMenu={GetNavigationMenuData}
isError={isErroredSavingName}
isNewApp={
isNewEditor={
applicationList.filter((el) => el.id === applicationId)
.length > 0
}
@ -290,7 +297,7 @@ export function EditorHeader() {
/>
</div>
</Tooltip>
<EditorSaveIndicator />
<EditorSaveIndicator isSaving={isSaving} saveError={pageSaveError} />
</HeaderSection>
<HelperBarInHeader />

View File

@ -25,10 +25,10 @@ import { toast } from "design-system";
import type { ThemeProp } from "WidgetProvider/constants";
import { DISCORD_URL, DOCS_BASE_URL } from "constants/ThirdPartyConstants";
type NavigationMenuDataProps = ThemeProp & {
export interface NavigationMenuDataProps extends ThemeProp {
editMode: typeof noop;
setForkApplicationModalOpen: React.Dispatch<React.SetStateAction<boolean>>;
};
}
export const GetNavigationMenuData = ({
editMode,

View File

@ -96,7 +96,7 @@ export function NavigationMenuItem({
case MenuTypes.MENU:
return (
<MenuItem
data-testid={`t--application-edit-menu-${kebabCase(text)}`}
data-testid={`t--editor-menu-${kebabCase(text)}`}
onClick={(e) => handleClick(e, menuItemData)}
>
{menuItemData.text}
@ -104,7 +104,7 @@ export function NavigationMenuItem({
);
case MenuTypes.PARENT:
return (
<MenuSub data-testid={`t--application-edit-menu-${kebabCase(text)}`}>
<MenuSub data-testid={`t--editor-menu-${kebabCase(text)}`}>
<MenuSubTrigger>{menuItemData.text}</MenuSubTrigger>
<MenuSubContent width="214px">
{menuItemData?.children?.map((subitem, idx) => (
@ -126,7 +126,7 @@ export function NavigationMenuItem({
return (
<ReconfirmMenuItem
className="error-menuitem"
data-testid={`t--application-edit-menu-${kebabCase(text)}`}
data-testid={`t--editor-menu-${kebabCase(text)}`}
onClick={(e) => handleReconfirmClick(e, menuItemData)}
>
{confirm.text}

View File

@ -0,0 +1,37 @@
import styled from "styled-components";
import { Classes } from "@blueprintjs/core";
import { getTypographyByKey } from "design-system-old";
import { Icon } from "design-system";
export const Container = styled.div`
display: flex;
cursor: pointer;
&:hover {
background-color: var(--ads-v2-color-bg-subtle);
}
& .${Classes.EDITABLE_TEXT} {
height: ${(props) => props.theme.smallHeaderHeight} !important;
display: block;
cursor: pointer;
}
&&&& .${Classes.EDITABLE_TEXT}, &&&& .${Classes.EDITABLE_TEXT_EDITING} {
padding: 0;
width: 100%;
}
&&&& .${Classes.EDITABLE_TEXT_CONTENT}, &&&& .${Classes.EDITABLE_TEXT_INPUT} {
display: block;
${getTypographyByKey("h5")};
line-height: ${(props) => props.theme.smallHeaderHeight} !important;
padding: 0 ${(props) => props.theme.spaces[2]}px;
height: ${(props) => props.theme.smallHeaderHeight} !important;
}
&&&& .${Classes.EDITABLE_TEXT_INPUT} {
margin-right: 20px;
}
`;
export const StyledIcon = styled(Icon)`
height: 100%;
padding-right: ${(props) => props.theme.spaces[2]}px;
align-self: center;
`;

View File

@ -1,22 +1,25 @@
import React, { useState, useCallback } from "react";
import styled, { useTheme } from "styled-components";
import { Classes } from "@blueprintjs/core";
import { useTheme } from "styled-components";
import type { noop } from "lodash";
import type {
CommonComponentProps,
EditInteractionKind,
} from "design-system-old";
import { getTypographyByKey, SavingState } from "design-system-old";
import EditableAppName from "./EditableAppName";
import { GetNavigationMenuData } from "./NavigationMenuData";
import { SavingState } from "design-system-old";
import EditableName from "./EditableName";
import { NavigationMenu } from "./NavigationMenu";
import type { Theme } from "constants/DefaultTheme";
import { Icon, Menu, toast, MenuTrigger } from "design-system";
import { Menu, toast, MenuTrigger } from "design-system";
import ForkApplicationModal from "pages/Applications/ForkApplicationModal";
import { Container, StyledIcon } from "./components";
import { useSelector } from "react-redux";
import { getCurrentApplicationId } from "selectors/editorSelectors";
import type { MenuItemData } from "./NavigationMenuItem";
import type { NavigationMenuDataProps } from "./NavigationMenuData";
type EditorAppNameProps = CommonComponentProps & {
applicationId: string | undefined;
type EditorNameProps = CommonComponentProps & {
applicationId?: string | undefined;
defaultValue: string;
placeholder?: string;
editInteractionKind: EditInteractionKind;
@ -27,56 +30,30 @@ type EditorAppNameProps = CommonComponentProps & {
hideEditIcon?: boolean;
fill?: boolean;
isError?: boolean;
isNewApp: boolean;
isNewEditor: boolean;
isPopoverOpen: boolean;
setIsPopoverOpen: typeof noop;
editorName: string;
getNavigationMenu: ({
editMode,
setForkApplicationModalOpen,
}: NavigationMenuDataProps) => MenuItemData[];
};
const Container = styled.div`
display: flex;
cursor: pointer;
&:hover {
background-color: var(--ads-v2-color-bg-subtle);
}
& .${Classes.EDITABLE_TEXT} {
height: ${(props) => props.theme.smallHeaderHeight} !important;
display: block;
cursor: pointer;
}
&&&& .${Classes.EDITABLE_TEXT}, &&&& .${Classes.EDITABLE_TEXT_EDITING} {
padding: 0;
width: 100%;
}
&&&& .${Classes.EDITABLE_TEXT_CONTENT}, &&&& .${Classes.EDITABLE_TEXT_INPUT} {
display: block;
${getTypographyByKey("h5")};
line-height: ${(props) => props.theme.smallHeaderHeight} !important;
padding: 0 ${(props) => props.theme.spaces[2]}px;
height: ${(props) => props.theme.smallHeaderHeight} !important;
}
&&&& .${Classes.EDITABLE_TEXT_INPUT} {
margin-right: 20px;
}
`;
const StyledIcon = styled(Icon)`
height: 100%;
padding-right: ${(props) => props.theme.spaces[2]}px;
align-self: center;
`;
export function EditorAppName(props: EditorAppNameProps) {
export function EditorName(props: EditorNameProps) {
const {
defaultSavingState,
defaultValue,
isNewApp,
editorName,
getNavigationMenu,
isNewEditor,
isPopoverOpen,
setIsPopoverOpen,
} = props;
const theme = useTheme() as Theme;
const [isEditingDefault, setIsEditingDefault] = useState(isNewApp);
const [isEditingDefault, setIsEditingDefault] = useState(isNewEditor);
const [isEditing, setIsEditing] = useState(!!isEditingDefault);
const [isInvalid, setIsInvalid] = useState<string | boolean>(false);
const [savingState, setSavingState] = useState<SavingState>(
@ -84,6 +61,7 @@ export function EditorAppName(props: EditorAppNameProps) {
);
const [isForkApplicationModalopen, setForkApplicationModalOpen] =
useState(false);
const currentAppId = useSelector(getCurrentApplicationId);
const onBlur = (value: string) => {
if (props.onBlur) props.onBlur(value);
@ -92,7 +70,7 @@ export function EditorAppName(props: EditorAppNameProps) {
const inputValidation = (value: string) => {
if (value.trim() === "") {
toast.show("Application name can't be empty", {
toast.show(`${editorName} name can't be empty`, {
kind: "error",
});
}
@ -110,7 +88,7 @@ export function EditorAppName(props: EditorAppNameProps) {
[inputValidation, defaultValue],
);
const handleAppNameClick = useCallback(() => {
const handleNameClick = useCallback(() => {
if (!isEditing) {
setIsPopoverOpen((isOpen: boolean) => {
return !isOpen;
@ -124,7 +102,7 @@ export function EditorAppName(props: EditorAppNameProps) {
}
}, []);
const NavigationMenuData = GetNavigationMenuData({
const NavigationMenuData = getNavigationMenu({
editMode,
theme,
setForkApplicationModalOpen,
@ -133,16 +111,13 @@ export function EditorAppName(props: EditorAppNameProps) {
return defaultValue !== "" ? (
<>
<Menu
className="t--application-edit-menu"
className="t--editor-menu"
onOpenChange={handleOnInteraction}
open={isPopoverOpen}
>
<MenuTrigger disabled={isEditing}>
<Container
data-testid="t--application-edit-menu-cta"
onClick={handleAppNameClick}
>
<EditableAppName
<Container data-testid="t--editor-menu-cta" onClick={handleNameClick}>
<EditableName
className={props.className}
defaultSavingState={defaultSavingState}
defaultValue={defaultValue}
@ -174,16 +149,18 @@ export function EditorAppName(props: EditorAppNameProps) {
setIsPopoverOpen={setIsPopoverOpen}
/>
</Menu>
<ForkApplicationModal
applicationId={props.applicationId || ""}
handleClose={() => {
setForkApplicationModalOpen(false);
}}
isInEditMode
isModalOpen={isForkApplicationModalopen}
/>
{props.applicationId || currentAppId ? (
<ForkApplicationModal
applicationId={props.applicationId || currentAppId}
handleClose={() => {
setForkApplicationModalOpen(false);
}}
isInEditMode
isModalOpen={isForkApplicationModalopen}
/>
) : null}
</>
) : null;
}
export default EditorAppName;
export default EditorName;

View File

@ -1,9 +1,6 @@
import React from "react";
import { useSelector } from "react-redux";
import styled from "styled-components";
import { TextType, Text } from "design-system-old";
import { getIsPageSaving, getPageSavingError } from "selectors/editorSelectors";
import { Colors } from "constants/Colors";
import { createMessage, EDITOR_HEADER } from "@appsmith/constants/messages";
import { Icon, Spinner } from "design-system";
@ -13,17 +10,20 @@ const SaveStatusContainer = styled.div`
display: flex;
`;
export function EditorSaveIndicator() {
const isSaving = useSelector(getIsPageSaving);
const pageSaveError = useSelector(getPageSavingError);
export function EditorSaveIndicator({
isSaving,
saveError,
}: {
isSaving: boolean;
saveError: boolean;
}) {
let saveStatusIcon: React.ReactNode;
let saveStatusText = "";
if (isSaving) {
saveStatusIcon = <Spinner className="t--save-status-is-saving" />;
saveStatusText = createMessage(EDITOR_HEADER.saving);
} else {
if (pageSaveError) {
if (saveError) {
saveStatusIcon = (
<Icon className={"t--save-status-error"} name="cloud-off-line" />
);
@ -31,7 +31,7 @@ export function EditorSaveIndicator() {
}
}
if (!pageSaveError && !isSaving) return null;
if (!saveError && !isSaving) return null;
return (
<SaveStatusContainer className={"t--save-status-container gap-x-1"}>

View File

@ -1,37 +0,0 @@
import { combineReducers } from "redux";
import appReducer from "./appReducer";
import canvasWidgetsReducer from "./canvasWidgetsReducer";
import canvasWidgetsStructureReducer from "./canvasWidgetsStructureReducer";
import metaWidgetsReducer from "./metaWidgetsReducer";
import datasourceReducer from "./datasourceReducer";
import jsActionsReducer from "./jsActionsReducer";
import jsExecutionsReducer from "./jsExecutionsReducer";
import metaReducer from "./metaReducer";
import pageListReducer from "./pageListReducer";
import pluginsReducer from "reducers/entityReducers/pluginsReducer";
import autoHeightLayoutTreeReducer from "./autoHeightReducers/autoHeightLayoutTreeReducer";
import canvasLevelsReducer from "./autoHeightReducers/canvasLevelsReducer";
import actionsReducer from "@appsmith/reducers/entityReducers/actionsReducer";
/* Reducers which are integrated into the core system when registering a pluggable module
or done so by a module that is designed to be eventually pluggable */
import widgetPositionsReducer from "layoutSystems/anvil/integrations/reducers/widgetPositionsReducer";
const entityReducer = combineReducers({
canvasWidgets: canvasWidgetsReducer,
canvasWidgetsStructure: canvasWidgetsStructureReducer,
metaWidgets: metaWidgetsReducer,
actions: actionsReducer,
datasources: datasourceReducer,
pageList: pageListReducer,
jsExecutions: jsExecutionsReducer,
plugins: pluginsReducer,
meta: metaReducer,
app: appReducer,
jsActions: jsActionsReducer,
autoHeightLayoutTree: autoHeightLayoutTreeReducer,
canvasLevels: canvasLevelsReducer,
widgetPositions: widgetPositionsReducer,
});
export default entityReducer;