Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ apps/docs/functions/**/*.mjs
apps/docs/functions/**/*.jsx
apps/docs/__tests__/**/*.mjs
apps/docs/__tests__/**/*.jsx
apps/docs/test-utils/**/*.mjs
apps/docs/test-utils/**/*.jsx
apps/guide/__tests__/**/*.mjs
apps/guide/__tests__/**/*.jsx
apps/docs/e2e/**/*.mjs
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/__tests__/visual/BlogArticle_.test.res
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ test("desktop blog article with article image shows image", async () => {
await element(title)->toBeVisible

let wrapper = await screen->getByTestId("blog-article-wrapper")
await waitForImages("[data-testid='blog-article-wrapper']")
await TestUtils.waitForImages("[data-testid='blog-article-wrapper']")
await element(wrapper)->toMatchScreenshot("desktop-blog-article-with-image")
})

Expand Down
14 changes: 10 additions & 4 deletions apps/docs/__tests__/visual/LandingPage_.test.res
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
open ReactRouter
open Vitest

@module("vitest")
external testWithTimeout: (string, unit => promise<unit>, int) => unit = "test"

let snapshotSection = async (~width, ~height, ~sectionTestId, ~screenshotName) => {
await viewport(width, height)

Expand All @@ -20,6 +17,15 @@ let snapshotSection = async (~width, ~height, ~sectionTestId, ~screenshotName) =
| Null => failwith(`expected to find section ${sectionTestId}`)
}

if sectionTestId == "landing-other-selling-points" {
let sourceSelector = `[data-testid="${sectionTestId}"]`
await TestUtils.waitForImages(sourceSelector)
// Headless UI's appear transition mutates classes after first render. Since
// these tests snapshot a cloned outerHTML string, wait for the live section
// to settle so the clone does not preserve a transient opacity-0 state.
await TestUtils.sleep(1100)
}

let sandboxTestId = `${sectionTestId}-snapshot`
let snapshotHtml = sourceSection.outerHTML
await screen->unmount
Expand All @@ -34,7 +40,7 @@ let snapshotSection = async (~width, ~height, ~sectionTestId, ~screenshotName) =

let snapshotTarget = await snapshotScreen->getByTestId(sandboxTestId)
await element(snapshotTarget)->toBeVisible
await waitForImages(`[data-testid="${sandboxTestId}"]`)
await TestUtils.waitForImages(`[data-testid="${sandboxTestId}"]`)
await element(snapshotTarget)->toMatchScreenshot(screenshotName)
await snapshotScreen->unmount
}
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/__tests__/visual/MarkdownComponents_.test.res
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ test("renders Image with caption", async () => {
await element(caption)->toBeVisible

let wrapper = await screen->getByTestId("image-wrapper")
await waitForImages("[data-testid='image-wrapper']")
await TestUtils.waitForImages("[data-testid='image-wrapper']")
await element(wrapper)->toMatchScreenshot("markdown-image")
})

Expand Down Expand Up @@ -292,7 +292,7 @@ test("renders Image with small size", async () => {
await element(caption)->toBeVisible

let wrapper = await screen->getByTestId("image-small-wrapper")
await waitForImages("[data-testid='image-small-wrapper']")
await TestUtils.waitForImages("[data-testid='image-small-wrapper']")
await element(wrapper)->toMatchScreenshot("markdown-image-small")
})

Expand Down
3 changes: 3 additions & 0 deletions apps/docs/__tests__/visual/NavbarSecondary_.test.res
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ test("desktop secondary navbar shows all doc section links", async () => {

await element(await navbar->getByText("Language Manual"))->toBeVisible
await element(await navbar->getByText("API"))->toBeVisible
await element(await navbar->getByText("Guides"))->toBeVisible
await element(await navbar->getByText("Syntax Lookup"))->toBeVisible
await element(await navbar->getByText("React"))->toBeVisible

Expand All @@ -33,6 +34,7 @@ test("mobile secondary navbar shows all links", async () => {

await element(await navbar->getByText("Language Manual"))->toBeVisible
await element(await navbar->getByText("API"))->toBeVisible
await element(await navbar->getByText("Guides"))->toBeVisible
await element(await navbar->getByText("Syntax Lookup"))->toBeVisible
await element(await navbar->getByText("React"))->toBeVisible

Expand All @@ -52,6 +54,7 @@ test("secondary navbar highlights active section", async () => {

await element(await navbar->getByText("React"))->toBeVisible
await element(await navbar->getByText("Language Manual"))->toBeVisible
await element(await navbar->getByText("Guides"))->toBeVisible

await element(navbar)->toMatchScreenshot("desktop-navbar-secondary-react-active")
})
11 changes: 5 additions & 6 deletions apps/docs/app/DocsRoutes.res
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,10 @@ let docsReactRoutes =
route(path, "./routes/DocsReactRoute.jsx", ~options={id: path})
)

let docsGuidelinesRoutes =
MdxFile.scanPaths(
~dir="markdown-pages/docs/guidelines",
~alias="docs/guidelines",
)->Array.map(path => route(path, "./routes/DocsGuidelinesRoute.jsx", ~options={id: path}))
let docsGuidesRoutes =
MdxFile.scanPaths(~dir="markdown-pages/docs/guides", ~alias="docs/guides")->Array.map(path =>
route(path, "./routes/DocsGuidesRoute.jsx", ~options={id: path})
)

let communityRoutes =
MdxFile.scanPaths(~dir="markdown-pages/community", ~alias="community")->Array.map(path =>
Expand Down Expand Up @@ -95,8 +94,8 @@ let default = [
...beltRoutes,
...domRoutes,
...docsManualRoutes,
...docsGuidesRoutes,
...docsReactRoutes,
...docsGuidelinesRoutes,
route("syntax-lookup", "./routes/SyntaxLookupRoute.jsx", ~options={id: "syntax-lookup"}),
...syntaxLookupDetailRoutes,
],
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/app/DocsRoutes.resi
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ let beltRoutes: array<ReactRouter.Routes.t>
let blogArticleRoutes: array<ReactRouter.Routes.t>
let docsManualRoutes: array<ReactRouter.Routes.t>
let docsReactRoutes: array<ReactRouter.Routes.t>
let docsGuidelinesRoutes: array<ReactRouter.Routes.t>
let docsGuidesRoutes: array<ReactRouter.Routes.t>
let communityRoutes: array<ReactRouter.Routes.t>
let syntaxLookupDetailRoutes: array<ReactRouter.Routes.t>
let default: array<ReactRouter.Routes.t>
7 changes: 6 additions & 1 deletion apps/docs/app/layouts/DocsLayoutRoute.res
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
@react.component
let default = () => {
let location = ReactRouter.useLocation()

<>
<NavbarSecondary />
// This layout persists across docs route changes. Key the secondary nav by
// pathname so its scroll-direction state cannot keep the mobile nav hidden
// after client-side navigation.
<NavbarSecondary key={(location.pathname :> string)} />
<ReactRouter.Outlet />
</>
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
type loaderData = {
compiledMdx: CompiledMdx.t,
categories: array<SidebarNav.Category.t>,
entries: array<TableOfContents.entry>,
title: string,
description: string,
filePath: string,
}

let guidesTableOfContents = async () => {
let groups =
(await MdxFile.loadAllAttributes(~dir="markdown-pages/docs"))
->Mdx.filterMdxPages("docs/guides")
->Mdx.groupBySection
->Dict.mapValues(values =>
values->Mdx.sortSection->SidebarHelpers.convertToNavItems("/docs/guides")
)

SidebarHelpers.getAllGroups(groups, ["Overview", "Packages"])
}

let loader: ReactRouter.Loader.t<loaderData> = async ({request}) => {
let {pathname} = WebAPI.URL.make(~url=request.url)
let filePath = MdxFile.resolveFilePath(
(pathname :> string),
~dir="markdown-pages/docs/guidelines",
~alias="docs/guidelines",
~dir="markdown-pages/docs/guides",
~alias="docs/guides",
)

let raw = await Node.Fs.readFile(filePath, "utf-8")
Expand All @@ -20,37 +33,49 @@ let loader: ReactRouter.Loader.t<loaderData> = async ({request}) => {
let description = FrontmatterUtils.getField(frontmatter, "description")
let title = FrontmatterUtils.getField(frontmatter, "title")

let categories = await guidesTableOfContents()

let compiledMdx = await MdxFile.compileMdx(raw, ~filePath, ~remarkPlugins=Mdx.plugins)

let entries = TocUtils.buildEntries(raw)

{
compiledMdx,
categories,
entries,
title: `${title} | ReScript Guidelines`,
title: `${title} | ReScript Guides`,
description,
filePath,
}
}

let default = () => {
let {compiledMdx, entries, title, description, filePath} = ReactRouter.useLoaderData()
let {compiledMdx, categories, entries, title, description, filePath} = ReactRouter.useLoaderData()

let breadcrumbs = list{
{Url.name: "Docs", href: "/docs/guides/overview"},
{
Url.name: "Guides",
href: "/docs/guides/overview",
},
}

let docsAppRoot = "apps/docs"
let editHref = `https://github.com/rescript-lang/rescript-lang.org/blob/master/${docsAppRoot}/${filePath}`

let categories: array<SidebarNav.Category.t> = []
let activeToc = {TableOfContents.title, entries}

<>
<Meta title description />
<NavbarTertiary>
<NavbarTertiary sidebar={<DocsSidebar categories activeToc />}>
<Breadcrumbs crumbs=breadcrumbs />
<a
href=editHref className="inline text-14 hover:underline text-fire" rel="noopener noreferrer"
>
{React.string("Edit")}
</a>
</NavbarTertiary>
<DocsLayout categories activeToc={title, entries} docSearchLvl0="Guidelines">
<DocsLayout categories activeToc docSearchLvl0="Guides">
<div className="markdown-body">
<MdxContent compiledMdx />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
type loaderData = {
compiledMdx: CompiledMdx.t,
categories: array<SidebarNav.Category.t>,
entries: array<TableOfContents.entry>,
title: string,
description: string,
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/app/routes/Packages.res
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,11 @@ module InfoSidebar = {
<div>
<h2 className=h2> {React.string("Guidelines")} </h2>
<ul className="space-y-4">
<ReactRouter.Link to=#"/docs/guidelines/publishing-packages" className=link>
<ReactRouter.Link to=#"/docs/guides/publishing-packages" className=link>
{React.string("Publishing ReScript npm packages")}
</ReactRouter.Link>
/* <li> */
/* <Next.Link href="/docs/guidelines/writing-bindings" className=link> */
/* <Next.Link href="/docs/guides/writing-bindings" className=link> */
/* {React.string("Writing Bindings & Libraries")} */
/* </Next.Link> */
/* </li> */
Expand Down
11 changes: 11 additions & 0 deletions apps/docs/e2e/Navigation.cy.res
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ describe("Desktop Navigation", () => {
clickNavLink(~testId="navbar-secondary", ~text="API")
url()->shouldInclude("/docs/manual/api")->ignore

// Guides
clickNavLink(~testId="navbar-secondary", ~text="Guides")
url()->shouldInclude("/docs/guides/overview")->ignore
get("h1")->shouldContainText("Guides")->ignore

// Syntax Lookup
clickNavLink(~testId="navbar-secondary", ~text="Syntax Lookup")
url()->shouldInclude("/syntax-lookup")->ignore
Expand Down Expand Up @@ -198,6 +203,12 @@ describe("Mobile Navigation", () => {
clickNavLink(~testId="navbar-secondary", ~text="API")
url()->shouldInclude("/docs/manual/api")->ignore

// Guides
cyScrollTo("top")
clickNavLink(~testId="navbar-secondary", ~text="Guides")
url()->shouldInclude("/docs/guides/overview")->ignore
get("h1")->shouldContainText("Guides")->ignore

// Syntax Lookup
cyScrollTo("top")
clickNavLink(~testId="navbar-secondary", ~text="Syntax Lookup")
Expand Down
28 changes: 28 additions & 0 deletions apps/docs/markdown-pages/docs/guides/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: "Guides"
description: "Task-oriented ReScript guides"
canonical: "/docs/guides/overview"
section: "Overview"
order: 1
---

# Guides

Task-oriented guides collect workflows that cut across the language manual, API reference, and framework-specific docs.

## Start Here

- [ReScript for JavaScript Developers](../manual/rescript-for-javascript-developers.mdx)
- [Converting from JS](../manual/converting-from-js.mdx)
- [Project Structure](../manual/project-structure.mdx)
- [Dead Code Analysis in ReScript](../manual/editor-code-analysis.mdx)

## React

- [Beyond JSX](../react/beyond-jsx.mdx)
- [Extensions of Props](../react/extensions-of-props.mdx)
- [Forwarding Refs](../react/forwarding-refs.mdx)

## Packages

- [Publishing ReScript packages](./publishing-packages.mdx)
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
---
title: "Publishing ReScript packages"
description: "Guidelines on publishing ReScript bindings and libraries to our Package Index"
canonical: "/docs/guidelines/publishing-packages"
canonical: "/docs/guides/publishing-packages"
section: "Packages"
order: 1
---

[Back to packages](/packages)
Expand Down
1 change: 1 addition & 0 deletions apps/docs/public/_redirects
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/community /community/overview 308
/bucklescript-rebranding /blog/bucklescript-is-rebranding 308
/docs/guidelines/publishing-packages /docs/guides/publishing-packages 308

/docs/manual/v12.0.0/* /docs/manual/:splat 308
/docs/manual/next/* /docs/manual/:splat 308
Expand Down
5 changes: 5 additions & 0 deletions apps/docs/rescript.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
"subdirs": true,
"type": "dev"
},
{
"dir": "test-utils",
"subdirs": true,
"type": "dev"
},
{
"dir": "app",
"subdirs": true
Expand Down
5 changes: 5 additions & 0 deletions apps/docs/scripts/test-redirects.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ let assertManualLlmsVersionRedirects = (sourceVersion, destinationVersion) => {
};

assertRedirect("/llms/manual/llms.txt", "/llms.txt", "307");
assertRedirect(
"/docs/guidelines/publishing-packages",
"/docs/guides/publishing-packages",
"308",
);
const latestAlias = assertRedirect(
"/llms/manual/latest/llms.txt",
"/llms.txt",
Expand Down
7 changes: 7 additions & 0 deletions apps/docs/src/components/NavbarSecondary.res
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ let make = () => {
>
{React.string("API")}
</Link>
<Link
prefetch=#intent
to=#"/docs/guides/overview"
className={isActiveLink(~includes="/docs/guides/", ~route)}
>
{React.string("Guides")}
</Link>
<Link
prefetch=#intent
to=#"/syntax-lookup"
Expand Down
26 changes: 26 additions & 0 deletions apps/docs/test-utils/TestUtils.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
let getByTextExact = (element, text) => Vitest.getByTextWithOptions(element, text, {"exact": true})

let sleep = ms =>
Promise.make((resolve, _) => {
let _timeoutId = setTimeout(~handler=() => {
resolve()
}, ~timeout=ms)
})

external imageFromNode: WebAPI.DOMAPI.node => WebAPI.DOMAPI.htmlImageElement = "%identity"

let waitForImages = async (selector: string) => {
let root = switch document->WebAPI.Document.querySelector(selector) {
| Value(root) => root
| Null => failwith(`expected to find screenshot target ${selector}`)
}

let images = root->WebAPI.Element.querySelectorAll("img")

if images.length > 0 {
for i in 0 to images.length - 1 {
let image = images->WebAPI.NodeList.item(i)->imageFromNode
await image->WebAPI.HTMLImageElement.decode
}
}
}
3 changes: 2 additions & 1 deletion packages/shared/src/Path.res
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,8 @@ type t = [
| #"/docs/manual/async-await"
| #"/docs/manual/array-and-list"
| #"/docs/manual/api"
| #"/docs/guidelines/publishing-packages"
| #"/docs/guides/overview"
| #"/docs/guides/publishing-packages"
| #"/community/translations"
| #"/community/roadmap"
| #"/community/overview"
Expand Down
Loading
Loading