chore: Move IDE header to ADS/Templates (#37406)
## Description Extracting out our IDE Header component into ADS for better usability. ADS Templates are shallow components built using opinionated ADS which provide "slots" to place other business logic components inside it. It reduces the work of using ADS by providing pre built UI components Also creating the EntityExplorer folder and created ListWithHeader as a component. Will keep updating this ADS template as we work on Entity Explorer modularization Fixes #37607 ## 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/11971098012> > Commit: e28c56e76aa47d727a7e9df0f46948e0785b49bf > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11971098012&attempt=2" target="_blank">Cypress dashboard</a>. > Tags: `@tag.All` > Spec: > <hr>Fri, 22 Nov 2024 13:13:39 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 ## Release Notes - **New Features** - Introduced the `IDEHeader` component with subcomponents (`Left`, `Center`, `Right`) for improved header layout. - Added `IDEHeaderSwitcher` for enhanced navigation within the header. - New styled components `ListItemContainer`, `ListHeaderContainer`, and `ListWithHeader` for the entity explorer interface. - Added new constants `IDE_HEADER_HEIGHT` and `LOGO_WIDTH` for standardized dimensions in the header layout. - **Improvements** - Updated header titles and constants for better clarity in the user interface. - Enhanced layout and styling of the `PagesSection` component for improved user experience. - Consolidated import statements for better organization across various components. - Removed deprecated components and tests to streamline the codebase. - **Documentation** - Added documentation for the `IDEHeader` component, including stories for visual representation. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
5cfe143b86
commit
8cb0beeecc
|
|
@ -0,0 +1,38 @@
|
|||
import React from "react";
|
||||
import { ListHeaderContainer } from "./styles";
|
||||
import { Text } from "../../Text";
|
||||
import { Flex } from "../../Flex";
|
||||
|
||||
interface Props {
|
||||
headerText: string;
|
||||
headerControls?: React.ReactNode;
|
||||
maxHeight?: string;
|
||||
headerClassName?: string;
|
||||
children: React.ReactNode | React.ReactNode[];
|
||||
}
|
||||
|
||||
export const ListWithHeader = (props: Props) => {
|
||||
return (
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
maxHeight={props.maxHeight}
|
||||
overflow="hidden"
|
||||
>
|
||||
<ListHeaderContainer className={props.headerClassName}>
|
||||
<Text kind="heading-xs">{props.headerText}</Text>
|
||||
{props.headerControls}
|
||||
</ListHeaderContainer>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
flex="1"
|
||||
flexDirection="column"
|
||||
overflow="auto"
|
||||
px="spaces-2"
|
||||
width="100%"
|
||||
>
|
||||
{props.children}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export { ListItemContainer, ListHeaderContainer } from "./styles";
|
||||
export { ListWithHeader } from "./ListWithHeader";
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
export const ListItemContainer = styled.div`
|
||||
width: 100%;
|
||||
|
||||
& .t--entity-item {
|
||||
grid-template-columns: 0 auto 1fr auto auto auto auto auto;
|
||||
height: 32px;
|
||||
|
||||
& .t--entity-name {
|
||||
padding-left: var(--ads-v2-spaces-3);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const ListHeaderContainer = styled.div`
|
||||
padding: var(--ads-v2-spaces-3);
|
||||
padding-right: var(--ads-v2-spaces-2);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
|
||||
span {
|
||||
line-height: 20px;
|
||||
}
|
||||
`;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import styled from "styled-components";
|
||||
import { PopoverContent } from "../../../Popover";
|
||||
|
||||
export const SwitchTrigger = styled.div<{ active: boolean }>`
|
||||
display: flex;
|
||||
border-radius: var(--ads-v2-border-radius);
|
||||
background-color: ${(props) =>
|
||||
props.active ? `var(--ads-v2-color-bg-subtle)` : "unset"};
|
||||
cursor: pointer;
|
||||
padding: var(--ads-v2-spaces-2);
|
||||
|
||||
:hover {
|
||||
background-color: var(--ads-v2-color-bg-subtle);
|
||||
}
|
||||
`;
|
||||
|
||||
export const ContentContainer = styled(PopoverContent)`
|
||||
padding: 0;
|
||||
padding-bottom: 0.25em;
|
||||
`;
|
||||
|
|
@ -1,67 +1,67 @@
|
|||
import React from "react";
|
||||
import { render, fireEvent } from "@testing-library/react";
|
||||
import HeaderEditorSwitcher from "./HeaderEditorSwitcher";
|
||||
import { render, fireEvent, screen } from "@testing-library/react";
|
||||
import { IDEHeaderSwitcher } from "./IDEHeaderSwitcher";
|
||||
import "@testing-library/jest-dom";
|
||||
|
||||
describe("HeaderEditorSwitcher", () => {
|
||||
describe("HeaderSwitcher", () => {
|
||||
const mockOnClick = jest.fn();
|
||||
const mockSetActive = jest.fn();
|
||||
const defaultProps = {
|
||||
prefix: "Prefix",
|
||||
title: "Title",
|
||||
titleTestId: "titleTestId",
|
||||
active: false,
|
||||
onClick: mockOnClick,
|
||||
setActive: mockSetActive,
|
||||
children: <span>Test</span>,
|
||||
};
|
||||
|
||||
it("renders with correct props", () => {
|
||||
const { getByText } = render(<HeaderEditorSwitcher {...defaultProps} />);
|
||||
render(<IDEHeaderSwitcher {...defaultProps} />);
|
||||
|
||||
// eslint-disable-next-line testing-library/no-node-access
|
||||
const testIdElement = document.getElementsByClassName(
|
||||
defaultProps.titleTestId,
|
||||
);
|
||||
|
||||
expect(getByText("Prefix /")).toBeInTheDocument();
|
||||
expect(getByText(defaultProps.title)).toBeInTheDocument();
|
||||
expect(screen.getByText("Prefix /")).toBeInTheDocument();
|
||||
expect(screen.getByText(defaultProps.title)).toBeInTheDocument();
|
||||
expect(testIdElement).toBeDefined();
|
||||
});
|
||||
|
||||
it("renders active state correctly", () => {
|
||||
const { getByText } = render(
|
||||
<HeaderEditorSwitcher {...defaultProps} active />,
|
||||
);
|
||||
render(<IDEHeaderSwitcher {...defaultProps} active />);
|
||||
|
||||
expect(getByText("Prefix /")).toHaveStyle(
|
||||
expect(screen.getByText("Prefix /")).toHaveStyle(
|
||||
"background-color: var(--ads-v2-color-bg-subtle)",
|
||||
);
|
||||
});
|
||||
|
||||
it("calls onClick handler when clicked", () => {
|
||||
const { getByText } = render(<HeaderEditorSwitcher {...defaultProps} />);
|
||||
render(<IDEHeaderSwitcher {...defaultProps} />);
|
||||
|
||||
fireEvent.click(getByText("Title"));
|
||||
fireEvent.click(screen.getByText("Title"));
|
||||
|
||||
expect(mockOnClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("forwards ref correctly", () => {
|
||||
const ref = React.createRef();
|
||||
const ref = React.createRef<HTMLDivElement>();
|
||||
|
||||
render(<HeaderEditorSwitcher {...defaultProps} ref={ref} />);
|
||||
render(<IDEHeaderSwitcher {...defaultProps} ref={ref} />);
|
||||
expect(ref.current).toBeTruthy();
|
||||
});
|
||||
|
||||
it("does not crash when onClick is not provided", () => {
|
||||
const { getByText } = render(
|
||||
<HeaderEditorSwitcher {...defaultProps} onClick={undefined} />,
|
||||
);
|
||||
render(<IDEHeaderSwitcher {...defaultProps} onClick={undefined} />);
|
||||
|
||||
fireEvent.click(getByText("Title")); // Should not throw error
|
||||
fireEvent.click(screen.getByText("Title")); // Should not throw error
|
||||
});
|
||||
|
||||
it("does not show separator and applies different inactive color to icon", () => {
|
||||
const ref = React.createRef();
|
||||
const { container, getByTestId } = render(
|
||||
<HeaderEditorSwitcher
|
||||
const ref = React.createRef<HTMLDivElement>();
|
||||
const { container } = render(
|
||||
<IDEHeaderSwitcher
|
||||
{...defaultProps}
|
||||
data-testid="root-div"
|
||||
ref={ref}
|
||||
|
|
@ -69,9 +69,10 @@ describe("HeaderEditorSwitcher", () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line testing-library/no-container,testing-library/no-node-access
|
||||
const icon = container.querySelector(".remixicon-icon"); // Get chevron icon
|
||||
|
||||
expect(getByTestId("root-div")).toHaveTextContent("Prefix");
|
||||
expect(screen.getByTestId("root-div")).toHaveTextContent("Prefix");
|
||||
expect(icon).toHaveAttribute(
|
||||
"fill",
|
||||
"var(--ads-v2-colors-content-label-inactive-fg)",
|
||||
|
|
@ -83,16 +84,20 @@ describe("HeaderEditorSwitcher", () => {
|
|||
const className = "custom-class";
|
||||
|
||||
const { container } = render(
|
||||
<HeaderEditorSwitcher
|
||||
<IDEHeaderSwitcher
|
||||
active
|
||||
className={className}
|
||||
data-testid={testId} // Additional prop
|
||||
prefix="Prefix"
|
||||
setActive={mockSetActive}
|
||||
title="Title"
|
||||
titleTestId="titleTestId"
|
||||
/>,
|
||||
>
|
||||
<span>Test</span>
|
||||
</IDEHeaderSwitcher>,
|
||||
);
|
||||
|
||||
// eslint-disable-next-line testing-library/no-container,testing-library/no-node-access
|
||||
const firstDiv = container.querySelector("div"); // Get the first div element
|
||||
const classNames = firstDiv?.getAttribute("class")?.split(" ") || [];
|
||||
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import React, { type ForwardedRef, useCallback } from "react";
|
||||
import { Flex } from "../../../Flex";
|
||||
import { Icon } from "../../../Icon";
|
||||
import { Popover, PopoverTrigger } from "../../../Popover";
|
||||
import { Text } from "../../../Text";
|
||||
import * as Styled from "./HeaderSwitcher.styles";
|
||||
|
||||
interface Props {
|
||||
prefix: string;
|
||||
title?: string;
|
||||
titleTestId: string;
|
||||
active: boolean;
|
||||
setActive: (active: boolean) => void;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const IDEHeaderSwitcher = React.forwardRef(
|
||||
(props: Props, ref: ForwardedRef<HTMLDivElement>) => {
|
||||
const {
|
||||
active,
|
||||
children,
|
||||
className,
|
||||
onClick,
|
||||
prefix,
|
||||
setActive,
|
||||
title,
|
||||
titleTestId,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const separator = title ? " /" : "";
|
||||
|
||||
const closeSwitcher = useCallback(() => {
|
||||
return setActive(false);
|
||||
}, [setActive]);
|
||||
|
||||
return (
|
||||
<Popover onOpenChange={setActive} open={active}>
|
||||
<PopoverTrigger>
|
||||
<Styled.SwitchTrigger
|
||||
active={active}
|
||||
className={`flex align-center items-center justify-center ${className}`}
|
||||
data-testid={titleTestId}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
{...rest}
|
||||
>
|
||||
<Text
|
||||
color="var(--ads-v2-colors-content-label-inactive-fg)"
|
||||
kind="body-m"
|
||||
>
|
||||
{prefix + separator}
|
||||
</Text>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
className={titleTestId}
|
||||
data-active={active}
|
||||
gap="spaces-1"
|
||||
height="100%"
|
||||
justifyContent="center"
|
||||
paddingLeft="spaces-2"
|
||||
>
|
||||
<Text isBold kind="body-m">
|
||||
{title}
|
||||
</Text>
|
||||
<Icon
|
||||
color={
|
||||
title
|
||||
? undefined
|
||||
: "var(--ads-v2-colors-content-label-inactive-fg)"
|
||||
}
|
||||
name={active ? "arrow-up-s-line" : "arrow-down-s-line"}
|
||||
size="md"
|
||||
/>
|
||||
</Flex>
|
||||
</Styled.SwitchTrigger>
|
||||
</PopoverTrigger>
|
||||
<Styled.ContentContainer align="start" onEscapeKeyDown={closeSwitcher}>
|
||||
{children}
|
||||
</Styled.ContentContainer>
|
||||
</Popover>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
IDEHeaderSwitcher.displayName = "IDEHeaderSwitcher";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { IDEHeaderSwitcher } from "./IDEHeaderSwitcher";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export const IDE_HEADER_HEIGHT = 40;
|
||||
export const LOGO_WIDTH = 50;
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { Canvas, Meta } from "@storybook/blocks";
|
||||
import * as IDEHeaderStories from "./IDEHeader.stories";
|
||||
|
||||
<Meta of={IDEHeaderStories} />
|
||||
|
||||
# IDEHeader
|
||||
|
||||
IDEHeader sets the stage for the IDE experience. It is the topmost section of the IDE that contains the Appsmith logo, the app name, and the user profile.
|
||||
|
||||
<Canvas of={IDEHeaderStories.Default} />
|
||||
|
||||
## Anatomy
|
||||
|
||||
### Left Section options
|
||||
|
||||
The local title
|
||||
|
||||
#### Header Title
|
||||
|
||||
A title that is specific to the app state. It is displayed on the left side of the header.
|
||||
|
||||
<Canvas of={IDEHeaderStories.WithHeaderTitle} />
|
||||
|
||||
#### Header Dropdown
|
||||
|
||||
A dropdown that allows the user to switch between different pages.
|
||||
|
||||
<Canvas of={IDEHeaderStories.WithHeaderDropdown} />
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
import React from "react";
|
||||
import type { Meta } from "@storybook/react";
|
||||
import { IDEHeader } from "./IDEHeader";
|
||||
import { IDEHeaderTitle } from "./IDEHeaderTitle";
|
||||
import { IDEHeaderSwitcher } from "./HeaderSwitcher";
|
||||
import { noop } from "lodash";
|
||||
import { Icon } from "../../Icon";
|
||||
import { Button } from "../../Button";
|
||||
import { List } from "../../List";
|
||||
import { Flex } from "../../Flex";
|
||||
import { Text } from "../../Text";
|
||||
import { ListHeaderContainer } from "../EntityExplorer/styles";
|
||||
|
||||
const meta: Meta = {
|
||||
title: "ADS/Templates/IDEHeader",
|
||||
component: IDEHeader,
|
||||
parameters: {
|
||||
layout: "fullscreen",
|
||||
},
|
||||
decorators: [
|
||||
(Story: () => React.ReactNode) => (
|
||||
<div style={{ width: "100%" }}>{Story()}</div>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const Default = () => (
|
||||
<IDEHeader>
|
||||
<IDEHeader.Left logo={<Icon name="upload-cloud" size="md" />}>
|
||||
<span>Left Content</span>
|
||||
</IDEHeader.Left>
|
||||
<IDEHeader.Center>
|
||||
<span>Center Content</span>
|
||||
</IDEHeader.Center>
|
||||
<IDEHeader.Right>
|
||||
<span>Right Content</span>
|
||||
</IDEHeader.Right>
|
||||
</IDEHeader>
|
||||
);
|
||||
|
||||
export const WithHeaderTitle = () => {
|
||||
return (
|
||||
<IDEHeader>
|
||||
<IDEHeader.Left logo={<Icon name="upload-cloud" size="md" />}>
|
||||
<IDEHeaderTitle title="Settings" />
|
||||
</IDEHeader.Left>
|
||||
<IDEHeader.Center>
|
||||
<div />
|
||||
</IDEHeader.Center>
|
||||
<IDEHeader.Right>
|
||||
<div />
|
||||
</IDEHeader.Right>
|
||||
</IDEHeader>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithHeaderDropdown = () => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
return (
|
||||
<IDEHeader>
|
||||
<IDEHeader.Left logo={<Icon name="upload-cloud" size="md" />}>
|
||||
<IDEHeaderSwitcher
|
||||
active={open}
|
||||
prefix={"Pages"}
|
||||
setActive={setOpen}
|
||||
title="Page1"
|
||||
titleTestId={"testId"}
|
||||
>
|
||||
<Flex
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
maxHeight={"300px"}
|
||||
overflow="hidden"
|
||||
>
|
||||
<ListHeaderContainer>
|
||||
<Text kind="heading-xs">Pages</Text>
|
||||
<Button isIconButton kind="tertiary" startIcon="plus" />
|
||||
</ListHeaderContainer>
|
||||
<List
|
||||
items={[
|
||||
{
|
||||
title: "Page1",
|
||||
onClick: noop,
|
||||
description: "",
|
||||
descriptionType: "inline",
|
||||
},
|
||||
{
|
||||
title: "Page2",
|
||||
onClick: noop,
|
||||
description: "",
|
||||
descriptionType: "inline",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Flex>
|
||||
</IDEHeaderSwitcher>
|
||||
</IDEHeader.Left>
|
||||
<IDEHeader.Center>
|
||||
<div />
|
||||
</IDEHeader.Center>
|
||||
<IDEHeader.Right>
|
||||
<div />
|
||||
</IDEHeader.Right>
|
||||
</IDEHeader>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import React, { type PropsWithChildren } from "react";
|
||||
import { Flex } from "../../Flex";
|
||||
import { IDE_HEADER_HEIGHT, LOGO_WIDTH } from "./IDEHeader.constants";
|
||||
|
||||
interface ChildrenProps {
|
||||
children: React.ReactNode | React.ReactNode[];
|
||||
}
|
||||
|
||||
const Left = (props: PropsWithChildren<{ logo: React.ReactNode }>) => {
|
||||
return (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
className="header-left-section"
|
||||
flex="1"
|
||||
gap="spaces-4"
|
||||
height="100%"
|
||||
justifyContent="left"
|
||||
>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
borderRight="1px solid var(--ads-v2-color-border)"
|
||||
h="100%"
|
||||
justifyContent="center"
|
||||
w={`${LOGO_WIDTH}px`}
|
||||
>
|
||||
{props.logo}
|
||||
</Flex>
|
||||
{props.children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const Center = (props: ChildrenProps) => {
|
||||
return (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
className="header-center-section"
|
||||
flex="1"
|
||||
height="100%"
|
||||
justifyContent="center"
|
||||
>
|
||||
{props.children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const Right = (props: ChildrenProps) => {
|
||||
return (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
className="header-right-section"
|
||||
flex="1"
|
||||
gap="spaces-3"
|
||||
height="100%"
|
||||
justifyContent="right"
|
||||
>
|
||||
{props.children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const IDEHeader = (props: ChildrenProps) => {
|
||||
return (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
background="var(--ads-v2-color-bg)"
|
||||
borderBottom="1px solid var(--ads-v2-color-border)"
|
||||
className="t--editor-header"
|
||||
height={`${IDE_HEADER_HEIGHT}px`}
|
||||
overflow="hidden"
|
||||
width="100%"
|
||||
>
|
||||
{props.children}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
IDEHeader.Left = Left;
|
||||
IDEHeader.Center = Center;
|
||||
IDEHeader.Right = Right;
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import React from "react";
|
||||
import { Flex, Text } from "@appsmith/ads";
|
||||
import { Flex } from "../../Flex";
|
||||
import { Text } from "../../Text";
|
||||
|
||||
/**
|
||||
* Handy little styled component that can be used to render the title in the IDEHeader component
|
||||
* **/
|
||||
const HeaderTitle = ({ title }: { title: string }) => {
|
||||
export const IDEHeaderTitle = ({ title }: { title: string }) => {
|
||||
return (
|
||||
<Flex alignItems="center" height="100%" justifyContent="center">
|
||||
<Text isBold kind="body-m">
|
||||
|
|
@ -13,5 +14,3 @@ const HeaderTitle = ({ title }: { title: string }) => {
|
|||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderTitle;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* The IDEHeader gets exported with 3 layout subsections.
|
||||
* IDEHeader.Left, IDEHeader.Center, IDEHeader.Right
|
||||
* These are composable components that you can use to spread the content of the header
|
||||
* It is possible to use the IDE Header without using these subsections
|
||||
*/
|
||||
export { IDEHeader } from "./IDEHeader";
|
||||
export { IDE_HEADER_HEIGHT, LOGO_WIDTH } from "./IDEHeader.constants";
|
||||
|
||||
/**
|
||||
* IDEHeaderSwitcher can be used for a trigger component to show a dropdown for pages, modules
|
||||
* or any list of elements in the header E.g., Pages / Page 1
|
||||
*/
|
||||
export { IDEHeaderSwitcher } from "./HeaderSwitcher";
|
||||
|
||||
/**
|
||||
* IDEHeaderTitle is a small text styled wrapper that is suitable to be used inside IDEHeader
|
||||
*/
|
||||
export { IDEHeaderTitle } from "./IDEHeaderTitle";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./IDEHeader";
|
||||
export * from "./EntityExplorer";
|
||||
|
|
@ -36,3 +36,4 @@ export * from "./Toast";
|
|||
export * from "./ToggleButton";
|
||||
export * from "./Tooltip";
|
||||
export * from "./ToggleButtonGroup";
|
||||
export * from "./Templates";
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
import HeaderDropdown from "./HeaderDropdown";
|
||||
import "@testing-library/jest-dom";
|
||||
|
||||
describe("HeaderDropdown", () => {
|
||||
it("renders children components correctly", () => {
|
||||
const { getByText } = render(
|
||||
<HeaderDropdown>
|
||||
<HeaderDropdown.Header>
|
||||
<span>Header</span>
|
||||
</HeaderDropdown.Header>
|
||||
<HeaderDropdown.Body>
|
||||
<span>Body</span>
|
||||
</HeaderDropdown.Body>
|
||||
</HeaderDropdown>,
|
||||
);
|
||||
|
||||
expect(getByText("Header")).toBeInTheDocument();
|
||||
expect(getByText("Body")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies custom className to the header", () => {
|
||||
const customClass = "my-custom-class";
|
||||
const { container } = render(
|
||||
<HeaderDropdown>
|
||||
<HeaderDropdown.Header className={customClass}>
|
||||
<span>Header</span>
|
||||
</HeaderDropdown.Header>
|
||||
<HeaderDropdown.Body>
|
||||
<span>Body</span>
|
||||
</HeaderDropdown.Body>
|
||||
</HeaderDropdown>,
|
||||
);
|
||||
|
||||
const headerElement = container.querySelector(`.${customClass}`);
|
||||
|
||||
expect(headerElement).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
import { Flex } from "@appsmith/ads";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Container = styled(Flex)`
|
||||
& .t--entity-item {
|
||||
grid-template-columns: 0 auto 1fr auto auto auto auto auto;
|
||||
height: 32px;
|
||||
|
||||
& .t--entity-name {
|
||||
padding-left: var(--ads-v2-spaces-3);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const HeaderWrapper = styled.div`
|
||||
padding: var(--ads-v2-spaces-3);
|
||||
padding-right: var(--ads-v2-spaces-2);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
span {
|
||||
line-height: 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
interface EditorHeaderDropdown {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface EditorHeaderDropdownHeader {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface EditorHeaderDropdownBody {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
function EditorHeaderDropdown({ children }: EditorHeaderDropdown) {
|
||||
return (
|
||||
<Container
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
maxHeight="300px"
|
||||
overflow="hidden"
|
||||
>
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
function EditorHeaderDropdownHeader({
|
||||
children,
|
||||
className,
|
||||
}: EditorHeaderDropdownHeader) {
|
||||
return <HeaderWrapper className={className}>{children}</HeaderWrapper>;
|
||||
}
|
||||
|
||||
function EditorHeaderDropdownBody({ children }: EditorHeaderDropdownBody) {
|
||||
return (
|
||||
<Flex
|
||||
alignItems="center"
|
||||
flex="1"
|
||||
flexDirection="column"
|
||||
overflow="auto"
|
||||
px="spaces-2"
|
||||
width="100%"
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
EditorHeaderDropdown.Header = EditorHeaderDropdownHeader;
|
||||
EditorHeaderDropdown.Body = EditorHeaderDropdownBody;
|
||||
|
||||
export default EditorHeaderDropdown;
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import { Flex, Icon, Text } from "@appsmith/ads";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
interface HeaderEditorSwitcherProps {
|
||||
prefix: string;
|
||||
title?: string;
|
||||
titleTestId: string;
|
||||
active: boolean;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const SwitchTrigger = styled.div<{ active: boolean }>`
|
||||
border-radius: var(--ads-v2-border-radius);
|
||||
background-color: ${(props) =>
|
||||
props.active ? `var(--ads-v2-color-bg-subtle)` : "unset"};
|
||||
cursor: pointer;
|
||||
padding: var(--ads-v2-spaces-2);
|
||||
:hover {
|
||||
background-color: var(--ads-v2-color-bg-subtle);
|
||||
}
|
||||
`;
|
||||
|
||||
const HeaderEditorSwitcher = React.forwardRef(
|
||||
// TODO: Fix this the next time the file is edited
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(props: HeaderEditorSwitcherProps, ref: any) => {
|
||||
const { active, className, prefix, title, titleTestId, ...rest } = props;
|
||||
|
||||
const separator = title ? " /" : "";
|
||||
|
||||
return (
|
||||
<SwitchTrigger
|
||||
active={active}
|
||||
className={`flex align-center items-center justify-center ${className}`}
|
||||
ref={ref}
|
||||
{...rest}
|
||||
>
|
||||
<Text
|
||||
color="var(--ads-v2-colors-content-label-inactive-fg)"
|
||||
kind="body-m"
|
||||
>
|
||||
{prefix + separator}
|
||||
</Text>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
className={titleTestId}
|
||||
data-active={active}
|
||||
gap="spaces-1"
|
||||
height="100%"
|
||||
justifyContent="center"
|
||||
paddingLeft="spaces-2"
|
||||
>
|
||||
<Text isBold kind="body-m">
|
||||
{title}
|
||||
</Text>
|
||||
<Icon
|
||||
color={
|
||||
title
|
||||
? undefined
|
||||
: "var(--ads-v2-colors-content-label-inactive-fg)"
|
||||
}
|
||||
name={active ? "arrow-up-s-line" : "arrow-down-s-line"}
|
||||
size="md"
|
||||
/>
|
||||
</Flex>
|
||||
</SwitchTrigger>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default HeaderEditorSwitcher;
|
||||
|
|
@ -3,15 +3,6 @@
|
|||
Components that are part of the main structure of the IDE experience
|
||||
====================================================**/
|
||||
|
||||
/**
|
||||
* The IDEHeader gets exported with 3 layout subsections.
|
||||
* IDEHeader.Left, IDEHeader.Center, IDEHeader.Right
|
||||
* These are composable components that you can use to spread the content of the header
|
||||
* It is possible to use the IDE Header without using these subsections
|
||||
*/
|
||||
export { IDE_HEADER_HEIGHT } from "./Structure/constants";
|
||||
export { default as IDEHeader } from "./Structure/Header";
|
||||
|
||||
/**
|
||||
* The IDEToolbar gets exported with 2 layout subsections.
|
||||
* IDEToolbar.Left and IDEToolbar.Right
|
||||
|
|
@ -25,22 +16,6 @@ export { default as IDEToolbar } from "./Structure/Toolbar";
|
|||
Components that are smaller UI abstractions for easy use and standardisation within the IDE
|
||||
=======================================================**/
|
||||
|
||||
/**
|
||||
* IDEHeaderTitle is a small text styled wrapper that is suitable to be used inside IDEHeader
|
||||
*/
|
||||
export { default as IDEHeaderTitle } from "./Components/HeaderTitle";
|
||||
/**
|
||||
* IDEHeaderEditorSwitcher can be used for a trigger component to show a dropdown for pages, modules
|
||||
* or any list of elements in the header E.g., Pages / Page 1
|
||||
*/
|
||||
export { default as IDEHeaderEditorSwitcher } from "./Components/HeaderEditorSwitcher";
|
||||
/**
|
||||
* The IDEHeaderDropdown gets exported with 2 layout subsections.
|
||||
* IDEHeaderDropdown.Header, IDEHeaderDropdown.Body
|
||||
* These are composable components that you can use to spread the content of the header
|
||||
* It is possible to use the IDE Header without using these subsections
|
||||
*/
|
||||
export { default as IDEHeaderDropdown } from "./Components/HeaderDropdown";
|
||||
/**
|
||||
* IDEBottomView is a versatile view meant to be at the bottom of the screen.
|
||||
* It is resizable and can be hidden or collapsed based on the behavior configured
|
||||
|
|
|
|||
|
|
@ -2485,7 +2485,8 @@ export const ADD_PAGE_FROM_TEMPLATE_MODAL = {
|
|||
|
||||
export const HEADER_TITLES = {
|
||||
DATA: () => "Data",
|
||||
EDITOR: () => "Pages",
|
||||
PAGES: () => "Pages",
|
||||
EDITOR: () => "Editor",
|
||||
SETTINGS: () => "Settings",
|
||||
LIBRARIES: () => "Libraries",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import styled from "styled-components";
|
||||
import { Classes } from "@blueprintjs/core";
|
||||
import { Link, Text } from "@appsmith/ads";
|
||||
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||
import { Link, Text, IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
|
||||
export const Wrapper = styled.div`
|
||||
flex-basis: calc(100% - ${(props) => props.theme.homePage.leftPane.width}px);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import { getIsAppSettingsPaneWithNavigationTabOpen } from "selectors/appSettings
|
|||
import NavigationLogo from "ee/pages/AppViewer/NavigationLogo";
|
||||
import MenuItemContainer from "./components/MenuItemContainer";
|
||||
import BackToAppsButton from "./components/BackToAppsButton";
|
||||
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
import { BOTTOM_BAR_HEIGHT } from "components/BottomBar/constants";
|
||||
|
||||
interface SidebarProps {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import styled from "styled-components";
|
||||
import { Classes } from "@blueprintjs/core";
|
||||
import { getTypographyByKey } from "@appsmith/ads-old";
|
||||
import { Icon } from "@appsmith/ads";
|
||||
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||
import { Icon, IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
|
||||
export const Container = styled.div`
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { Text } from "@appsmith/ads";
|
||||
import { ListItemContainer, ListWithHeader } from "@appsmith/ads";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useLocation } from "react-router";
|
||||
|
||||
|
|
@ -18,7 +18,6 @@ import { getNextEntityName } from "utils/AppsmithUtils";
|
|||
import { getCurrentWorkspaceId } from "ee/selectors/selectedWorkspaceSelectors";
|
||||
import { getInstanceId } from "ee/selectors/tenantSelectors";
|
||||
import { PageElement } from "pages/Editor/IDE/EditorPane/components/PageElement";
|
||||
import { IDEHeaderDropdown } from "IDE";
|
||||
import { PAGE_ENTITY_NAME } from "ee/constants/messages";
|
||||
|
||||
const PagesSection = ({ onItemSelected }: { onItemSelected: () => void }) => {
|
||||
|
|
@ -50,35 +49,50 @@ const PagesSection = ({ onItemSelected }: { onItemSelected: () => void }) => {
|
|||
dispatch(
|
||||
createNewPageFromEntities(applicationId, name, workspaceId, instanceId),
|
||||
);
|
||||
}, [dispatch, pages, applicationId]);
|
||||
}, [pages, dispatch, applicationId, workspaceId, instanceId]);
|
||||
|
||||
const onMenuClose = useCallback(() => setIsMenuOpen(false), [setIsMenuOpen]);
|
||||
|
||||
const pageElements = useMemo(
|
||||
() =>
|
||||
pages.map((page) => (
|
||||
<PageElement key={page.pageId} onClick={onItemSelected} page={page} />
|
||||
<ListItemContainer key={page.pageId}>
|
||||
<PageElement onClick={onItemSelected} page={page} />
|
||||
</ListItemContainer>
|
||||
)),
|
||||
[pages, location.pathname],
|
||||
[pages, location.pathname, onItemSelected],
|
||||
);
|
||||
|
||||
const createPageContextMenu = useMemo(() => {
|
||||
if (!canCreatePages) return null;
|
||||
|
||||
return (
|
||||
<AddPageContextMenu
|
||||
buttonSize="sm"
|
||||
className={`${EntityClassNames.ADD_BUTTON} group pages`}
|
||||
createPageCallback={createPageCallback}
|
||||
onItemSelected={onItemSelected}
|
||||
onMenuClose={onMenuClose}
|
||||
openMenu={isMenuOpen}
|
||||
/>
|
||||
);
|
||||
}, [
|
||||
canCreatePages,
|
||||
createPageCallback,
|
||||
isMenuOpen,
|
||||
onItemSelected,
|
||||
onMenuClose,
|
||||
]);
|
||||
|
||||
return (
|
||||
<IDEHeaderDropdown>
|
||||
<IDEHeaderDropdown.Header className="pages">
|
||||
<Text kind="heading-xs">{`All Pages (${pages.length})`}</Text>
|
||||
{canCreatePages ? (
|
||||
<AddPageContextMenu
|
||||
buttonSize="sm"
|
||||
className={`${EntityClassNames.ADD_BUTTON} group pages`}
|
||||
createPageCallback={createPageCallback}
|
||||
onItemSelected={onItemSelected}
|
||||
onMenuClose={onMenuClose}
|
||||
openMenu={isMenuOpen}
|
||||
/>
|
||||
) : null}
|
||||
</IDEHeaderDropdown.Header>
|
||||
<IDEHeaderDropdown.Body>{pageElements}</IDEHeaderDropdown.Body>
|
||||
</IDEHeaderDropdown>
|
||||
<ListWithHeader
|
||||
headerClassName={"pages"}
|
||||
headerControls={createPageContextMenu}
|
||||
headerText={`All Pages (${pages.length})`}
|
||||
maxHeight={"300px"}
|
||||
>
|
||||
{pageElements}
|
||||
</ListWithHeader>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,34 @@
|
|||
import React, { useState } from "react";
|
||||
import { Popover, PopoverTrigger, PopoverContent } from "@appsmith/ads";
|
||||
import React from "react";
|
||||
import { IDEHeaderSwitcher } from "@appsmith/ads";
|
||||
|
||||
import { createMessage, HEADER_TITLES } from "ee/constants/messages";
|
||||
import { PagesSection } from "../EditorPane/PagesSection";
|
||||
import { IDEHeaderEditorSwitcher } from "IDE";
|
||||
import { useBoolean } from "usehooks-ts";
|
||||
import { useSelector } from "react-redux";
|
||||
import { getCurrentPageId, getPageById } from "selectors/editorSelectors";
|
||||
|
||||
const EditorTitle = ({ title }: { title: string }) => {
|
||||
const [active, setActive] = useState(false);
|
||||
const EditorTitle = () => {
|
||||
const {
|
||||
setFalse: setMenuClose,
|
||||
setValue: setMenuState,
|
||||
value: isMenuOpen,
|
||||
} = useBoolean(false);
|
||||
|
||||
const closeMenu = () => {
|
||||
setActive(false);
|
||||
};
|
||||
const pageId = useSelector(getCurrentPageId) as string;
|
||||
const currentPage = useSelector(getPageById(pageId));
|
||||
|
||||
if (!currentPage) return null;
|
||||
|
||||
return (
|
||||
<Popover onOpenChange={setActive} open={active}>
|
||||
<PopoverTrigger>
|
||||
<IDEHeaderEditorSwitcher
|
||||
active={active}
|
||||
prefix={createMessage(HEADER_TITLES.EDITOR)}
|
||||
title={title}
|
||||
titleTestId="t--pages-switcher"
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
align="start"
|
||||
className="!p-0 !pb-1"
|
||||
onEscapeKeyDown={closeMenu}
|
||||
>
|
||||
<PagesSection onItemSelected={closeMenu} />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<IDEHeaderSwitcher
|
||||
active={isMenuOpen}
|
||||
prefix={createMessage(HEADER_TITLES.PAGES)}
|
||||
setActive={setMenuState}
|
||||
title={currentPage.pageName}
|
||||
titleTestId="t--pages-switcher"
|
||||
>
|
||||
<PagesSection onItemSelected={setMenuClose} />
|
||||
</IDEHeaderSwitcher>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import {
|
|||
TabPanel,
|
||||
Button,
|
||||
Link,
|
||||
IDEHeader,
|
||||
IDEHeaderTitle,
|
||||
} from "@appsmith/ads";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { EditInteractionKind, SavingState } from "@appsmith/ads-old";
|
||||
|
|
@ -73,11 +75,10 @@ import { EditorTitle } from "./EditorTitle";
|
|||
import { useCurrentAppState } from "../hooks/useCurrentAppState";
|
||||
import { EditorState } from "ee/entities/IDE/constants";
|
||||
import { EditorSaveIndicator } from "pages/Editor/EditorSaveIndicator";
|
||||
import type { Page } from "entities/Page";
|
||||
import { IDEHeader, IDEHeaderTitle } from "IDE";
|
||||
import { APPLICATIONS_URL } from "constants/routes";
|
||||
import { useNavigationMenuData } from "../../EditorName/useNavigationMenuData";
|
||||
import useLibraryHeaderTitle from "ee/pages/Editor/IDE/Header/useLibraryHeaderTitle";
|
||||
import { AppsmithLink } from "pages/Editor/AppsmithLink";
|
||||
|
||||
const StyledDivider = styled(Divider)`
|
||||
height: 50%;
|
||||
|
|
@ -89,10 +90,9 @@ const { cloudHosting } = getAppsmithConfigs();
|
|||
|
||||
interface HeaderTitleProps {
|
||||
appState: EditorState;
|
||||
currentPage?: Page;
|
||||
}
|
||||
|
||||
const HeaderTitleComponent = ({ appState, currentPage }: HeaderTitleProps) => {
|
||||
const HeaderTitleComponent = ({ appState }: HeaderTitleProps) => {
|
||||
const libraryHeaderTitle = useLibraryHeaderTitle();
|
||||
|
||||
switch (appState) {
|
||||
|
|
@ -104,7 +104,7 @@ const HeaderTitleComponent = ({ appState, currentPage }: HeaderTitleProps) => {
|
|||
/>
|
||||
);
|
||||
case EditorState.EDITOR:
|
||||
return <EditorTitle key={appState} title={currentPage?.pageName || ""} />;
|
||||
return <EditorTitle key={appState} />;
|
||||
case EditorState.SETTINGS:
|
||||
return (
|
||||
<IDEHeaderTitle
|
||||
|
|
@ -115,7 +115,7 @@ const HeaderTitleComponent = ({ appState, currentPage }: HeaderTitleProps) => {
|
|||
case EditorState.LIBRARIES:
|
||||
return <IDEHeaderTitle key={appState} title={libraryHeaderTitle} />;
|
||||
default:
|
||||
return <EditorTitle key={appState} title={currentPage?.pageName || ""} />;
|
||||
return <EditorTitle key={appState} />;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -218,8 +218,8 @@ const Header = () => {
|
|||
return (
|
||||
<>
|
||||
<IDEHeader>
|
||||
<IDEHeader.Left>
|
||||
<HeaderTitleComponent appState={appState} currentPage={currentPage} />
|
||||
<IDEHeader.Left logo={<AppsmithLink />}>
|
||||
<HeaderTitleComponent appState={appState} />
|
||||
<EditorSaveIndicator isSaving={isSaving} saveError={pageSaveError} />
|
||||
</IDEHeader.Left>
|
||||
<IDEHeader.Center>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
RUN_GUTTER_ID,
|
||||
} from "./constants";
|
||||
import { thinScrollbar } from "constants/DefaultTheme";
|
||||
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
|
||||
export const CodeEditorWithGutterStyles = css`
|
||||
.${RUN_GUTTER_ID} {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { getIsAppSettingsPaneWithNavigationTabOpen } from "selectors/appSettings
|
|||
import { EditorState } from "ee/entities/IDE/constants";
|
||||
import { RenderModes } from "constants/WidgetConstants";
|
||||
import styled from "styled-components";
|
||||
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
import { BOTTOM_BAR_HEIGHT } from "components/BottomBar/constants";
|
||||
|
||||
const Container = styled.div`
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import styled from "styled-components";
|
||||
import { Profile } from "pages/common/ProfileImage";
|
||||
import { getTypographyByKey } from "@appsmith/ads-old";
|
||||
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
|
||||
export const HeaderWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import classNames from "classnames";
|
|||
import { useSelector } from "react-redux";
|
||||
import { combinedPreviewModeSelector } from "../../../selectors/editorSelectors";
|
||||
import { protectedModeSelector } from "selectors/gitSyncSelectors";
|
||||
import { IDE_HEADER_HEIGHT } from "../../../IDE";
|
||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
import { BOTTOM_BAR_HEIGHT } from "../../../components/BottomBar/constants";
|
||||
import { PROTECTED_CALLOUT_HEIGHT } from "../IDE/ProtectedCallout";
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import styled from "styled-components";
|
|||
import { snipingModeSelector } from "selectors/editorSelectors";
|
||||
import { retryPromise } from "utils/AppsmithUtils";
|
||||
import { useSelector } from "react-redux";
|
||||
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
|
||||
const BindingBanner = styled.div`
|
||||
position: fixed;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ import { PartialExportModal } from "components/editorComponents/PartialImportExp
|
|||
import { PartialImportModal } from "components/editorComponents/PartialImportExport/PartialImportModal";
|
||||
import type { Page } from "entities/Page";
|
||||
import { AppCURLImportModal } from "ee/pages/Editor/CurlImport";
|
||||
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
||||
import GeneratePageModal from "./GeneratePage";
|
||||
|
||||
interface EditorProps {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user