Skip to content

Commit a1371db

Browse files
authored
feat(site): move template's readme to its own tab (coder#6863)
* feat(site): display template's readme first on template page * chore: prettier * move readme to a new docs tab * test * prettier * fix tests * prettier
1 parent 95e578b commit a1371db

File tree

6 files changed

+114
-16
lines changed

6 files changed

+114
-16
lines changed

site/src/AppRouter.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,15 @@ const WorkspaceSettingsPage = lazy(
132132
const CreateTokenPage = lazy(
133133
() => import("./pages/CreateTokenPage/CreateTokenPage"),
134134
)
135+
136+
const TemplateDocsPage = lazy(
137+
() => import("./pages/TemplatePage/TemplateDocsPage/TemplateDocsPage"),
138+
)
139+
135140
const TemplateFilesPage = lazy(
136141
() => import("./pages/TemplatePage/TemplateFilesPage/TemplateFilesPage"),
137142
)
143+
138144
const TemplateVersionsPage = lazy(
139145
() =>
140146
import("./pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage"),
@@ -174,6 +180,7 @@ export const AppRouter: FC = () => {
174180
<Route path=":template">
175181
<Route element={<TemplateLayout />}>
176182
<Route index element={<TemplateSummaryPage />} />
183+
<Route path="docs" element={<TemplateDocsPage />} />
177184
<Route path="files" element={<TemplateFilesPage />} />
178185
<Route path="versions" element={<TemplateVersionsPage />} />
179186
</Route>

site/src/components/TemplateLayout/TemplateLayout.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,18 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
110110
>
111111
Summary
112112
</NavLink>
113+
<NavLink
114+
end
115+
to={`/templates/${templateName}/docs`}
116+
className={({ isActive }) =>
117+
combineClasses([
118+
styles.tabItem,
119+
isActive ? styles.tabItemActive : undefined,
120+
])
121+
}
122+
>
123+
Docs
124+
</NavLink>
113125
{data.permissions.canUpdateTemplate && (
114126
<NavLink
115127
to={`/templates/${templateName}/files`}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { screen } from "@testing-library/react"
2+
import { TemplateLayout } from "components/TemplateLayout/TemplateLayout"
3+
import { ResizeObserver } from "resize-observer"
4+
import { renderWithAuth } from "testHelpers/renderHelpers"
5+
import TemplateDocsPage from "./TemplateDocsPage"
6+
7+
jest.mock("remark-gfm", () => jest.fn())
8+
9+
const TEMPLATE_NAME = "coder-ts"
10+
11+
Object.defineProperty(window, "ResizeObserver", {
12+
value: ResizeObserver,
13+
})
14+
15+
const renderPage = () =>
16+
renderWithAuth(
17+
<TemplateLayout>
18+
<TemplateDocsPage />
19+
</TemplateLayout>,
20+
{
21+
route: `/templates/${TEMPLATE_NAME}/docs`,
22+
path: "/templates/:template/docs",
23+
},
24+
)
25+
26+
describe("TemplateSummaryPage", () => {
27+
it("shows the template readme", async () => {
28+
renderPage()
29+
await screen.findByTestId("markdown")
30+
})
31+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import { MemoizedMarkdown } from "components/Markdown/Markdown"
3+
import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout"
4+
import frontMatter from "front-matter"
5+
import { Helmet } from "react-helmet-async"
6+
import { pageTitle } from "util/page"
7+
8+
export default function TemplateDocsPage() {
9+
const { template, activeVersion } = useTemplateLayoutContext()
10+
const styles = useStyles()
11+
12+
const readme = frontMatter(activeVersion.readme)
13+
14+
return (
15+
<>
16+
<Helmet>
17+
<title>{pageTitle(`${template.name} · Documentation`)}</title>
18+
</Helmet>
19+
20+
<div className={styles.markdownSection} id="readme">
21+
<div className={styles.readmeLabel}>README.md</div>
22+
<div className={styles.markdownWrapper}>
23+
<MemoizedMarkdown>{readme.body}</MemoizedMarkdown>
24+
</div>
25+
</div>
26+
</>
27+
)
28+
}
29+
30+
export const useStyles = makeStyles((theme) => {
31+
return {
32+
markdownSection: {
33+
background: theme.palette.background.paper,
34+
border: `1px solid ${theme.palette.divider}`,
35+
borderRadius: theme.shape.borderRadius,
36+
},
37+
38+
readmeLabel: {
39+
color: theme.palette.text.secondary,
40+
fontWeight: 600,
41+
padding: theme.spacing(2, 3),
42+
borderBottom: `1px solid ${theme.palette.divider}`,
43+
},
44+
45+
markdownWrapper: {
46+
padding: theme.spacing(0, 3, 5),
47+
maxWidth: 800,
48+
margin: "auto",
49+
},
50+
}
51+
})

site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPage.test.tsx

+1-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { rest } from "msw"
44
import { ResizeObserver } from "resize-observer"
55
import {
66
MockTemplate,
7-
MockWorkspaceResource,
87
MockTemplateVersion,
98
MockMemberPermissions,
109
} from "testHelpers/entities"
@@ -31,15 +30,13 @@ const renderPage = () =>
3130
)
3231

3332
describe("TemplateSummaryPage", () => {
34-
it("shows the template name, readme and resources", async () => {
33+
it("shows the template name and resources", async () => {
3534
// Mocking the dayjs module within the createDayString file
3635
const mock = jest.spyOn(CreateDayString, "createDayString")
3736
mock.mockImplementation(() => "a minute ago")
3837

3938
renderPage()
4039
await screen.findByText(MockTemplate.display_name)
41-
await screen.findByTestId("markdown")
42-
screen.getByText(MockWorkspaceResource.name)
4340
screen.queryAllByText(`${MockTemplateVersion.name}`).length
4441
})
4542
it("does not allow a member to delete a template", () => {

site/src/pages/TemplatePage/TemplateSummaryPage/TemplateSummaryPageView.tsx

+12-12
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ import {
55
WorkspaceResource,
66
} from "api/typesGenerated"
77
import { Loader } from "components/Loader/Loader"
8-
import { MemoizedMarkdown } from "components/Markdown/Markdown"
98
import { Stack } from "components/Stack/Stack"
109
import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable"
1110
import { TemplateStats } from "components/TemplateStats/TemplateStats"
12-
import frontMatter from "front-matter"
13-
import { FC } from "react"
11+
import { FC, useEffect } from "react"
1412
import { DAUChart } from "../../../components/DAUChart/DAUChart"
1513
import { TemplateSummaryData } from "./data"
14+
import { useLocation, useNavigate } from "react-router-dom"
1615

1716
export interface TemplateSummaryPageViewProps {
1817
data?: TemplateSummaryData
@@ -25,14 +24,22 @@ export const TemplateSummaryPageView: FC<TemplateSummaryPageViewProps> = ({
2524
template,
2625
activeVersion,
2726
}) => {
28-
const styles = useStyles()
27+
const navigate = useNavigate()
28+
const location = useLocation()
29+
30+
useEffect(() => {
31+
if (location.hash === "#readme") {
32+
// We moved the readme to the docs page, but we known that some users
33+
// have bookmarked the readme or linked it elsewhere. Redirect them to the docs page.
34+
navigate(`/templates/${template.name}/docs`, { replace: true })
35+
}
36+
}, [template, navigate, location])
2937

3038
if (!data) {
3139
return <Loader />
3240
}
3341

3442
const { daus, resources } = data
35-
const readme = frontMatter(activeVersion.readme)
3643

3744
const getStartedResources = (resources: WorkspaceResource[]) => {
3845
return resources.filter(
@@ -45,13 +52,6 @@ export const TemplateSummaryPageView: FC<TemplateSummaryPageViewProps> = ({
4552
<TemplateStats template={template} activeVersion={activeVersion} />
4653
{daus && <DAUChart daus={daus} />}
4754
<TemplateResourcesTable resources={getStartedResources(resources)} />
48-
49-
<div className={styles.markdownSection} id="readme">
50-
<div className={styles.readmeLabel}>README.md</div>
51-
<div className={styles.markdownWrapper}>
52-
<MemoizedMarkdown>{readme.body}</MemoizedMarkdown>
53-
</div>
54-
</div>
5555
</Stack>
5656
)
5757
}

0 commit comments

Comments
 (0)