Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ jobs:
run: yarn playwright install --with-deps
- name: Vitest
run: yarn ci:test
- name: Commit and Push changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: update vitest screenshots [skip ci]"
file_pattern: "**/__screenshots__/**/*.png"
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,8 @@ __tests__/**/*.jsx
_scripts

# Local env files
.env.local
.env.local

# Vitest screenshots
!__tests__/__screenshots__/**/*
.vitest-attachments
28 changes: 0 additions & 28 deletions __tests__/Example.test.res

This file was deleted.

100 changes: 100 additions & 0 deletions __tests__/NavbarPrimary_.test.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
open ReactRouter
open Vitest

test("desktop has everything visible", async () => {
await viewport(1440, 500)

let screen = await render(
<BrowserRouter>
<NavbarPrimary />
</BrowserRouter>,
)

let leftContent = await screen->getByTestId("navbar-primary-left-content")

await element(leftContent->getByText("Docs"))->toBeVisible
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getByText / getByLabelText are defined in src/bindings/Vitest.res as returning promise<element>, but these calls aren’t awaited before passing into element(...). This will either fail to type-check or behave incorrectly at runtime. Also, the aria-label in the component is "GitHub" (capital H), while the test uses "Github", so the query will fail even after awaiting.

Copilot uses AI. Check for mistakes.
await element(leftContent->getByText("Playground"))->toBeVisible
await element(leftContent->getByText("Blog"))->toBeVisible
await element(leftContent->getByText("Community"))->toBeVisible

let rightContent = await screen->getByTestId("navbar-primary-right-content")

await element(rightContent->getByLabelText("Github"))->toBeVisible
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getByText / getByLabelText are defined in src/bindings/Vitest.res as returning promise<element>, but these calls aren’t awaited before passing into element(...). This will either fail to type-check or behave incorrectly at runtime. Also, the aria-label in the component is "GitHub" (capital H), while the test uses "Github", so the query will fail even after awaiting.

Copilot uses AI. Check for mistakes.
await element(rightContent->getByLabelText("X (formerly Twitter)"))->toBeVisible
await element(rightContent->getByLabelText("Bluesky"))->toBeVisible
await element(rightContent->getByLabelText("Forum"))->toBeVisible

let navbar = await screen->getByTestId("navbar-primary")

await element(navbar)->toMatchScreenshot("desktop-navbar-primary")
})

test("tablet has everything visible", async () => {
await viewport(900, 500)

let screen = await render(
<BrowserRouter>
<NavbarPrimary />
</BrowserRouter>,
)

let leftContent = await screen->getByTestId("navbar-primary-left-content")

await element(leftContent->getByText("Docs"))->toBeVisible
await element(leftContent->getByText("Playground"))->toBeVisible
await element(leftContent->getByText("Blog"))->toBeVisible
await element(leftContent->getByText("Community"))->toBeVisible

let rightContent = await screen->getByTestId("navbar-primary-right-content")

await element(rightContent->getByLabelText("Github"))->toBeVisible
await element(rightContent->getByLabelText("X (formerly Twitter)"))->toBeVisible
await element(rightContent->getByLabelText("Bluesky"))->toBeVisible
await element(rightContent->getByLabelText("Forum"))->toBeVisible

let navbar = await screen->getByTestId("navbar-primary")

await element(navbar)->toMatchScreenshot("tablet-navbar-primary")
})

test("phone has some things hidden and a mobile nav that can be toggled", async () => {
await viewport(600, 1200)

let screen = await render(
<BrowserRouter>
<NavbarPrimary />
</BrowserRouter>,
)

let leftContent = await screen->getByTestId("navbar-primary-left-content")

await element(leftContent->getByText("Docs"))->toBeVisible
await element(leftContent->getByText("Playground"))->notToBeVisible
await element(leftContent->getByText("Blog"))->notToBeVisible
await element(leftContent->getByText("Community"))->notToBeVisible

let rightContent = await screen->getByTestId("navbar-primary-right-content")

await element(rightContent->getByLabelText("Github"))->notToBeVisible
await element(rightContent->getByLabelText("X (formerly Twitter)"))->notToBeVisible
await element(rightContent->getByLabelText("Bluesky"))->notToBeVisible
await element(rightContent->getByLabelText("Forum"))->notToBeVisible

await element(screen->getByTestId("mobile-nav"))->notToBeVisible

let button = await screen->getByTestId("toggle-mobile-overlay")

await element(button)->toBeVisible

await button->click

let mobileNav = await screen->getByTestId("mobile-nav")

await element(mobileNav)->toBeVisible

let navbar = await screen->getByTestId("navbar-primary")

await element(navbar)->toMatchScreenshot("mobile-navbar-primary")

await element(mobileNav)->toMatchScreenshot("mobile-overlay-navbar-primary")
})
93 changes: 93 additions & 0 deletions __tests__/NavbarTertiary_.test.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
open ReactRouter
open Vitest

let sidebarContent =
<aside>
<div dataTestId="sidebar-version-select"> {React.string("v12 (latest)")} </div>
<div dataTestId="sidebar-categories">
<div> {React.string("OVERVIEW")} </div>
<div> {React.string("Introduction")} </div>
</div>
</aside>

let breadcrumbs =
<span dataTestId="breadcrumbs"> {React.string("Docs / Language Manual / Installation")} </span>

let editLink = <a dataTestId="edit-link" href="#"> {React.string("Edit")} </a>

test("desktop shows breadcrumbs and edit link", async () => {
await viewport(1440, 500)

let screen = await render(
<BrowserRouter>
<NavbarTertiary sidebar=sidebarContent>
breadcrumbs
editLink
</NavbarTertiary>
</BrowserRouter>,
)

let navbar = await screen->getByTestId("navbar-tertiary")

await element(navbar)->toBeVisible

let crumbs = await screen->getByTestId("breadcrumbs")
await element(crumbs)->toBeVisible

let edit = await screen->getByTestId("edit-link")
await element(edit)->toBeVisible

await element(navbar)->toMatchScreenshot("desktop-navbar-tertiary")
})

test("mobile shows breadcrumbs and drawer button", async () => {
await viewport(600, 1200)

let screen = await render(
<BrowserRouter>
<NavbarTertiary sidebar=sidebarContent>
breadcrumbs
editLink
</NavbarTertiary>
</BrowserRouter>,
)

let navbar = await screen->getByTestId("navbar-tertiary")
await element(navbar)->toBeVisible

let crumbs = await screen->getByTestId("breadcrumbs")
await element(crumbs)->toBeVisible

let edit = await screen->getByTestId("edit-link")
await element(edit)->toBeVisible

await element(navbar)->toMatchScreenshot("mobile-navbar-tertiary")
})

test("mobile drawer can be toggled open", async () => {
await viewport(600, 1200)

let screen = await render(
<BrowserRouter>
<NavbarTertiary sidebar=sidebarContent>
breadcrumbs
editLink
</NavbarTertiary>
</BrowserRouter>,
)

// Sidebar dialog should not be visible initially
let sidebar = await screen->getByTestId("sidebar-categories")
await element(sidebar)->notToBeVisible

// Click the drawer toggle button
let drawerButton = await screen->getByRole(#button)
await drawerButton->click

// Sidebar content should now be visible
let sidebarAfter = await screen->getByTestId("sidebar-categories")
await element(sidebarAfter)->toBeVisible

let versionSelect = await screen->getByTestId("sidebar-version-select")
await element(versionSelect)->toBeVisible
})
80 changes: 80 additions & 0 deletions __tests__/VersionSelect_.test.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
open Vitest

test("renders current version label", async () => {
let screen = await render(<VersionSelect />)

let el = await screen->getByTestId("version-select")
await element(el)->toBeVisible

let label = await screen->getByText("v12 (latest)")
await element(label)->toBeVisible
})

test("clicking button shows older versions", async () => {
let screen = await render(<VersionSelect />)

// Menu should be hidden initially
let v11 = await screen->getByText("v11")
await element(v11)->notToBeVisible

// Click the trigger button
let button = await screen->getByRole(#button)
await button->click

// Older versions should now be visible
let v11After = await screen->getByText("v11")
await element(v11After)->toBeVisible

let v9 = await screen->getByText("v9.1 - v10.1")
await element(v9)->toBeVisible

let v8 = await screen->getByText("v8.2 - v9.0")
await element(v8)->toBeVisible

let v6 = await screen->getByText("v6.0 - v8.1")
await element(v6)->toBeVisible
})

test("clicking button again closes older versions", async () => {
let screen = await render(<VersionSelect />)

let button = await screen->getByRole(#button)

// Open
await button->click
let v11 = await screen->getByText("v11")
await element(v11)->toBeVisible

// Close
await button->click
let v11After = await screen->getByText("v11")
await element(v11After)->notToBeVisible
})

test("multiple instances have unique popover IDs", async () => {
let screen = await render(
<div>
<div dataTestId="first">
<VersionSelect />
</div>
<div dataTestId="second">
<VersionSelect />
</div>
</div>,
)

let first = await screen->getByTestId("first")
let second = await screen->getByTestId("second")

// Click the button in the first instance
let firstButton = await first->getByRole(#button)
await firstButton->click

// First instance menu should be visible
let firstV11 = await first->getByText("v11")
await element(firstV11)->toBeVisible

// Second instance menu should remain hidden
let secondV11 = await second->getByText("v11")
await element(secondV11)->notToBeVisible
})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 5 additions & 20 deletions app/root.res
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,6 @@ open ReactRouter

@react.component
let default = () => {
let {pathname} = ReactRouter.useLocation()
let (isOverlayOpen, setOverlayOpen) = React.useState(_ => false)
let (isScrollLockEnabled, setIsScrollLockEnabled) = React.useState(_ => false)

React.useEffect(() => {
// When the path changes close the sidebar and disable scroll lock
setOverlayOpen(_ => false)
setIsScrollLockEnabled(_ => false)
None
}, [pathname])

<html lang="en">
<head>
<style> {React.string("html {opacity:0;}")} </style>
Expand All @@ -65,15 +54,11 @@ let default = () => {
/>
<meta charSet="UTF-8" />
</head>
<body className={isScrollLockEnabled ? "overflow-hidden" : ""}>
<ScrollLockContext.Provider lockState=(isScrollLockEnabled, setIsScrollLockEnabled)>
<EnableCollapsibleNavbar isEnabled={!isOverlayOpen}>
<Navigation isOverlayOpen setOverlayOpen />
<Outlet />
<ScrollRestoration />
<Scripts />
</EnableCollapsibleNavbar>
</ScrollLockContext.Provider>
<body>
<NavbarPrimary />
<Outlet />
<ScrollRestoration />
<Scripts />
</body>
</html>
}
4 changes: 3 additions & 1 deletion app/routes.res
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ let stdlibRoutes =
let beltRoutes =
beltPaths->Array.map(path => route(path, "./routes/ApiRoute.jsx", ~options={id: path}))

let mdxRoutes = mdxRoutes("./routes/MdxRoute.jsx")

let default = [
index("./routes/LandingPageRoute.jsx"),
route("packages", "./routes/PackagesRoute.jsx"),
Expand All @@ -42,6 +44,6 @@ let default = [
route("docs/manual/api/dom", "./routes/ApiRoute.jsx", ~options={id: "api-dom"}),
...stdlibRoutes,
...beltRoutes,
...mdxRoutes("./routes/MdxRoute.jsx"),
...mdxRoutes,
route("*", "./routes/NotFoundRoute.jsx"),
]
Loading
Loading