This reverts commit 8cb0beeecc.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced `HeaderEditorSwitcher` and `HeaderDropdown` components for
enhanced UI interactions.
- **Bug Fixes**
- Updated import paths for `IDE_HEADER_HEIGHT` across multiple
components to ensure consistent styling.
- **Refactor**
- Renamed and restructured components for clarity and improved
functionality, including `HeaderTitle` and `IDEHeaderSwitcher`.
- **Tests**
- Added tests for the `HeaderDropdown` component to validate rendering
and functionality.
- **Chores**
- Removed deprecated components and files to streamline the codebase.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
parent
7ac4df21dd
commit
1ea45e8e48
|
|
@ -1,38 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export { ListItemContainer, ListHeaderContainer } from "./styles";
|
|
||||||
export { ListWithHeader } from "./ListWithHeader";
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
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,88 +0,0 @@
|
||||||
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";
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export { IDEHeaderSwitcher } from "./IDEHeaderSwitcher";
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export const IDE_HEADER_HEIGHT = 40;
|
|
||||||
export const LOGO_WIDTH = 50;
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
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} />
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
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,19 +0,0 @@
|
||||||
/**
|
|
||||||
* 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";
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export * from "./IDEHeader";
|
|
||||||
export * from "./EntityExplorer";
|
|
||||||
|
|
@ -36,4 +36,3 @@ export * from "./Toast";
|
||||||
export * from "./ToggleButton";
|
export * from "./ToggleButton";
|
||||||
export * from "./Tooltip";
|
export * from "./Tooltip";
|
||||||
export * from "./ToggleButtonGroup";
|
export * from "./ToggleButtonGroup";
|
||||||
export * from "./Templates";
|
|
||||||
|
|
|
||||||
40
app/client/src/IDE/Components/HeaderDropdown.test.tsx
Normal file
40
app/client/src/IDE/Components/HeaderDropdown.test.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
79
app/client/src/IDE/Components/HeaderDropdown.tsx
Normal file
79
app/client/src/IDE/Components/HeaderDropdown.tsx
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
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,67 +1,67 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { render, fireEvent, screen } from "@testing-library/react";
|
import { render, fireEvent } from "@testing-library/react";
|
||||||
import { IDEHeaderSwitcher } from "./IDEHeaderSwitcher";
|
import HeaderEditorSwitcher from "./HeaderEditorSwitcher";
|
||||||
import "@testing-library/jest-dom";
|
import "@testing-library/jest-dom";
|
||||||
|
|
||||||
describe("HeaderSwitcher", () => {
|
describe("HeaderEditorSwitcher", () => {
|
||||||
const mockOnClick = jest.fn();
|
const mockOnClick = jest.fn();
|
||||||
const mockSetActive = jest.fn();
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
prefix: "Prefix",
|
prefix: "Prefix",
|
||||||
title: "Title",
|
title: "Title",
|
||||||
titleTestId: "titleTestId",
|
titleTestId: "titleTestId",
|
||||||
active: false,
|
active: false,
|
||||||
onClick: mockOnClick,
|
onClick: mockOnClick,
|
||||||
setActive: mockSetActive,
|
|
||||||
children: <span>Test</span>,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it("renders with correct props", () => {
|
it("renders with correct props", () => {
|
||||||
render(<IDEHeaderSwitcher {...defaultProps} />);
|
const { getByText } = render(<HeaderEditorSwitcher {...defaultProps} />);
|
||||||
|
|
||||||
// eslint-disable-next-line testing-library/no-node-access
|
|
||||||
const testIdElement = document.getElementsByClassName(
|
const testIdElement = document.getElementsByClassName(
|
||||||
defaultProps.titleTestId,
|
defaultProps.titleTestId,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText("Prefix /")).toBeInTheDocument();
|
expect(getByText("Prefix /")).toBeInTheDocument();
|
||||||
expect(screen.getByText(defaultProps.title)).toBeInTheDocument();
|
expect(getByText(defaultProps.title)).toBeInTheDocument();
|
||||||
expect(testIdElement).toBeDefined();
|
expect(testIdElement).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders active state correctly", () => {
|
it("renders active state correctly", () => {
|
||||||
render(<IDEHeaderSwitcher {...defaultProps} active />);
|
const { getByText } = render(
|
||||||
|
<HeaderEditorSwitcher {...defaultProps} active />,
|
||||||
|
);
|
||||||
|
|
||||||
expect(screen.getByText("Prefix /")).toHaveStyle(
|
expect(getByText("Prefix /")).toHaveStyle(
|
||||||
"background-color: var(--ads-v2-color-bg-subtle)",
|
"background-color: var(--ads-v2-color-bg-subtle)",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calls onClick handler when clicked", () => {
|
it("calls onClick handler when clicked", () => {
|
||||||
render(<IDEHeaderSwitcher {...defaultProps} />);
|
const { getByText } = render(<HeaderEditorSwitcher {...defaultProps} />);
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Title"));
|
fireEvent.click(getByText("Title"));
|
||||||
|
|
||||||
expect(mockOnClick).toHaveBeenCalled();
|
expect(mockOnClick).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("forwards ref correctly", () => {
|
it("forwards ref correctly", () => {
|
||||||
const ref = React.createRef<HTMLDivElement>();
|
const ref = React.createRef();
|
||||||
|
|
||||||
render(<IDEHeaderSwitcher {...defaultProps} ref={ref} />);
|
render(<HeaderEditorSwitcher {...defaultProps} ref={ref} />);
|
||||||
expect(ref.current).toBeTruthy();
|
expect(ref.current).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not crash when onClick is not provided", () => {
|
it("does not crash when onClick is not provided", () => {
|
||||||
render(<IDEHeaderSwitcher {...defaultProps} onClick={undefined} />);
|
const { getByText } = render(
|
||||||
|
<HeaderEditorSwitcher {...defaultProps} onClick={undefined} />,
|
||||||
|
);
|
||||||
|
|
||||||
fireEvent.click(screen.getByText("Title")); // Should not throw error
|
fireEvent.click(getByText("Title")); // Should not throw error
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not show separator and applies different inactive color to icon", () => {
|
it("does not show separator and applies different inactive color to icon", () => {
|
||||||
const ref = React.createRef<HTMLDivElement>();
|
const ref = React.createRef();
|
||||||
const { container } = render(
|
const { container, getByTestId } = render(
|
||||||
<IDEHeaderSwitcher
|
<HeaderEditorSwitcher
|
||||||
{...defaultProps}
|
{...defaultProps}
|
||||||
data-testid="root-div"
|
data-testid="root-div"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|
@ -69,10 +69,9 @@ describe("HeaderSwitcher", () => {
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line testing-library/no-container,testing-library/no-node-access
|
|
||||||
const icon = container.querySelector(".remixicon-icon"); // Get chevron icon
|
const icon = container.querySelector(".remixicon-icon"); // Get chevron icon
|
||||||
|
|
||||||
expect(screen.getByTestId("root-div")).toHaveTextContent("Prefix");
|
expect(getByTestId("root-div")).toHaveTextContent("Prefix");
|
||||||
expect(icon).toHaveAttribute(
|
expect(icon).toHaveAttribute(
|
||||||
"fill",
|
"fill",
|
||||||
"var(--ads-v2-colors-content-label-inactive-fg)",
|
"var(--ads-v2-colors-content-label-inactive-fg)",
|
||||||
|
|
@ -84,20 +83,16 @@ describe("HeaderSwitcher", () => {
|
||||||
const className = "custom-class";
|
const className = "custom-class";
|
||||||
|
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<IDEHeaderSwitcher
|
<HeaderEditorSwitcher
|
||||||
active
|
active
|
||||||
className={className}
|
className={className}
|
||||||
data-testid={testId} // Additional prop
|
data-testid={testId} // Additional prop
|
||||||
prefix="Prefix"
|
prefix="Prefix"
|
||||||
setActive={mockSetActive}
|
|
||||||
title="Title"
|
title="Title"
|
||||||
titleTestId="titleTestId"
|
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 firstDiv = container.querySelector("div"); // Get the first div element
|
||||||
const classNames = firstDiv?.getAttribute("class")?.split(" ") || [];
|
const classNames = firstDiv?.getAttribute("class")?.split(" ") || [];
|
||||||
|
|
||||||
73
app/client/src/IDE/Components/HeaderEditorSwitcher.tsx
Normal file
73
app/client/src/IDE/Components/HeaderEditorSwitcher.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
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;
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Flex } from "../../Flex";
|
import { Flex, Text } from "@appsmith/ads";
|
||||||
import { Text } from "../../Text";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handy little styled component that can be used to render the title in the IDEHeader component
|
* Handy little styled component that can be used to render the title in the IDEHeader component
|
||||||
* **/
|
* **/
|
||||||
export const IDEHeaderTitle = ({ title }: { title: string }) => {
|
const HeaderTitle = ({ title }: { title: string }) => {
|
||||||
return (
|
return (
|
||||||
<Flex alignItems="center" height="100%" justifyContent="center">
|
<Flex alignItems="center" height="100%" justifyContent="center">
|
||||||
<Text isBold kind="body-m">
|
<Text isBold kind="body-m">
|
||||||
|
|
@ -14,3 +13,5 @@ export const IDEHeaderTitle = ({ title }: { title: string }) => {
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default HeaderTitle;
|
||||||
|
|
@ -3,6 +3,15 @@
|
||||||
Components that are part of the main structure of the IDE experience
|
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.
|
* The IDEToolbar gets exported with 2 layout subsections.
|
||||||
* IDEToolbar.Left and IDEToolbar.Right
|
* IDEToolbar.Left and IDEToolbar.Right
|
||||||
|
|
@ -16,6 +25,22 @@ export { default as IDEToolbar } from "./Structure/Toolbar";
|
||||||
Components that are smaller UI abstractions for easy use and standardisation within the IDE
|
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.
|
* 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
|
* It is resizable and can be hidden or collapsed based on the behavior configured
|
||||||
|
|
|
||||||
|
|
@ -2485,8 +2485,7 @@ export const ADD_PAGE_FROM_TEMPLATE_MODAL = {
|
||||||
|
|
||||||
export const HEADER_TITLES = {
|
export const HEADER_TITLES = {
|
||||||
DATA: () => "Data",
|
DATA: () => "Data",
|
||||||
PAGES: () => "Pages",
|
EDITOR: () => "Pages",
|
||||||
EDITOR: () => "Editor",
|
|
||||||
SETTINGS: () => "Settings",
|
SETTINGS: () => "Settings",
|
||||||
LIBRARIES: () => "Libraries",
|
LIBRARIES: () => "Libraries",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { Classes } from "@blueprintjs/core";
|
import { Classes } from "@blueprintjs/core";
|
||||||
import { Link, Text, IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { Link, Text } from "@appsmith/ads";
|
||||||
|
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||||
|
|
||||||
export const Wrapper = styled.div`
|
export const Wrapper = styled.div`
|
||||||
flex-basis: calc(100% - ${(props) => props.theme.homePage.leftPane.width}px);
|
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 NavigationLogo from "ee/pages/AppViewer/NavigationLogo";
|
||||||
import MenuItemContainer from "./components/MenuItemContainer";
|
import MenuItemContainer from "./components/MenuItemContainer";
|
||||||
import BackToAppsButton from "./components/BackToAppsButton";
|
import BackToAppsButton from "./components/BackToAppsButton";
|
||||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||||
import { BOTTOM_BAR_HEIGHT } from "components/BottomBar/constants";
|
import { BOTTOM_BAR_HEIGHT } from "components/BottomBar/constants";
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { Classes } from "@blueprintjs/core";
|
import { Classes } from "@blueprintjs/core";
|
||||||
import { getTypographyByKey } from "@appsmith/ads-old";
|
import { getTypographyByKey } from "@appsmith/ads-old";
|
||||||
import { Icon, IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { Icon } from "@appsmith/ads";
|
||||||
|
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||||
|
|
||||||
export const Container = styled.div`
|
export const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useCallback, useMemo, useState } from "react";
|
import React, { useCallback, useMemo, useState } from "react";
|
||||||
import { ListItemContainer, ListWithHeader } from "@appsmith/ads";
|
import { Text } from "@appsmith/ads";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useLocation } from "react-router";
|
import { useLocation } from "react-router";
|
||||||
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { getNextEntityName } from "utils/AppsmithUtils";
|
||||||
import { getCurrentWorkspaceId } from "ee/selectors/selectedWorkspaceSelectors";
|
import { getCurrentWorkspaceId } from "ee/selectors/selectedWorkspaceSelectors";
|
||||||
import { getInstanceId } from "ee/selectors/tenantSelectors";
|
import { getInstanceId } from "ee/selectors/tenantSelectors";
|
||||||
import { PageElement } from "pages/Editor/IDE/EditorPane/components/PageElement";
|
import { PageElement } from "pages/Editor/IDE/EditorPane/components/PageElement";
|
||||||
|
import { IDEHeaderDropdown } from "IDE";
|
||||||
import { PAGE_ENTITY_NAME } from "ee/constants/messages";
|
import { PAGE_ENTITY_NAME } from "ee/constants/messages";
|
||||||
|
|
||||||
const PagesSection = ({ onItemSelected }: { onItemSelected: () => void }) => {
|
const PagesSection = ({ onItemSelected }: { onItemSelected: () => void }) => {
|
||||||
|
|
@ -49,24 +50,23 @@ const PagesSection = ({ onItemSelected }: { onItemSelected: () => void }) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
createNewPageFromEntities(applicationId, name, workspaceId, instanceId),
|
createNewPageFromEntities(applicationId, name, workspaceId, instanceId),
|
||||||
);
|
);
|
||||||
}, [pages, dispatch, applicationId, workspaceId, instanceId]);
|
}, [dispatch, pages, applicationId]);
|
||||||
|
|
||||||
const onMenuClose = useCallback(() => setIsMenuOpen(false), [setIsMenuOpen]);
|
const onMenuClose = useCallback(() => setIsMenuOpen(false), [setIsMenuOpen]);
|
||||||
|
|
||||||
const pageElements = useMemo(
|
const pageElements = useMemo(
|
||||||
() =>
|
() =>
|
||||||
pages.map((page) => (
|
pages.map((page) => (
|
||||||
<ListItemContainer key={page.pageId}>
|
<PageElement key={page.pageId} onClick={onItemSelected} page={page} />
|
||||||
<PageElement onClick={onItemSelected} page={page} />
|
|
||||||
</ListItemContainer>
|
|
||||||
)),
|
)),
|
||||||
[pages, location.pathname, onItemSelected],
|
[pages, location.pathname],
|
||||||
);
|
);
|
||||||
|
|
||||||
const createPageContextMenu = useMemo(() => {
|
|
||||||
if (!canCreatePages) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<IDEHeaderDropdown>
|
||||||
|
<IDEHeaderDropdown.Header className="pages">
|
||||||
|
<Text kind="heading-xs">{`All Pages (${pages.length})`}</Text>
|
||||||
|
{canCreatePages ? (
|
||||||
<AddPageContextMenu
|
<AddPageContextMenu
|
||||||
buttonSize="sm"
|
buttonSize="sm"
|
||||||
className={`${EntityClassNames.ADD_BUTTON} group pages`}
|
className={`${EntityClassNames.ADD_BUTTON} group pages`}
|
||||||
|
|
@ -75,24 +75,10 @@ const PagesSection = ({ onItemSelected }: { onItemSelected: () => void }) => {
|
||||||
onMenuClose={onMenuClose}
|
onMenuClose={onMenuClose}
|
||||||
openMenu={isMenuOpen}
|
openMenu={isMenuOpen}
|
||||||
/>
|
/>
|
||||||
);
|
) : null}
|
||||||
}, [
|
</IDEHeaderDropdown.Header>
|
||||||
canCreatePages,
|
<IDEHeaderDropdown.Body>{pageElements}</IDEHeaderDropdown.Body>
|
||||||
createPageCallback,
|
</IDEHeaderDropdown>
|
||||||
isMenuOpen,
|
|
||||||
onItemSelected,
|
|
||||||
onMenuClose,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ListWithHeader
|
|
||||||
headerClassName={"pages"}
|
|
||||||
headerControls={createPageContextMenu}
|
|
||||||
headerText={`All Pages (${pages.length})`}
|
|
||||||
maxHeight={"300px"}
|
|
||||||
>
|
|
||||||
{pageElements}
|
|
||||||
</ListWithHeader>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,35 @@
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { IDEHeaderSwitcher } from "@appsmith/ads";
|
import { Popover, PopoverTrigger, PopoverContent } from "@appsmith/ads";
|
||||||
|
|
||||||
import { createMessage, HEADER_TITLES } from "ee/constants/messages";
|
import { createMessage, HEADER_TITLES } from "ee/constants/messages";
|
||||||
import { PagesSection } from "../EditorPane/PagesSection";
|
import { PagesSection } from "../EditorPane/PagesSection";
|
||||||
import { useBoolean } from "usehooks-ts";
|
import { IDEHeaderEditorSwitcher } from "IDE";
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { getCurrentPageId, getPageById } from "selectors/editorSelectors";
|
|
||||||
|
|
||||||
const EditorTitle = () => {
|
const EditorTitle = ({ title }: { title: string }) => {
|
||||||
const {
|
const [active, setActive] = useState(false);
|
||||||
setFalse: setMenuClose,
|
|
||||||
setValue: setMenuState,
|
|
||||||
value: isMenuOpen,
|
|
||||||
} = useBoolean(false);
|
|
||||||
|
|
||||||
const pageId = useSelector(getCurrentPageId) as string;
|
const closeMenu = () => {
|
||||||
const currentPage = useSelector(getPageById(pageId));
|
setActive(false);
|
||||||
|
};
|
||||||
if (!currentPage) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IDEHeaderSwitcher
|
<Popover onOpenChange={setActive} open={active}>
|
||||||
active={isMenuOpen}
|
<PopoverTrigger>
|
||||||
prefix={createMessage(HEADER_TITLES.PAGES)}
|
<IDEHeaderEditorSwitcher
|
||||||
setActive={setMenuState}
|
active={active}
|
||||||
title={currentPage.pageName}
|
prefix={createMessage(HEADER_TITLES.EDITOR)}
|
||||||
|
title={title}
|
||||||
titleTestId="t--pages-switcher"
|
titleTestId="t--pages-switcher"
|
||||||
|
/>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent
|
||||||
|
align="start"
|
||||||
|
className="!p-0 !pb-1"
|
||||||
|
onEscapeKeyDown={closeMenu}
|
||||||
>
|
>
|
||||||
<PagesSection onItemSelected={setMenuClose} />
|
<PagesSection onItemSelected={closeMenu} />
|
||||||
</IDEHeaderSwitcher>
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@ import {
|
||||||
TabPanel,
|
TabPanel,
|
||||||
Button,
|
Button,
|
||||||
Link,
|
Link,
|
||||||
IDEHeader,
|
|
||||||
IDEHeaderTitle,
|
|
||||||
} from "@appsmith/ads";
|
} from "@appsmith/ads";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { EditInteractionKind, SavingState } from "@appsmith/ads-old";
|
import { EditInteractionKind, SavingState } from "@appsmith/ads-old";
|
||||||
|
|
@ -75,10 +73,11 @@ import { EditorTitle } from "./EditorTitle";
|
||||||
import { useCurrentAppState } from "../hooks/useCurrentAppState";
|
import { useCurrentAppState } from "../hooks/useCurrentAppState";
|
||||||
import { EditorState } from "ee/entities/IDE/constants";
|
import { EditorState } from "ee/entities/IDE/constants";
|
||||||
import { EditorSaveIndicator } from "pages/Editor/EditorSaveIndicator";
|
import { EditorSaveIndicator } from "pages/Editor/EditorSaveIndicator";
|
||||||
|
import type { Page } from "entities/Page";
|
||||||
|
import { IDEHeader, IDEHeaderTitle } from "IDE";
|
||||||
import { APPLICATIONS_URL } from "constants/routes";
|
import { APPLICATIONS_URL } from "constants/routes";
|
||||||
import { useNavigationMenuData } from "../../EditorName/useNavigationMenuData";
|
import { useNavigationMenuData } from "../../EditorName/useNavigationMenuData";
|
||||||
import useLibraryHeaderTitle from "ee/pages/Editor/IDE/Header/useLibraryHeaderTitle";
|
import useLibraryHeaderTitle from "ee/pages/Editor/IDE/Header/useLibraryHeaderTitle";
|
||||||
import { AppsmithLink } from "pages/Editor/AppsmithLink";
|
|
||||||
|
|
||||||
const StyledDivider = styled(Divider)`
|
const StyledDivider = styled(Divider)`
|
||||||
height: 50%;
|
height: 50%;
|
||||||
|
|
@ -90,9 +89,10 @@ const { cloudHosting } = getAppsmithConfigs();
|
||||||
|
|
||||||
interface HeaderTitleProps {
|
interface HeaderTitleProps {
|
||||||
appState: EditorState;
|
appState: EditorState;
|
||||||
|
currentPage?: Page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HeaderTitleComponent = ({ appState }: HeaderTitleProps) => {
|
const HeaderTitleComponent = ({ appState, currentPage }: HeaderTitleProps) => {
|
||||||
const libraryHeaderTitle = useLibraryHeaderTitle();
|
const libraryHeaderTitle = useLibraryHeaderTitle();
|
||||||
|
|
||||||
switch (appState) {
|
switch (appState) {
|
||||||
|
|
@ -104,7 +104,7 @@ const HeaderTitleComponent = ({ appState }: HeaderTitleProps) => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case EditorState.EDITOR:
|
case EditorState.EDITOR:
|
||||||
return <EditorTitle key={appState} />;
|
return <EditorTitle key={appState} title={currentPage?.pageName || ""} />;
|
||||||
case EditorState.SETTINGS:
|
case EditorState.SETTINGS:
|
||||||
return (
|
return (
|
||||||
<IDEHeaderTitle
|
<IDEHeaderTitle
|
||||||
|
|
@ -115,7 +115,7 @@ const HeaderTitleComponent = ({ appState }: HeaderTitleProps) => {
|
||||||
case EditorState.LIBRARIES:
|
case EditorState.LIBRARIES:
|
||||||
return <IDEHeaderTitle key={appState} title={libraryHeaderTitle} />;
|
return <IDEHeaderTitle key={appState} title={libraryHeaderTitle} />;
|
||||||
default:
|
default:
|
||||||
return <EditorTitle key={appState} />;
|
return <EditorTitle key={appState} title={currentPage?.pageName || ""} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -218,8 +218,8 @@ const Header = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IDEHeader>
|
<IDEHeader>
|
||||||
<IDEHeader.Left logo={<AppsmithLink />}>
|
<IDEHeader.Left>
|
||||||
<HeaderTitleComponent appState={appState} />
|
<HeaderTitleComponent appState={appState} currentPage={currentPage} />
|
||||||
<EditorSaveIndicator isSaving={isSaving} saveError={pageSaveError} />
|
<EditorSaveIndicator isSaving={isSaving} saveError={pageSaveError} />
|
||||||
</IDEHeader.Left>
|
</IDEHeader.Left>
|
||||||
<IDEHeader.Center>
|
<IDEHeader.Center>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
||||||
RUN_GUTTER_ID,
|
RUN_GUTTER_ID,
|
||||||
} from "./constants";
|
} from "./constants";
|
||||||
import { thinScrollbar } from "constants/DefaultTheme";
|
import { thinScrollbar } from "constants/DefaultTheme";
|
||||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||||
|
|
||||||
export const CodeEditorWithGutterStyles = css`
|
export const CodeEditorWithGutterStyles = css`
|
||||||
.${RUN_GUTTER_ID} {
|
.${RUN_GUTTER_ID} {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { getIsAppSettingsPaneWithNavigationTabOpen } from "selectors/appSettings
|
||||||
import { EditorState } from "ee/entities/IDE/constants";
|
import { EditorState } from "ee/entities/IDE/constants";
|
||||||
import { RenderModes } from "constants/WidgetConstants";
|
import { RenderModes } from "constants/WidgetConstants";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||||
import { BOTTOM_BAR_HEIGHT } from "components/BottomBar/constants";
|
import { BOTTOM_BAR_HEIGHT } from "components/BottomBar/constants";
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { Profile } from "pages/common/ProfileImage";
|
import { Profile } from "pages/common/ProfileImage";
|
||||||
import { getTypographyByKey } from "@appsmith/ads-old";
|
import { getTypographyByKey } from "@appsmith/ads-old";
|
||||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||||
|
|
||||||
export const HeaderWrapper = styled.div`
|
export const HeaderWrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import classNames from "classnames";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { combinedPreviewModeSelector } from "../../../selectors/editorSelectors";
|
import { combinedPreviewModeSelector } from "../../../selectors/editorSelectors";
|
||||||
import { protectedModeSelector } from "selectors/gitSyncSelectors";
|
import { protectedModeSelector } from "selectors/gitSyncSelectors";
|
||||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { IDE_HEADER_HEIGHT } from "../../../IDE";
|
||||||
import { BOTTOM_BAR_HEIGHT } from "../../../components/BottomBar/constants";
|
import { BOTTOM_BAR_HEIGHT } from "../../../components/BottomBar/constants";
|
||||||
import { PROTECTED_CALLOUT_HEIGHT } from "../IDE/ProtectedCallout";
|
import { PROTECTED_CALLOUT_HEIGHT } from "../IDE/ProtectedCallout";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import styled from "styled-components";
|
||||||
import { snipingModeSelector } from "selectors/editorSelectors";
|
import { snipingModeSelector } from "selectors/editorSelectors";
|
||||||
import { retryPromise } from "utils/AppsmithUtils";
|
import { retryPromise } from "utils/AppsmithUtils";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||||
|
|
||||||
const BindingBanner = styled.div`
|
const BindingBanner = styled.div`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ import { PartialExportModal } from "components/editorComponents/PartialImportExp
|
||||||
import { PartialImportModal } from "components/editorComponents/PartialImportExport/PartialImportModal";
|
import { PartialImportModal } from "components/editorComponents/PartialImportExport/PartialImportModal";
|
||||||
import type { Page } from "entities/Page";
|
import type { Page } from "entities/Page";
|
||||||
import { AppCURLImportModal } from "ee/pages/Editor/CurlImport";
|
import { AppCURLImportModal } from "ee/pages/Editor/CurlImport";
|
||||||
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
|
import { IDE_HEADER_HEIGHT } from "IDE";
|
||||||
import GeneratePageModal from "./GeneratePage";
|
import GeneratePageModal from "./GeneratePage";
|
||||||
|
|
||||||
interface EditorProps {
|
interface EditorProps {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user