Skip to content

Commit cea2a5a

Browse files
authored
feat: add the guide for page layouts (#708)
1 parent 17ac95d commit cea2a5a

28 files changed

+248
-74
lines changed

i18n/en/docusaurus-plugin-content-docs/current/guides/examples/autocompleted.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
sidebar_position: 5
33
sidebar_class_name: sidebar-item--wip
4+
unlisted: true
45
---
56

67
import WIP from '@site/src/shared/ui/wip/tmpl.mdx'
@@ -14,4 +15,4 @@ import WIP from '@site/src/shared/ui/wip/tmpl.mdx'
1415
> About decomposition by layers
1516
1617
## See also
17-
- [(Discussion) About the application of the methodology for the selection with loaded dictionaries](https://github.com/feature-sliced/documentation/discussions/65#discussioncomment-480807)
18+
- [(Discussion) About the application of the methodology for the selection with loaded dictionaries](https://github.com/feature-sliced/documentation/discussions/65#discussioncomment-480807)

i18n/en/docusaurus-plugin-content-docs/current/guides/examples/browser-api.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
sidebar_class_name: sidebar-item--wip
3+
unlisted: true
34
---
45

56
import WIP from '@site/src/shared/ui/wip/tmpl.mdx'

i18n/en/docusaurus-plugin-content-docs/current/guides/examples/cms.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
sidebar_class_name: sidebar-item--wip
3+
unlisted: true
34
---
45

56
import WIP from '@site/src/shared/ui/wip/tmpl.mdx'
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
sidebar_class_name: sidebar-item--wip
3+
unlisted: true
34
---
45

56
import WIP from '@site/src/shared/ui/wip/tmpl.mdx'
@@ -8,4 +9,4 @@ import WIP from '@site/src/shared/ui/wip/tmpl.mdx'
89

910
<WIP ticket="187" />
1011

11-
> Errors, Alerts, Notifications, ...
12+
> Errors, Alerts, Notifications, ...

i18n/en/docusaurus-plugin-content-docs/current/guides/examples/i18n.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
sidebar_position: 6
33
sidebar_class_name: sidebar-item--wip
4+
unlisted: true
45
---
56

67
import WIP from '@site/src/shared/ui/wip/tmpl.mdx'

i18n/en/docusaurus-plugin-content-docs/current/guides/examples/index.mdx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@ import { UserSwitchOutlined, LayoutOutlined, FontSizeOutlined } from "@ant-desig
2020
Icon={UserSwitchOutlined}
2121
tags={["Forms", "2FA", "OAuth", "Token storage", "Token refresh"]}
2222
/>
23-
<NavCard
24-
title="PageLayout"
25-
description="Main cases with page layout widgets"
26-
to="/docs/guides/examples/page-layout"
27-
Icon={LayoutOutlined}
28-
tags={["Header", "Sidebar"]}
29-
/>
3023
<NavCard
3124
title="Types"
32-
description="Where should we locate types? Which kind of types exists in the context of FSD?"
25+
description="Where to keep the types? What kinds of types are there in the context of FSD?"
3326
to="/docs/guides/examples/types"
3427
Icon={FontSizeOutlined}
28+
tags={["DTO", "Mappers", "Entity relationships", "Auto-generation", "Validation schemas"]}
29+
/>
30+
<NavCard
31+
title="Page layouts"
32+
description="Main cases with layouts"
33+
to="/docs/guides/examples/page-layout"
34+
Icon={LayoutOutlined}
35+
tags={["Where to store them", "Using widgets in layouts"]}
3536
/>

i18n/en/docusaurus-plugin-content-docs/current/guides/examples/metric.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
sidebar_class_name: sidebar-item--wip
3+
unlisted: true
34
---
45

56
import WIP from '@site/src/shared/ui/wip/tmpl.mdx'

i18n/en/docusaurus-plugin-content-docs/current/guides/examples/monorepo.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
sidebar_position: 9
33
sidebar_class_name: sidebar-item--wip
4+
unlisted: true
45
---
56

67
import WIP from '@site/src/shared/ui/wip/tmpl.mdx'
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
sidebar_position: 3
3+
---
4+
5+
# Page layouts
6+
7+
This guide examines the abstraction of a _page layout_ — when several pages share the same overall structure, and differ only in the main content.
8+
9+
:::info
10+
11+
Is your question not covered by this guide? Post your question by leaving feedback on this article (blue button on the right) and we will consider expanding this guide!
12+
13+
:::
14+
15+
## Simple layout
16+
17+
The simplest layout can be seen on this page. It has a header with site navigation, two sidebars, and a footer with external links. There is no complicated business logic, and the only dynamic parts are sidebars and the switchers on the right side of the header. Such a layout can be placed entirely in `shared/ui` or in `app/layouts`, with props filling in the content for the sidebars:
18+
19+
```tsx title="shared/ui/layout/Layout.tsx"
20+
import { Link, Outlet } from "react-router-dom";
21+
import { useThemeSwitcher } from "./useThemeSwitcher";
22+
23+
export function Layout({ siblingPages, headings }) {
24+
const [theme, toggleTheme] = useThemeSwitcher();
25+
26+
return (
27+
<div>
28+
<header>
29+
<nav>
30+
<ul>
31+
<li> <Link to="/">Home</Link> </li>
32+
<li> <Link to="/docs">Docs</Link> </li>
33+
<li> <Link to="/blog">Blog</Link> </li>
34+
</ul>
35+
</nav>
36+
<button onClick={toggleTheme}>{theme}</button>
37+
</header>
38+
<main>
39+
<SiblingPageSidebar siblingPages={siblingPages} />
40+
<Outlet /> {/* This is where the main content goes */}
41+
<HeadingsSidebar headings={headings} />
42+
</main>
43+
<footer>
44+
<ul>
45+
<li>GitHub</li>
46+
<li>Twitter</li>
47+
</ul>
48+
</footer>
49+
</div>
50+
);
51+
}
52+
```
53+
54+
```ts title="shared/ui/layout/useThemeSwitcher.ts"
55+
export function useThemeSwitcher() {
56+
const [theme, setTheme] = useState("light");
57+
58+
function toggleTheme() {
59+
setTheme(theme === "light" ? "dark" : "light");
60+
}
61+
62+
useEffect(() => {
63+
document.body.classList.remove("light", "dark");
64+
document.body.classList.add(theme);
65+
}, [theme]);
66+
67+
return [theme, toggleTheme] as const;
68+
}
69+
```
70+
71+
The code of sidebars is left as an exercise for the reader 😉.
72+
73+
## Using widgets in the layout
74+
75+
Sometimes you want to include certain business logic in the layout, especially if you're using deeply nested routes with a router like [React Router][ext-react-router]. Then you can't store the layout in Shared or in Widgets due to [the import rule on layers][import-rule-on-layers]:
76+
77+
> A module in a slice can only import other slices when they are located on layers strictly below.
78+
79+
Before we discuss solutions, we need to discuss if it's even a problem in the first place. Do you _really need_ that layout, and if so, does it _really need_ to be a Widget? If the block of business logic in question is reused on 2-3 pages, and the layout is simply a small wrapper for that widget, consider one of these two options:
80+
81+
1. **Write the layout inline on the App layer, where you configure the routing**
82+
This is great for routers that support nesting, because you can group certain routes and apply the layout only to them.
83+
84+
2. **Just copy-paste it**
85+
The urge to abstract code is often very overrated. It is especially the case for layouts, which rarely change. At some point, if one of these pages will need to change, you can simply do the change without needlessly affecting other pages. If you're worried that someone might forget to update the other pages, you can always leave a comment that describes the relationship between the pages.
86+
87+
If none of the above are applicable, there are two solutions to include a widget in the layout:
88+
89+
1. **Use render props or slots**
90+
Most frameworks allow you to pass a piece of UI externally. In React, it's called [render props][ext-render-props], in Vue it's called [slots][ext-vue-slots].
91+
2. **Move the layout to the App layer**
92+
You can also store your layout on the App layer, for example, in `app/layouts`, and compose any widgets you want.
93+
94+
## Further reading
95+
96+
- There's an example of how to build a layout with authentication with React and Remix (equivalent to React Router) in the [tutorial][tutorial].
97+
98+
[tutorial]: /docs/get-started/tutorial
99+
[import-rule-on-layers]: /docs/reference/layers#import-rule-on-layers
100+
[ext-react-router]: https://reactrouter.com/
101+
[ext-render-props]: https://www.patterns.dev/react/render-props-pattern/
102+
[ext-vue-slots]: https://vuejs.org/guide/components/slots

i18n/en/docusaurus-plugin-content-docs/current/guides/examples/page-layout.mdx

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)