Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"
>
Comment thread
hetunandu marked this conversation as resolved.
<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>
);
};
Comment thread
hetunandu marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ListItemContainer, ListHeaderContainer } from "./styles";
export { ListWithHeader } from "./ListWithHeader";
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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;
}
`;

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;
}
`;
Original file line number Diff line number Diff line change
@@ -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;
`;
Original file line number Diff line number Diff line change
@@ -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";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { IDEHeaderSwitcher } from "./IDEHeaderSwitcher";
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const IDE_HEADER_HEIGHT = 40;
export const LOGO_WIDTH = 50;
Original file line number Diff line number Diff line change
@@ -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} />
Original file line number Diff line number Diff line change
@@ -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" />
Comment thread
hetunandu marked this conversation as resolved.
</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>
);
};
Loading