-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add application layout components (#1090)
- Loading branch information
Showing
30 changed files
with
1,442 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,10 @@ | |
"version": "0.47.3", | ||
"main": "dist/index.js", | ||
"module": "dist/index.js", | ||
"author": "Huw Wilkins <[email protected]>", | ||
"author": { | ||
"email": "[email protected]", | ||
"name": "Canonical Webteam" | ||
}, | ||
"license": "LGPL-3.0", | ||
"files": [ | ||
"dist/**/*.js", | ||
|
139 changes: 139 additions & 0 deletions
139
src/components/ApplicationLayout/AppAside/AppAside.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
/* eslint-disable react-hooks/rules-of-hooks */ | ||
import React, { useState } from "react"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
|
||
import Application from "components/ApplicationLayout/Application"; | ||
import AppMain from "components/ApplicationLayout/AppMain"; | ||
import Button from "components/Button"; | ||
import Col from "components/Col"; | ||
import Form from "components/Form"; | ||
import Icon from "components/Icon"; | ||
import Input from "components/Input"; | ||
import Panel from "components/Panel"; | ||
import Row from "components/Row"; | ||
|
||
import AppAside from "./AppAside"; | ||
|
||
const meta: Meta<typeof AppAside> = { | ||
component: AppAside, | ||
tags: ["autodocs"], | ||
argTypes: { | ||
children: { | ||
control: false, | ||
}, | ||
}, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof AppAside>; | ||
|
||
/** | ||
* In most common cases an `AppAside` should contain a `<Panel>` to display the | ||
* content as intended in the application layout. | ||
* | ||
* `AppAside` should be a direct child of an `<Application>` or passed to the | ||
* application layout `<ApplicationLayout aside={<AppAside .../>}>`. | ||
*/ | ||
export const Default: Story = { | ||
render: (args) => { | ||
const [pinned, setPinned] = useState(false); | ||
const [width, setWidth] = useState(null); | ||
const [collapsed, setCollapsed] = useState(false); | ||
return ( | ||
<Application> | ||
<AppMain> | ||
<p>Scroll to the right to see the panel.</p> | ||
<Button onClick={() => setCollapsed(false)}>Open</Button> | ||
<Button onClick={() => setWidth("narrow")}>Narrow</Button> | ||
<Button onClick={() => setWidth(null)}>Default</Button> | ||
<Button onClick={() => setWidth("wide")}>Wide</Button> | ||
</AppMain> | ||
<AppAside | ||
{...args} | ||
pinned={pinned} | ||
wide={width === "wide"} | ||
narrow={width === "narrow"} | ||
collapsed={collapsed} | ||
> | ||
<Panel | ||
controls={ | ||
<> | ||
<Button | ||
onClick={() => setPinned(!pinned)} | ||
dense | ||
className="u-no-margin--bottom" | ||
> | ||
Pin | ||
</Button> | ||
<Button | ||
appearance="base" | ||
className="u-no-margin--bottom" | ||
hasIcon | ||
onClick={() => setCollapsed(!collapsed)} | ||
> | ||
<Icon name="close">Close</Icon> | ||
</Button> | ||
</> | ||
} | ||
title="App aside" | ||
> | ||
<Form stacked> | ||
<Input | ||
label="Full name" | ||
type="text" | ||
name="fullName" | ||
autoComplete="name" | ||
stacked | ||
/> | ||
<Input | ||
label="Username" | ||
type="text" | ||
name="username-stacked" | ||
autoComplete="username" | ||
aria-describedby="exampleHelpTextMessage" | ||
stacked | ||
help="30 characters or fewer." | ||
/> | ||
<Input | ||
type="text" | ||
label="Email address" | ||
aria-invalid="true" | ||
name="username-stackederror" | ||
autoComplete="email" | ||
required | ||
error="This field is required." | ||
stacked | ||
/> | ||
<Input | ||
label="Address line 1" | ||
type="text" | ||
name="address-optional-stacked" | ||
autoComplete="address-line1" | ||
stacked | ||
/> | ||
<Input | ||
label="Address line 2" | ||
type="text" | ||
name="address-optional-stacked" | ||
autoComplete="address-line3" | ||
stacked | ||
/> | ||
<Row> | ||
<Col size={12}> | ||
<Button | ||
appearance="positive" | ||
className="u-float-right" | ||
name="add-details" | ||
> | ||
Add details | ||
</Button> | ||
</Col> | ||
</Row> | ||
</Form> | ||
</Panel> | ||
</AppAside> | ||
</Application> | ||
); | ||
}, | ||
}; |
24 changes: 24 additions & 0 deletions
24
src/components/ApplicationLayout/AppAside/AppAside.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import React from "react"; | ||
import { render, screen } from "@testing-library/react"; | ||
|
||
import AppAside from "./AppAside"; | ||
|
||
it("displays collapsed", async () => { | ||
render(<AppAside collapsed>Content</AppAside>); | ||
expect(screen.queryByRole("complementary")).toHaveClass("is-collapsed"); | ||
}); | ||
|
||
it("displays as narrow", async () => { | ||
render(<AppAside narrow>Content</AppAside>); | ||
expect(screen.queryByRole("complementary")).toHaveClass("is-narrow"); | ||
}); | ||
|
||
it("displays pinned", async () => { | ||
render(<AppAside pinned>Content</AppAside>); | ||
expect(screen.queryByRole("complementary")).toHaveClass("is-pinned"); | ||
}); | ||
|
||
it("displays as wide", async () => { | ||
render(<AppAside wide>Content</AppAside>); | ||
expect(screen.queryByRole("complementary")).toHaveClass("is-wide"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import React from "react"; | ||
import type { PropsWithSpread } from "types"; | ||
import classNames from "classnames"; | ||
import { type HTMLProps, type PropsWithChildren } from "react"; | ||
|
||
export type Props = PropsWithSpread< | ||
{ | ||
/** | ||
* Whether the aside panel should be collapsed. Toggling this state will animate | ||
* the panel open or closed. | ||
*/ | ||
collapsed?: boolean; | ||
/** | ||
* The panel content. | ||
*/ | ||
children?: PropsWithChildren["children"]; | ||
/** | ||
* A ref that will be passed to the wrapping `<aside>` element. | ||
*/ | ||
forwardRef?: React.Ref<HTMLElement> | null; | ||
/** | ||
* Whether the aside panel should be narrow. | ||
*/ | ||
narrow?: boolean; | ||
/** | ||
* Whether the aside panel should be pinned. When pinned the panel will appear | ||
* beside the main content, instead of above it. | ||
*/ | ||
pinned?: boolean; | ||
/** | ||
* Whether the aside panel should be wide. | ||
*/ | ||
wide?: boolean; | ||
}, | ||
HTMLProps<HTMLElement> | ||
>; | ||
|
||
const AppAside = ({ | ||
children, | ||
className, | ||
collapsed, | ||
narrow, | ||
forwardRef, | ||
pinned, | ||
wide, | ||
...props | ||
}: Props) => { | ||
return ( | ||
<aside | ||
className={classNames("l-aside", className, { | ||
"is-collapsed": collapsed, | ||
"is-narrow": narrow, | ||
"is-pinned": pinned, | ||
"is-wide": wide, | ||
})} | ||
{...props} | ||
ref={forwardRef} | ||
> | ||
{children} | ||
</aside> | ||
); | ||
}; | ||
export default AppAside; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default } from "./AppAside"; | ||
export type { Props as AppAsideProps } from "./AppAside"; |
65 changes: 65 additions & 0 deletions
65
src/components/ApplicationLayout/AppMain/AppMain.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
|
||
import Application from "components/ApplicationLayout/Application"; | ||
import Button from "components/Button"; | ||
import React from "react"; | ||
import Panel from "components/Panel"; | ||
|
||
import AppMain from "./AppMain"; | ||
|
||
const meta: Meta<typeof AppMain> = { | ||
component: AppMain, | ||
tags: ["autodocs"], | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof AppMain>; | ||
|
||
/** | ||
* In most common cases an `AppMain` should contain a `<Panel>` to display the | ||
* content as intended in the application layout. | ||
* | ||
* `AppMain` should be a direct child of an `<Application>` or when using `ApplicationLayout` | ||
* it will automatically wrap the component's children. | ||
*/ | ||
export const Default: Story = { | ||
args: { | ||
children: "AppMain", | ||
}, | ||
}; | ||
|
||
export const Content: Story = { | ||
render: (args) => { | ||
return ( | ||
<Application> | ||
<AppMain {...args}> | ||
<Panel | ||
controls={ | ||
<> | ||
<Button | ||
appearance="positive" | ||
onClick={() => {}} | ||
className="u-no-margin--bottom" | ||
> | ||
Add | ||
</Button> | ||
<Button | ||
appearance="negative" | ||
onClick={() => {}} | ||
className="u-no-margin--bottom" | ||
> | ||
Delete | ||
</Button> | ||
</> | ||
} | ||
title="App main" | ||
> | ||
<p>App main content.</p> | ||
<p>Scroll to the right to see the controls.</p> | ||
</Panel> | ||
</AppMain> | ||
</Application> | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import React from "react"; | ||
import { render, screen } from "@testing-library/react"; | ||
|
||
import AppMain from "./AppMain"; | ||
|
||
it("displays children", () => { | ||
const children = "Test content"; | ||
render(<AppMain>{children}</AppMain>); | ||
expect(screen.getByText(children)).toBeInTheDocument(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import React from "react"; | ||
import classNames from "classnames"; | ||
import type { HTMLProps, PropsWithChildren } from "react"; | ||
|
||
export type Props = { | ||
/** | ||
* The main content. | ||
*/ | ||
children?: PropsWithChildren["children"]; | ||
} & HTMLProps<HTMLDivElement>; | ||
|
||
/** | ||
* This is a [React](https://reactjs.org/) component for main content area in the Vanilla | ||
* [Application Layout](https://vanillaframework.io/docs/layouts/application). | ||
*/ | ||
const AppMain = ({ children, className, ...props }: Props) => { | ||
return ( | ||
<main className={classNames("l-main", className)} {...props}> | ||
{children} | ||
</main> | ||
); | ||
}; | ||
|
||
export default AppMain; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default } from "./AppMain"; | ||
export type { Props as AppMainProps } from "./AppMain"; |
Oops, something went wrong.