mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
feat(site): move template's readme to its own tab (#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
This commit is contained in:
@ -132,9 +132,15 @@ const WorkspaceSettingsPage = lazy(
|
||||
const CreateTokenPage = lazy(
|
||||
() => import("./pages/CreateTokenPage/CreateTokenPage"),
|
||||
)
|
||||
|
||||
const TemplateDocsPage = lazy(
|
||||
() => import("./pages/TemplatePage/TemplateDocsPage/TemplateDocsPage"),
|
||||
)
|
||||
|
||||
const TemplateFilesPage = lazy(
|
||||
() => import("./pages/TemplatePage/TemplateFilesPage/TemplateFilesPage"),
|
||||
)
|
||||
|
||||
const TemplateVersionsPage = lazy(
|
||||
() =>
|
||||
import("./pages/TemplatePage/TemplateVersionsPage/TemplateVersionsPage"),
|
||||
@ -174,6 +180,7 @@ export const AppRouter: FC = () => {
|
||||
<Route path=":template">
|
||||
<Route element={<TemplateLayout />}>
|
||||
<Route index element={<TemplateSummaryPage />} />
|
||||
<Route path="docs" element={<TemplateDocsPage />} />
|
||||
<Route path="files" element={<TemplateFilesPage />} />
|
||||
<Route path="versions" element={<TemplateVersionsPage />} />
|
||||
</Route>
|
||||
|
@ -110,6 +110,18 @@ export const TemplateLayout: FC<{ children?: JSX.Element }> = ({
|
||||
>
|
||||
Summary
|
||||
</NavLink>
|
||||
<NavLink
|
||||
end
|
||||
to={`/templates/${templateName}/docs`}
|
||||
className={({ isActive }) =>
|
||||
combineClasses([
|
||||
styles.tabItem,
|
||||
isActive ? styles.tabItemActive : undefined,
|
||||
])
|
||||
}
|
||||
>
|
||||
Docs
|
||||
</NavLink>
|
||||
{data.permissions.canUpdateTemplate && (
|
||||
<NavLink
|
||||
to={`/templates/${templateName}/files`}
|
||||
|
@ -0,0 +1,31 @@
|
||||
import { screen } from "@testing-library/react"
|
||||
import { TemplateLayout } from "components/TemplateLayout/TemplateLayout"
|
||||
import { ResizeObserver } from "resize-observer"
|
||||
import { renderWithAuth } from "testHelpers/renderHelpers"
|
||||
import TemplateDocsPage from "./TemplateDocsPage"
|
||||
|
||||
jest.mock("remark-gfm", () => jest.fn())
|
||||
|
||||
const TEMPLATE_NAME = "coder-ts"
|
||||
|
||||
Object.defineProperty(window, "ResizeObserver", {
|
||||
value: ResizeObserver,
|
||||
})
|
||||
|
||||
const renderPage = () =>
|
||||
renderWithAuth(
|
||||
<TemplateLayout>
|
||||
<TemplateDocsPage />
|
||||
</TemplateLayout>,
|
||||
{
|
||||
route: `/templates/${TEMPLATE_NAME}/docs`,
|
||||
path: "/templates/:template/docs",
|
||||
},
|
||||
)
|
||||
|
||||
describe("TemplateSummaryPage", () => {
|
||||
it("shows the template readme", async () => {
|
||||
renderPage()
|
||||
await screen.findByTestId("markdown")
|
||||
})
|
||||
})
|
@ -0,0 +1,51 @@
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { MemoizedMarkdown } from "components/Markdown/Markdown"
|
||||
import { useTemplateLayoutContext } from "components/TemplateLayout/TemplateLayout"
|
||||
import frontMatter from "front-matter"
|
||||
import { Helmet } from "react-helmet-async"
|
||||
import { pageTitle } from "util/page"
|
||||
|
||||
export default function TemplateDocsPage() {
|
||||
const { template, activeVersion } = useTemplateLayoutContext()
|
||||
const styles = useStyles()
|
||||
|
||||
const readme = frontMatter(activeVersion.readme)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{pageTitle(`${template.name} · Documentation`)}</title>
|
||||
</Helmet>
|
||||
|
||||
<div className={styles.markdownSection} id="readme">
|
||||
<div className={styles.readmeLabel}>README.md</div>
|
||||
<div className={styles.markdownWrapper}>
|
||||
<MemoizedMarkdown>{readme.body}</MemoizedMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const useStyles = makeStyles((theme) => {
|
||||
return {
|
||||
markdownSection: {
|
||||
background: theme.palette.background.paper,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
},
|
||||
|
||||
readmeLabel: {
|
||||
color: theme.palette.text.secondary,
|
||||
fontWeight: 600,
|
||||
padding: theme.spacing(2, 3),
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
|
||||
markdownWrapper: {
|
||||
padding: theme.spacing(0, 3, 5),
|
||||
maxWidth: 800,
|
||||
margin: "auto",
|
||||
},
|
||||
}
|
||||
})
|
@ -4,7 +4,6 @@ import { rest } from "msw"
|
||||
import { ResizeObserver } from "resize-observer"
|
||||
import {
|
||||
MockTemplate,
|
||||
MockWorkspaceResource,
|
||||
MockTemplateVersion,
|
||||
MockMemberPermissions,
|
||||
} from "testHelpers/entities"
|
||||
@ -31,15 +30,13 @@ const renderPage = () =>
|
||||
)
|
||||
|
||||
describe("TemplateSummaryPage", () => {
|
||||
it("shows the template name, readme and resources", async () => {
|
||||
it("shows the template name and resources", async () => {
|
||||
// Mocking the dayjs module within the createDayString file
|
||||
const mock = jest.spyOn(CreateDayString, "createDayString")
|
||||
mock.mockImplementation(() => "a minute ago")
|
||||
|
||||
renderPage()
|
||||
await screen.findByText(MockTemplate.display_name)
|
||||
await screen.findByTestId("markdown")
|
||||
screen.getByText(MockWorkspaceResource.name)
|
||||
screen.queryAllByText(`${MockTemplateVersion.name}`).length
|
||||
})
|
||||
it("does not allow a member to delete a template", () => {
|
||||
|
@ -5,14 +5,13 @@ import {
|
||||
WorkspaceResource,
|
||||
} from "api/typesGenerated"
|
||||
import { Loader } from "components/Loader/Loader"
|
||||
import { MemoizedMarkdown } from "components/Markdown/Markdown"
|
||||
import { Stack } from "components/Stack/Stack"
|
||||
import { TemplateResourcesTable } from "components/TemplateResourcesTable/TemplateResourcesTable"
|
||||
import { TemplateStats } from "components/TemplateStats/TemplateStats"
|
||||
import frontMatter from "front-matter"
|
||||
import { FC } from "react"
|
||||
import { FC, useEffect } from "react"
|
||||
import { DAUChart } from "../../../components/DAUChart/DAUChart"
|
||||
import { TemplateSummaryData } from "./data"
|
||||
import { useLocation, useNavigate } from "react-router-dom"
|
||||
|
||||
export interface TemplateSummaryPageViewProps {
|
||||
data?: TemplateSummaryData
|
||||
@ -25,14 +24,22 @@ export const TemplateSummaryPageView: FC<TemplateSummaryPageViewProps> = ({
|
||||
template,
|
||||
activeVersion,
|
||||
}) => {
|
||||
const styles = useStyles()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
||||
useEffect(() => {
|
||||
if (location.hash === "#readme") {
|
||||
// We moved the readme to the docs page, but we known that some users
|
||||
// have bookmarked the readme or linked it elsewhere. Redirect them to the docs page.
|
||||
navigate(`/templates/${template.name}/docs`, { replace: true })
|
||||
}
|
||||
}, [template, navigate, location])
|
||||
|
||||
if (!data) {
|
||||
return <Loader />
|
||||
}
|
||||
|
||||
const { daus, resources } = data
|
||||
const readme = frontMatter(activeVersion.readme)
|
||||
|
||||
const getStartedResources = (resources: WorkspaceResource[]) => {
|
||||
return resources.filter(
|
||||
@ -45,13 +52,6 @@ export const TemplateSummaryPageView: FC<TemplateSummaryPageViewProps> = ({
|
||||
<TemplateStats template={template} activeVersion={activeVersion} />
|
||||
{daus && <DAUChart daus={daus} />}
|
||||
<TemplateResourcesTable resources={getStartedResources(resources)} />
|
||||
|
||||
<div className={styles.markdownSection} id="readme">
|
||||
<div className={styles.readmeLabel}>README.md</div>
|
||||
<div className={styles.markdownWrapper}>
|
||||
<MemoizedMarkdown>{readme.body}</MemoizedMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
Reference in New Issue
Block a user