diff --git a/.eslintignore b/.eslintignore index f33ddf0..48d591d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ **/node_modules/* **/out/* **/.next/* -next.config.js \ No newline at end of file +next.config.js +**/cypress/* \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..cc7e2a6 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,25 @@ +name: github + +on: + push: + branches: + - 'main' + pull_request: + branches: + - '**' + workflow_dispatch: + +jobs: + test: + environment: Production + # Available tools on this machine: + # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup and Install + run: yarn install + - name: Build and Start Next.js + run: yarn build && (yarn start&) > /dev/null + - name: Run Tests + run: yarn test diff --git a/.gitignore b/.gitignore index 3135249..c5f7290 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,11 @@ examples packages # jambonz... -/.notes \ No newline at end of file +/.notes + +# cypress +/cypress/* +!/cypress/integration +!/cypress/plugins +!/cypress/support +!/cypress/scripts \ No newline at end of file diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index f74c781..0000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -.next -node_modules diff --git a/README.md b/README.md index 8056173..fb59dc9 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ jambonz > The "bring your own everything" CPaaS +[![CI](https://github.com/jambonz/next-static-site/actions/workflows/main.yml/badge.svg)](https://github.com/jambonz/next-static-site/actions/workflows/main.yml) + ![](/public/jambonz.png) ## Stack @@ -36,9 +38,26 @@ Clone this repository and install [yarn](https://yarnpkg.com/getting-started/ins - `yarn build && yarn start` - Create an optimized Next.js production build and serve it locally - `yarn build && yarn export` - - Create a static production build for any static deploy target + - Create a static production export for any static deploy target -Other packages being used prominently in this apps source code are [classnames](https://www.npmjs.com/package/classnames) and [nanoid](https://www.npmjs.com/package/nanoid#react). +### Testing + +*Note cypress test suite is still a work in progress* + +You can run e2e tests for the site using [Cypress](https://docs.cypress.io). Cypress specs rely on running the Next.js site on port `3000` as the baseUrl so the best way to test locally is to `yarn dev` in one shell and then `yarn test` in another shell. Optionally, you can `yarn build && yarn start` to create an optimized production server locally and in another shell run `yarn test`. The GitHub workflow for this repository runs the Cypress tests by building and then starting Next.js in the background like `yarn build && (yarn start&) > /dev/null` and then `yarn test`. + +Cypress specs are located at `cypress/integration/...`. The source of truth static YAML data should always be used when authoring Cypress tests so we've implemented a script that generates `JSON` data fixtures for Cypress from the YAML data before tests are run. When running `yarn test` what happens is: + +* A `pretest` script runs and generates the JSON fixtures for Cypress +* The Cypress tests are run in headless mode +* A `posttest` script runs and performs cleanup on the Cypress fixtures + +### Packages + +Packages being used prominently in this apps source code are: + +* [classnames](https://www.npmjs.com/package/classnames) +* [nanoid](https://www.npmjs.com/package/nanoid#react) ## Jambonz UI library @@ -54,8 +73,8 @@ You should always use the reusable components from the `jambonz-ui` component li We are using static data with [yamljs](https://www.npmjs.com/package/yamljs) and [Next.js static props](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation). Data files are located in the `data` directory. There's a JS data utility in `lib/data.js` that provides a method for "fetching" the static data for use with a Next.js pages async `getStaticProps` SSR method. -## Jambonz developer docs +## Jambonz markdown data -The project is generating developer docs from markdown files using static file JS utilities alongside Next.js static paths/props system. We are leveraging their [catch-all](https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes) dynamic routes logic located at `pages/docs/[[...slug]].js`. The markdown files are in the `docs` directory. The docs structure is controlled in the docs page YAML data located in `data/docs.yaml`. You can create docs markdown files at will in the `docs` directory but they will not render in the sidebar nav until they are also added to the nav structure in this file. +The project is generating some dynamic layouts with markdown files using static file JS utilities alongside Next.js static paths/props system. We are leveraging their [catch-all](https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes) dynamic routes logic. Example located at `pages/docs/[[...slug]].js`. The markdown files are in the `markdown` directory organized by subfolders. The markdown navigation structure is controlled in the relevant page YAML data located in the `data` directory for each `markdown` subfolder. You can create markdown files at will but they will not render in the sidebar nav until they are also added to the nav structure in the relevant `data` file. For example, the markdown files for the developer docs are located at `markdown/docs/...` and the YAML data for this layout is located at `data/docs.yml`. We are using [remark](https://github.com/remarkjs/remark), [remark-html](https://github.com/remarkjs/remark-html) and [remark-gfm](https://github.com/remarkjs/remark-gfm) as well as [gray-matter](https://github.com/jonschlinkert/gray-matter) for parsing the docs markdown files. Code syntax highlighting is done with [prismjs](https://prismjs.com) and the associative babel config is in the `.babelrc` file. It's important to leave the preset in this file that merges our config with `next/babel` so Next.js works properly. diff --git a/components/jambonz-ui.js b/components/jambonz-ui.js index 17239c4..446f9d5 100644 --- a/components/jambonz-ui.js +++ b/components/jambonz-ui.js @@ -49,7 +49,7 @@ export function useMatchMedia(mediaQuery = null) { // Normalize for our mobile media query export function useMobileMedia() { - return useMatchMedia('(max-width: 768px)'); + return useMatchMedia('(max-width: 896px)'); } export function H1({ children }) { @@ -92,6 +92,33 @@ export function MXS({ children }) { return
{children}
; } +export function Latest({ data }) { + const classes = { + 'latest': true, + [`latest--${data.label}`]: true, + 'pad': true, + 'bg-pink': true, + }; + + return ( +
+
+
+

{data.headline}

+
+
+
+ {/* Use dangerouslySetInnerHTML to render inline links from YAML data */} + {normalizeSubtext(data.subtext).map((subtext) => { + return
; + })} +
+
+
+
+ ); +} + export function Hero({ data, subStyle }) { const classes = { 'hero': true, @@ -177,7 +204,7 @@ export function Icon({ name, mainStyle = 'inline', subStyle = null, ...props }) export function TextLayout({ data }) { return (
-
+
); } diff --git a/components/layout.js b/components/layout.js index 46091c2..84a5c59 100644 --- a/components/layout.js +++ b/components/layout.js @@ -1,8 +1,17 @@ import Head from 'next/head'; +import Link from 'next/link'; import Navi from './navi'; import Footer from './footer'; +function Banner({ data }) { + return ( + + {data.text} + + ); +} + export default function Layout({ children, siteData, title = 'jambonz' }) { return ( <> @@ -23,6 +32,7 @@ export default function Layout({ children, siteData, title = 'jambonz' }) { */} + {siteData.banner && siteData.banner.active && }
{children} diff --git a/components/markdown.js b/components/markdown.js new file mode 100644 index 0000000..05f97f6 --- /dev/null +++ b/components/markdown.js @@ -0,0 +1,91 @@ +import { useState } from 'react'; + +import { nanoid } from 'nanoid'; +import classNames from 'classnames'; + +import Link from 'next/link'; +import { useRouter } from 'next/router'; + +import { Icon, TextLayout } from './jambonz-ui'; + +function MarkdownSidebar({scope, data}) { + const router = useRouter(); + const regex = new RegExp(`^/${scope}/|^/+|/+$`, 'g'); + const parsedTab = router.asPath.replace(regex, '').split('/').shift(); + const parsedPath = router.asPath.replace(/^\/+|\/+$/g, '').split('/').pop(); + const [active, setActive] = useState({ + [parsedTab]: true, + }); + + const handleToggle = (slug) => { + setActive((oldActive) => { + const newActive = {}; + + for (let i in oldActive) { + newActive[i] = oldActive[i]; + } + + newActive[slug] = newActive[slug] ? false : true; + + return newActive; + }); + }; + + return ( + + ); +} + +export default function Markdown({scope, data, docs}) { + return ( +
+
+ + +
+
+ ); +} \ No newline at end of file diff --git a/components/navi.js b/components/navi.js index cb27704..36759bc 100644 --- a/components/navi.js +++ b/components/navi.js @@ -11,10 +11,10 @@ function NaviItem({obj}) { const router = useRouter(); const rSlash = /^\/|\/$/g; const cleanLink = obj.link.replace(rSlash, ''); - const cleanPath = router.asPath.replace(rSlash, ''); + const cleanPath = router.asPath.replace(rSlash, '').split('/')[0]; const classes = { navi__link: true, - active: cleanLink === cleanPath, + active: cleanLink && cleanLink === cleanPath, }; return ( @@ -71,6 +71,11 @@ function NaviMobile({ active, handler, siteData }) { export default function Navi({ siteData }) { const [active, setActive] = useState(false); const mobile = useMobileMedia(); + const classes = { + navi: true, + mobile, + active, + }; const handleNavi = () => { setActive(!active); @@ -82,7 +87,7 @@ export default function Navi({ siteData }) { } return ( -