Skip to content

Commit

Permalink
chore: Move IDE header to ADS/Templates (appsmithorg#37764)
Browse files Browse the repository at this point in the history
## 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 appsmithorg#37607 

## Automation

/ok-to-test tags="@tag.Sanity"


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

- **New Features**
- Introduced `ListWithHeader` component for improved layout in the
entity explorer.
- Added `IDEHeader` component with subcomponents for better header
organization.
- Implemented `IDEHeaderSwitcher` for enhanced navigation options in the
header.
- Added styled components `ListItemContainer` and `ListHeaderContainer`
for consistent design.
  
- **Bug Fixes**
- Updated import paths for `IDE_HEADER_HEIGHT` to ensure consistent
usage across components.

- **Documentation**
- Added comprehensive documentation for the `IDEHeader` component to aid
developers.

- **Chores**
- Consolidated import statements for cleaner code structure across
various components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
hetunandu authored Nov 27, 2024
1 parent 5eee381 commit e6eda3a
Show file tree
Hide file tree
Showing 32 changed files with 494 additions and 388 deletions.
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"
>
<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>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ListItemContainer, ListHeaderContainer } from "./styles";
export { ListWithHeader } from "./ListWithHeader";
23 changes: 23 additions & 0 deletions packages/design-system/ads/src/Templates/EntityExplorer/styles.ts
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;
28 changes: 28 additions & 0 deletions packages/design-system/ads/src/Templates/IDEHeader/IDEHeader.mdx
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" />
</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

0 comments on commit e6eda3a

Please sign in to comment.