diff --git a/packages/docs/src/components/layout.js b/packages/docs/src/components/layout.js index 66c620cb9..e261f3ae7 100644 --- a/packages/docs/src/components/layout.js +++ b/packages/docs/src/components/layout.js @@ -1,6 +1,6 @@ /** @jsx jsx */ -import { jsx, Themed, useColorMode } from 'theme-ui' -import { useState, useRef } from 'react' +import { css, jsx, Themed, useColorMode } from 'theme-ui' +import { useState, useRef, useEffect } from 'react' import { Flex, Box } from '@theme-ui/components' import { AccordionNav } from '@theme-ui/sidenav' import { Link } from 'gatsby' @@ -45,10 +45,12 @@ export default function DocsLayout(props) { const nav = useRef(null) const [mode, setMode] = useColorMode() + const { pathname } = props.location + const isLanding = pathname === '/' + const fullwidth = - (props.pageContext.frontmatter && - props.pageContext.frontmatter.fullwidth) || - props.location.pathname === '/' + isLanding || + (props.pageContext.frontmatter && props.pageContext.frontmatter.fullwidth) const showNav = !props.pageContext?.frontmatter?.hidenav @@ -62,16 +64,22 @@ export default function DocsLayout(props) { sx={{ flexDirection: 'column', minHeight: '100vh', - }}> + }} + > {showNav && ( + position: isLanding ? 'initial' : 'sticky', + top: 0, + background: 'background', + }} + > { @@ -99,7 +107,8 @@ export default function DocsLayout(props) { ml: 2, whiteSpace: 'pre', }} - onClick={() => setMode(nextColorMode)}> + onClick={() => setMode(nextColorMode)} + > {getModeName(mode)} @@ -112,7 +121,8 @@ export default function DocsLayout(props) { alignItems: 'flex-start', display: ['block', 'flex'], height: '100%', - }}> + }} + > -
- {props.children} - - {!fullwidth && } -
+ + position: 'relative', + }} + > + {!isLanding && } +
+ {props.children} + + {!fullwidth && } +
+
) } + +function HeaderScrollShadow() { + const ref = useRef() + + useEffect(() => { + const onScroll = () => { + const { current } = ref + if (current) { + current.style.opacity = window.scrollY > 0 ? 1 : 0 + } + } + + window.addEventListener('scroll', onScroll) + return () => window.removeEventListener('scroll', onScroll) + }, []) + + return ( +
+ ) +} diff --git a/packages/e2e/integration/docs-navigation.ts b/packages/e2e/integration/docs-navigation.ts new file mode 100644 index 000000000..ea2576ad4 --- /dev/null +++ b/packages/e2e/integration/docs-navigation.ts @@ -0,0 +1,73 @@ +describe('docs navigation', () => { + it('works without 404', () => { + cy.visit('/') + cy.findByText('Documentation').click() + cy.location().should('have.property', 'pathname', '/getting-started') + cy.findByText('Theming').click() + cy.get('h1').should('have.text', 'Theming') + cy.findAllByRole('link').then(($links) => { + const links = $links.get() + const texts = links.map((link) => link.textContent) + + const expectedLinkTexts = [ + 'Hooks', + 'API', + 'Theme Specification', + 'Demo', + 'Resources', + 'Components', + 'Packages', + 'Guides', + 'Recipes', + 'Migrating', + 'Edit the page on GitHub', + 'Previous:Getting Started with Gatsby', + ] + + for (const s of expectedLinkTexts) { + expect(texts).to.include(s) + } + + const nextChapterLink = links.find( + (link) => link.textContent === 'Next:The sx Prop' + )! + + nextChapterLink.click() + + const packagesLink = links.find( + (link) => link.textContent === 'Packages' + )! + + packagesLink.click() + }) + + for (const packageName of [ + 'css', + 'core', + 'components', + 'presets', + 'color', + ]) { + cy.findAllByText('@theme-ui/' + packageName, { selector: 'li > a' }) + .first() + .click() + cy.location().should( + 'have.property', + 'pathname', + `/packages/${packageName}` + ) + } + + cy.window().then((win) => win.scrollTo(0, 200)) + + cy.percySnapshot('@theme-ui/color docs') + }) + + it('displays 404 page', () => { + cy.visit(`/not-found-${Math.random()}`, { failOnStatusCode: false }) + cy.findByRole('heading').should('have.text', '404') + cy.findByText('Page not found') + }) +}) + +export {}