├── .editorconfig
├── .env.sample
├── .eslintignore
├── .eslintrc.js
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
└── workflows
│ ├── assertions.yml
│ ├── chromatic.yml
│ ├── publish.yml
│ └── security.yml
├── .gitignore
├── .markdownlint.json
├── .markdownlintignore
├── .nvmrc
├── .prettierignore
├── .prettierrc.js
├── .storybook
├── main.js
├── manager.js
├── preview.js
└── theme.js
├── .stylelintignore
├── .stylelintrc.js
├── .vscode
├── extensions.json
└── settings.json
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── __tests__
└── jest
│ └── components
│ ├── atoms
│ ├── Breadcrumbs.test.js
│ ├── Button.test.js
│ ├── Code.test.js
│ ├── Container.test.js
│ ├── Heading.test.js
│ ├── Icon.test.js
│ ├── Inputs
│ │ ├── Checkbox.test.js
│ │ └── CheckboxGroup.test.js
│ ├── Logo.test.js
│ ├── PullQuote.test.js
│ ├── Quote.test.js
│ ├── RichText.test.js
│ ├── Separator.test.js
│ ├── Spacer.test.js
│ └── Table.test.js
│ └── molecules
│ ├── Alert.test.js
│ ├── ButtonGroup.test.js
│ ├── Card.test.js
│ ├── Comments.test.js
│ ├── Form.test.js
│ ├── ImageGallery.test.js
│ └── Navigation.test.js
├── backend
├── README.md
└── composer.json
├── components
├── atoms
│ ├── Breadcrumbs
│ │ ├── Breadcrumbs.js
│ │ ├── Breadcrumbs.module.css
│ │ ├── Breadcrumbs.stories.mdx
│ │ └── index.js
│ ├── Button
│ │ ├── Button.js
│ │ ├── Button.module.css
│ │ ├── Button.stories.mdx
│ │ └── index.js
│ ├── Code
│ │ ├── Code.js
│ │ ├── Code.module.css
│ │ ├── Code.stories.mdx
│ │ └── index.js
│ ├── Columns
│ │ ├── Columns.js
│ │ ├── Columns.module.css
│ │ └── index.js
│ ├── Container
│ │ ├── Container.js
│ │ ├── Container.module.css
│ │ ├── Container.stories.mdx
│ │ └── index.js
│ ├── ExitPreview
│ │ ├── ExitPreview.js
│ │ └── index.js
│ ├── Heading
│ │ ├── Heading.js
│ │ ├── Heading.stories.mdx
│ │ └── index.js
│ ├── Icon
│ │ ├── Icon.js
│ │ ├── Icon.stories.mdx
│ │ ├── icons.js
│ │ └── index.js
│ ├── Image
│ │ ├── Image.js
│ │ ├── Image.module.css
│ │ ├── Image.stories.mdx
│ │ └── index.js
│ ├── Inputs
│ │ ├── Checkbox
│ │ │ ├── Checkbox.js
│ │ │ └── index.js
│ │ ├── CheckboxGroup
│ │ │ ├── CheckboxGroup.js
│ │ │ └── index.js
│ │ ├── InputError
│ │ │ ├── InputError.js
│ │ │ ├── InputError.module.css
│ │ │ └── index.js
│ │ ├── Select
│ │ │ ├── Select.js
│ │ │ ├── Select.module.css
│ │ │ └── index.js
│ │ ├── Text
│ │ │ ├── Text.js
│ │ │ ├── Text.module.css
│ │ │ └── index.js
│ │ └── index.js
│ ├── Logo
│ │ ├── Logo.js
│ │ ├── Logo.stories.mdx
│ │ └── index.js
│ ├── PullQuote
│ │ ├── PullQuote.js
│ │ ├── PullQuote.module.css
│ │ ├── PullQuote.stories.mdx
│ │ └── index.js
│ ├── Quote
│ │ ├── Quote.js
│ │ ├── Quote.module.css
│ │ ├── Quote.stories.mdx
│ │ └── index.js
│ ├── RichText
│ │ ├── RichText.js
│ │ ├── RichText.module.css
│ │ ├── RichText.stories.mdx
│ │ └── index.js
│ ├── Separator
│ │ ├── Separator.js
│ │ ├── Separator.module.css
│ │ ├── Separator.stories.mdx
│ │ └── index.js
│ ├── Spacer
│ │ ├── Spacer.js
│ │ ├── Spacer.stories.mdx
│ │ └── index.js
│ ├── Table
│ │ ├── Table.js
│ │ ├── Table.module.css
│ │ ├── Table.stories.mdx
│ │ └── index.js
│ ├── TwitterEmbed
│ │ ├── TwitterEmbed.js
│ │ ├── TwitterEmbed.module.css
│ │ ├── TwitterEmbed.stories.mdx
│ │ └── index.js
│ └── VideoEmbed
│ │ ├── VideoEmbed.js
│ │ ├── VideoEmbed.module.css
│ │ ├── VideoEmbed.stories.mdx
│ │ └── index.js
├── blocks
│ ├── core
│ │ ├── BlockButton
│ │ │ ├── BlockButton.js
│ │ │ └── index.js
│ │ ├── BlockButtons
│ │ │ ├── BlockButtons.js
│ │ │ └── index.js
│ │ ├── BlockCode
│ │ │ ├── BlockCode.js
│ │ │ └── index.js
│ │ ├── BlockColumns
│ │ │ ├── BlockColumns.js
│ │ │ └── index.js
│ │ ├── BlockCover
│ │ │ ├── BlockCover.js
│ │ │ └── index.js
│ │ ├── BlockEmbed
│ │ │ ├── BlockEmbed.js
│ │ │ └── index.js
│ │ ├── BlockGravityForm
│ │ │ ├── BlockGravityForm.js
│ │ │ └── index.js
│ │ ├── BlockHeadings
│ │ │ ├── BlockHeadings.js
│ │ │ └── index.js
│ │ ├── BlockImage
│ │ │ ├── BlockImage.js
│ │ │ └── index.js
│ │ ├── BlockImageGallery
│ │ │ ├── BlockImageGallery.js
│ │ │ └── index.js
│ │ ├── BlockList
│ │ │ ├── BlockList.js
│ │ │ └── index.js
│ │ ├── BlockMediaText
│ │ │ ├── BlockMediaText.js
│ │ │ └── index.js
│ │ ├── BlockParagraph
│ │ │ ├── BlockParagraph.js
│ │ │ └── index.js
│ │ ├── BlockPullQuote
│ │ │ ├── BlockPullQuote.js
│ │ │ └── index.js
│ │ ├── BlockQuote
│ │ │ ├── BlockQuote.js
│ │ │ └── index.js
│ │ ├── BlockSeparator
│ │ │ ├── BlockSeparator.js
│ │ │ └── index.js
│ │ ├── BlockShortcode
│ │ │ ├── BlockShortcode.js
│ │ │ └── index.js
│ │ ├── BlockSpacer
│ │ │ ├── BlockSpacer.js
│ │ │ └── index.js
│ │ └── BlockTable
│ │ │ ├── BlockTable.js
│ │ │ └── index.js
│ └── index.js
├── common
│ ├── Layout.js
│ ├── Meta.js
│ └── WordPressProvider.js
├── documentation
│ ├── DocTable
│ │ ├── DocTable.js
│ │ ├── DocTable.module.css
│ │ └── index.js
│ ├── Field.stories.mdx
│ ├── Icons.stories.mdx
│ └── README.md
├── molecules
│ ├── Alert
│ │ ├── Alert.js
│ │ ├── Alert.module.css
│ │ ├── Alert.stories.mdx
│ │ └── index.js
│ ├── AlgoliaResults
│ │ ├── AlgoliaResults.js
│ │ ├── AlgoliaResults.module.css
│ │ ├── facets
│ │ │ ├── Authors.js
│ │ │ ├── PostType.js
│ │ │ └── Sort.js
│ │ ├── index.js
│ │ ├── refinements
│ │ │ ├── CustomClearRefinements.js
│ │ │ ├── CustomMenu.js
│ │ │ ├── CustomRefinementList.js
│ │ │ └── CustomToggleRefinement.js
│ │ └── templates
│ │ │ ├── Hit.js
│ │ │ ├── NoResults.js
│ │ │ └── SearchResults.js
│ ├── AlgoliaSearch
│ │ ├── AlgoliaSearch.js
│ │ ├── AlgoliaSearch.module.css
│ │ ├── components
│ │ │ ├── History.js
│ │ │ ├── Hit.js
│ │ │ ├── Results.js
│ │ │ ├── Search.js
│ │ │ ├── SearchIcon.js
│ │ │ └── SearchPlaceholder.js
│ │ ├── functions
│ │ │ ├── buildSearchUrl.js
│ │ │ ├── localStorage.js
│ │ │ ├── searchClick.js
│ │ │ └── searchSubmit.js
│ │ └── index.js
│ ├── Blocks
│ │ ├── Blocks.js
│ │ ├── Blocks.stories.mdx
│ │ └── index.js
│ ├── ButtonGroup
│ │ ├── ButtonGroup.js
│ │ ├── ButtonGroup.module.css
│ │ ├── ButtonGroup.stories.mdx
│ │ └── index.js
│ ├── Card
│ │ ├── Card.js
│ │ ├── Card.module.css
│ │ ├── Card.stories.mdx
│ │ └── index.js
│ ├── Comments
│ │ ├── Comments.js
│ │ └── index.js
│ ├── Form
│ │ ├── Form.js
│ │ ├── Form.module.css
│ │ ├── Form.stories.mdx
│ │ └── index.js
│ ├── GravityForm
│ │ ├── Fields
│ │ │ ├── Checkbox.js
│ │ │ ├── Fields.js
│ │ │ ├── File.js
│ │ │ ├── Select.js
│ │ │ ├── Text.js
│ │ │ ├── Textarea.js
│ │ │ └── index.js
│ │ ├── GravityForm.js
│ │ ├── GravityForm.module.css
│ │ ├── GravityForm.stories.mdx
│ │ └── index.js
│ ├── ImageGallery
│ │ ├── ImageGallery.js
│ │ ├── ImageGallery.module.css
│ │ ├── ImageGallery.stories.mdx
│ │ └── index.js
│ └── Navigation
│ │ ├── Navigation.js
│ │ ├── Navigation.module.css
│ │ └── index.js
└── organisms
│ ├── Archive
│ ├── Archive.js
│ ├── Archive.stories.mdx
│ └── index.js
│ ├── Footer
│ ├── Footer.js
│ ├── Footer.module.css
│ ├── Footer.stories.mdx
│ └── index.js
│ ├── Header
│ ├── Header.js
│ ├── Header.module.css
│ ├── Header.stories.mdx
│ └── index.js
│ ├── Hero
│ ├── Hero.js
│ ├── Hero.module.css
│ ├── Hero.stories.mdx
│ └── index.js
│ └── MediaText
│ ├── MediaText.js
│ ├── MediaText.module.css
│ ├── MediaText.stories.mdx
│ └── index.js
├── docs
├── .markdownlint.json
├── .markdownlintignore
├── .prettierignore
├── .prettierrc.js
├── README.md
├── babel.config.js
├── docs
│ ├── backend
│ │ ├── _category_.json
│ │ ├── algolia.md
│ │ ├── comments.md
│ │ ├── gravity-forms.md
│ │ ├── index.md
│ │ ├── menus.md
│ │ └── wp-config.md
│ ├── contributing.md
│ ├── frontend
│ │ ├── _category_.json
│ │ ├── coding-standards.md
│ │ ├── component-block-handler.md
│ │ ├── component-css-module.md
│ │ ├── component-js.md
│ │ ├── components-overview.md
│ │ ├── env-variables-and-vercel.md
│ │ ├── env-variables.md
│ │ ├── folder-structure.md
│ │ ├── index.md
│ │ ├── menus.md
│ │ ├── overview.md
│ │ └── previews.md
│ ├── index.md
│ ├── learn
│ │ ├── _category_.json
│ │ ├── builds.md
│ │ ├── comments.md
│ │ ├── creating-content.md
│ │ ├── custom-post-types.md
│ │ ├── settings-page.md
│ │ ├── wordpress-theme.md
│ │ └── wp-graphql.md
│ ├── other
│ │ ├── 3rd-party-services.md
│ │ ├── _category_.json
│ │ ├── docusaurus.md
│ │ ├── internal-documentation-for-wds.md
│ │ ├── npm-workflows.md
│ │ └── recommended-extensions.md
│ └── storybook
│ │ ├── _category_.json
│ │ └── index.md
├── docusaurus.config.js
├── lefthook-docs.yml
├── package-lock.json
├── package.json
├── sidebars.js
├── src
│ ├── components
│ │ ├── Features.js
│ │ ├── Features.module.css
│ │ ├── GetStarted.js
│ │ ├── GetStarted.module.css
│ │ ├── Hero.js
│ │ ├── Hero.module.css
│ │ ├── HowItWorks.js
│ │ ├── HowItWorks.module.css
│ │ ├── Plugins.js
│ │ └── Plugins.module.css
│ ├── css
│ │ └── custom.css
│ └── pages
│ │ ├── index.js
│ │ ├── index.module.css
│ │ └── old-index.md
├── static
│ ├── .nojekyll
│ └── img
│ │ ├── favicon.ico
│ │ ├── nextjs-wordpress-starter-frontend-backend-graphic-vertical.webp
│ │ ├── nextjs-wordpress-starter-frontend-backend-graphic.webp
│ │ ├── screenshot-activate-all-plugins.png
│ │ ├── screenshot-activate-graphql-gutenberg.png
│ │ ├── screenshot-env-vars-vercel.png
│ │ ├── screenshot-frontend.png
│ │ ├── screenshot-github-actions.png
│ │ ├── screenshot-headless-theme.png
│ │ ├── screenshot-local-by-flywheel.png
│ │ ├── screenshot-npm-run-dev.png
│ │ ├── screenshot-set-404-page-2.png
│ │ ├── screenshot-set-404-page.png
│ │ ├── screenshot-set-algolia-creds.png
│ │ ├── screenshot-set-application-password.png
│ │ ├── screenshot-set-menus.png
│ │ ├── screenshot-set-page-options.png
│ │ ├── screenshot-set-permalinks.png
│ │ ├── screenshot-setup-algolia-account.png
│ │ ├── screenshot-setup-gravity-forms.png
│ │ ├── screenshot-tgm-theme.png
│ │ ├── screenshot-wpe-prod-release.png
│ │ ├── wds-logo-60x60.png
│ │ └── wds-logo-60x60.webp
└── tailwind.config.js
├── functions
├── convertHextoRgb.js
├── createMarkup.js
├── extractRgbValues.js
├── formatFocalPoint.js
├── getEnvVar.js
├── getPagePropTypes.js
├── isLinkActive.js
├── middleware
│ └── gfMultipartFormParser.js
├── next-api
│ ├── README.md
│ └── wordpress
│ │ ├── README.md
│ │ ├── archive
│ │ └── getArchivePosts.js
│ │ ├── comments
│ │ └── processPostComment.js
│ │ └── gravityForms
│ │ ├── processGfFieldValues.js
│ │ └── processGfFormSubmission.js
├── parseQuerystring.js
└── wordpress
│ ├── README.md
│ ├── auth
│ ├── loginUser.js
│ ├── refreshAuthToken.js
│ └── registerUser.js
│ ├── blocks
│ ├── displayBlock.js
│ ├── formatBlockData.js
│ └── getBlockStyles.js
│ ├── comments
│ └── insertPostComment.js
│ ├── gravityForms
│ ├── encodeGfFormData.js
│ ├── getGfFieldId.js
│ ├── getGfFormById.js
│ ├── getGfFormDefaults.js
│ ├── getGfFormValidationSchema.js
│ ├── getHiddenClassName.js
│ ├── index.js
│ ├── insertGfFormEntry.js
│ └── yupSchema
│ │ ├── ArraySchemaFactory.js
│ │ └── StringSchemaFactory.js
│ ├── media
│ └── getMediaByID.js
│ ├── menus
│ ├── filterMenusByLocation.js
│ ├── formatHeirarchialMenu.js
│ └── getMenus.js
│ ├── postTypes
│ ├── getFrontendPage.js
│ ├── getHeadlessConfigPage.js
│ ├── getPostTypeArchive.js
│ ├── getPostTypeById.js
│ ├── getPostTypeStaticPaths.js
│ ├── getPostTypeStaticProps.js
│ ├── getPostTypeTaxonomyArchive.js
│ ├── isHierarchicalPostType.js
│ ├── isValidPostType.js
│ └── processPostTypeQuery.js
│ ├── posts
│ └── getPostsDateArchive.js
│ ├── seo
│ ├── formatArchiveSeoData.js
│ ├── formatDefaultSeoData.js
│ └── formatManualSeoMeta.js
│ └── taxonomies
│ ├── getTaxonomyStaticPaths.js
│ ├── getTaxonomyStaticProps.js
│ └── isValidTaxonomy.js
├── jest.config.js
├── jest.setup.js
├── jsconfig.json
├── lefthook-frontend.yml
├── lefthook.yml
├── lib
├── algolia
│ ├── README.md
│ └── connector.js
├── apolloConfig.js
├── next-api
│ ├── README.md
│ ├── connector.js
│ └── wordpress
│ │ ├── README.md
│ │ ├── _config
│ │ └── schema.js
│ │ ├── archive
│ │ └── queryArchivePosts.js
│ │ ├── comments
│ │ └── mutationAddComment.js
│ │ └── gravityForms
│ │ └── mutationSubmitForm.js
└── wordpress
│ ├── README.md
│ ├── _config
│ ├── archiveQuerySeo.js
│ ├── frontendPageSeo.js
│ ├── headlessConfigPageQuerySeo.js
│ ├── menuLocations.js
│ ├── postTypes.js
│ └── taxonomies.js
│ ├── _query-partials
│ ├── allMenus.js
│ ├── archiveData.js
│ ├── archivePageInfo.js
│ ├── archiveSeo.js
│ ├── authorPostFields.js
│ ├── categoriesPostFields.js
│ ├── commentsFields.js
│ ├── commentsPostFields.js
│ ├── defaultPageData.js
│ ├── defaultSeoFields.js
│ ├── featuredImagePostFields.js
│ ├── globalPostFields.js
│ ├── seoPostFields.js
│ └── tagsPostFields.js
│ ├── auth
│ ├── mutationLoginUser.js
│ ├── mutationRefreshAuthToken.js
│ └── mutationRegisterUser.js
│ ├── categories
│ └── queryPostsByCategory.js
│ ├── comments
│ ├── mutationInsertComment.js
│ └── queryCommentsByPostId.js
│ ├── connector.js
│ ├── gravityForms
│ ├── fieldProps.js
│ ├── mutationInsertFormEntry.js
│ └── queryFormById.js
│ ├── media
│ └── queryMediaAttributes.js
│ ├── pages
│ ├── queryDefaultPageData.js
│ ├── queryError404Page.js
│ └── queryPageById.js
│ ├── posts
│ ├── queryPostById.js
│ ├── queryPostsArchive.js
│ └── queryPostsDateArchive.js
│ └── tags
│ └── queryPostsByTag.js
├── next-sitemap.js
├── next.config.js
├── package-lock.json
├── package.json
├── packages
└── README.md
├── pages
├── 404.js
├── 500.js
├── [...slug].js
├── [year]
│ └── [month]
│ │ └── [day]
│ │ └── [slug].js
├── _app.js
├── _document.js
├── api
│ ├── auth
│ │ └── [...nextauth].js
│ ├── exit-preview.js
│ ├── preview.js
│ └── wordpress
│ │ ├── archive.js
│ │ ├── comments.js
│ │ ├── gravityForms.js
│ │ └── revalidate.js
├── category
│ └── [...slug].js
├── index.js
├── login.js
├── profile
│ └── index.js
├── register.js
├── search.js
└── tags
│ └── [...slug].js
├── postcss.config.js
├── public
├── favicon
│ ├── android-icon-144x144.png
│ ├── android-icon-192x192.png
│ ├── android-icon-36x36.png
│ ├── android-icon-48x48.png
│ ├── android-icon-512x512.png
│ ├── android-icon-72x72.png
│ ├── android-icon-96x96.png
│ ├── apple-icon-114x114.png
│ ├── apple-icon-120x120.png
│ ├── apple-icon-144x144.png
│ ├── apple-icon-152x152.png
│ ├── apple-icon-180x180.png
│ ├── apple-icon-57x57.png
│ ├── apple-icon-60x60.png
│ ├── apple-icon-72x72.png
│ ├── apple-icon-76x76.png
│ ├── apple-icon-precomposed.png
│ ├── apple-icon.png
│ ├── browserconfig.xml
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon-96x96.png
│ ├── favicon.ico
│ ├── maskable_icon.png
│ ├── ms-icon-144x144.png
│ ├── ms-icon-150x150.png
│ ├── ms-icon-310x310.png
│ ├── ms-icon-70x70.png
│ └── site.webmanifest
├── images
│ ├── wds-logo-inverse.svg
│ └── wds-logo.svg
├── robots.txt
├── sitemap-0.xml
└── sitemap.xml
├── scripts
└── .gitkeep
├── styles
├── demo.css
└── index.css
└── tailwind.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | end_of_line = lf
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | !/.*.js
2 | *.min.js
3 | .*cache
4 | .next/
5 | backend/
6 | build/
7 | dist/
8 | docs/
9 | node_modules/
10 | public/
11 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring/
2 | module.exports = {
3 | env: {
4 | browser: true,
5 | node: true,
6 | es6: true
7 | },
8 | extends: [
9 | 'eslint:recommended',
10 | 'plugin:jsdoc/recommended',
11 | 'plugin:jest/recommended',
12 | 'next',
13 | 'prettier'
14 | ],
15 | settings: {
16 | jsdoc: {
17 | tagNamePreference: {
18 | returns: 'return'
19 | }
20 | }
21 | },
22 | plugins: ['prettier', 'jsdoc'],
23 | rules: {
24 | '@next/next/no-img-element': 'off',
25 | 'func-style': ['error', 'declaration'],
26 | 'jsdoc/check-indentation': 'warn',
27 | 'jsdoc/check-line-alignment': [
28 | 'warn',
29 | 'always',
30 | {
31 | tags: ['param', 'return']
32 | }
33 | ],
34 | 'jsdoc/require-param': [
35 | 'warn',
36 | {
37 | checkRestProperty: true,
38 | unnamedRootBase: ['props']
39 | }
40 | ],
41 | 'jsdoc/check-values': [
42 | 'warn',
43 | {
44 | allowedAuthors: ['WebDevStudios']
45 | }
46 | ],
47 | 'jsx-a11y/anchor-is-valid': 'off',
48 | 'no-console': ['error', {allow: ['warn', 'error']}],
49 | 'prettier/prettier': 'error'
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Code Owners
2 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
3 |
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
10 |
11 | If you leave out sections there is a high likelihood it will be moved to our [GitHub Discussions](https://github.com/WebDevStudios/nextjs-wordpress-starter/discussions).
12 |
13 | **Describe the bug**
14 | A clear and concise description of what the bug is.
15 |
16 | **To Reproduce**
17 | Steps to reproduce the behavior:
18 |
19 | 1. Go to '...'
20 | 2. Click on '....'
21 | 3. Scroll down to '....'
22 | 4. See error
23 |
24 | **Expected behavior**
25 | A clear and concise description of what you expected to happen.
26 |
27 | **Screenshots**
28 | If applicable, add screenshots to help explain your problem.
29 |
30 | **Desktop (please complete the following information):**
31 |
32 | - OS: [e.g. iOS]
33 | - Browser [e.g. chrome, safari]
34 | - Version [e.g. 22]
35 |
36 | **Smartphone (please complete the following information):**
37 |
38 | - Device: [e.g. iPhone6]
39 | - OS: [e.g. iOS8.1]
40 | - Browser [e.g. stock browser, safari]
41 | - Version [e.g. 22]
42 |
43 | **Additional context**
44 | Add any other context about the problem here.
45 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | **Please create your feature request as a [GitHub Discussion](https://github.com/WebDevStudios/nextjs-wordpress-starter/discussions).**
10 |
11 | Thank you!
12 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Closes #
2 |
3 | ### Description
4 |
5 | What does your Pull Request do? Give some context...
6 |
7 | ### Screenshot
8 |
9 | If possible, add some screenshots of your feature.
10 |
11 | ### Verification
12 |
13 | How will a stakeholder test this?
14 |
15 | 1.
16 | 2.
17 | 3.
18 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: 'github-actions'
4 |
5 | - package-ecosystem: 'npm'
6 | directory: '/'
7 | schedule:
8 | interval: 'weekly'
9 | day: 'monday'
10 |
11 | - package-ecosystem: 'npm'
12 | directory: '/docs'
13 | schedule:
14 | interval: 'weekly'
15 | day: 'monday'
16 |
--------------------------------------------------------------------------------
/.github/workflows/assertions.yml:
--------------------------------------------------------------------------------
1 | name: Assertions
2 |
3 | on:
4 | pull_request:
5 | branches: main
6 |
7 | workflow_dispatch:
8 |
9 | jobs:
10 | lint:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout repository
15 | uses: actions/checkout@v3
16 | with:
17 | token: ${{ github.token }}
18 |
19 | - name: Setup Node
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: 'lts/*'
23 | cache: 'npm'
24 |
25 | - name: Install Dependencies
26 | run: |
27 | npm i --legacy-peer-deps
28 | cd docs
29 | npm i --legacy-peer-deps
30 |
31 | - name: Lint Docs
32 | run: |
33 | cd docs
34 | npm run lint
35 |
36 | - name: Lint Frontend
37 | run: |
38 | npm run lint
39 |
40 | - name: Run Frontend Tests
41 | run: |
42 | npm run test:jest
43 |
--------------------------------------------------------------------------------
/.github/workflows/chromatic.yml:
--------------------------------------------------------------------------------
1 | name: Chromatic
2 |
3 | on:
4 | pull_request:
5 | branches: main
6 |
7 | workflow_dispatch:
8 |
9 | jobs:
10 | chromatic:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Checkout Repository
15 | uses: actions/checkout@v3
16 | with:
17 | token: ${{ github.token }}
18 | fetch-depth: 0
19 |
20 | - name: Setup Node
21 | uses: actions/setup-node@v3
22 | with:
23 | node-version: 'lts/*'
24 | cache: 'npm'
25 |
26 | - name: Install Dependencies
27 | run: npm i --legacy-peer-deps
28 |
29 | - name: Publish to Chromatic
30 | uses: chromaui/action@v1
31 | with:
32 | token: ${{ github.token }}
33 | projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
34 | workingDir: /
35 | exitZeroOnChanges: true
36 | exitOnceUploaded: true
37 | skip: 'dependabot/**'
38 |
--------------------------------------------------------------------------------
/.github/workflows/security.yml:
--------------------------------------------------------------------------------
1 | name: 'Security'
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 |
7 | workflow_dispatch:
8 |
9 | jobs:
10 | analyze:
11 | name: Analyze
12 | runs-on: ubuntu-latest
13 | permissions:
14 | actions: read
15 | contents: read
16 | security-events: write
17 |
18 | strategy:
19 | fail-fast: false
20 | matrix:
21 | language: ['javascript']
22 |
23 | steps:
24 | - name: Checkout repository
25 | uses: actions/checkout@v3
26 |
27 | - name: Initialize CodeQL
28 | uses: github/codeql-action/init@v2
29 | with:
30 | languages: ${{ matrix.language }}
31 | queries: security-extended
32 |
33 | - name: Perform CodeQL Analysis
34 | uses: github/codeql-action/analyze@v2
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # testing
9 | /overage
10 |
11 | # next.js
12 | .next
13 | out
14 | .vercel
15 |
16 | # production
17 | build
18 |
19 | # misc
20 | .DS_Store
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env
29 | .env.local
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 | wpe.json
34 |
35 | # storybook
36 | build-storybook.log
37 | storybook-static/*
38 |
39 | # docusaurus
40 | .docusaurus
41 |
42 | # composer
43 | vendor
44 |
45 | # vercel
46 | .vercel
47 |
--------------------------------------------------------------------------------
/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "MD003": {"style": "atx"},
3 | "MD007": {"indent": 2},
4 | "MD026": {"punctuation": ".,;!。,;:!"},
5 | "default": true,
6 | "line-length": false,
7 | "no-duplicate-heading": false,
8 | "no-hard-tabs": false,
9 | "no-inline-html": false,
10 | "ol-prefix": false,
11 | "whitespace": false
12 | }
13 |
--------------------------------------------------------------------------------
/.markdownlintignore:
--------------------------------------------------------------------------------
1 | !/.*.js
2 | *.min.js
3 | .*cache
4 | .next/
5 | __tests__/
6 | backend/
7 | build/
8 | dist/
9 | docs/
10 | node_modules/
11 | public/
12 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | lts/*
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | !/.*.js
2 | *.min.js
3 | .*cache
4 | .next/
5 | build/
6 | dist/
7 | docs/
8 | node_modules/
9 | public/
10 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | // https://prettier.io/docs/en/configuration.html
2 | module.exports = {
3 | tabWidth: 2,
4 | useTabs: false,
5 | singleQuote: true,
6 | bracketSpacing: false,
7 | semi: false,
8 | trailingComma: 'none'
9 | }
10 |
--------------------------------------------------------------------------------
/.storybook/manager.js:
--------------------------------------------------------------------------------
1 | import {addons} from '@storybook/addons'
2 | import theme from './theme'
3 |
4 | /**
5 | * Configure Storybook features and behavior.
6 | *
7 | * @see https://storybook.js.org/docs/react/configure/features-and-behavior
8 | */
9 | addons.setConfig({
10 | isFullscreen: false,
11 | showNav: true,
12 | showPanel: true,
13 | panelPosition: 'bottom',
14 | sidebarAnimations: true,
15 | enableShortcuts: true,
16 | isToolshown: true,
17 | theme: theme,
18 | selectedPanel: undefined,
19 | initialActive: 'sidebar',
20 | showRoots: false
21 | })
22 |
--------------------------------------------------------------------------------
/.storybook/theme.js:
--------------------------------------------------------------------------------
1 | import {create} from '@storybook/theming/create'
2 |
3 | /**
4 | * Configure Storybook theme.
5 | *
6 | * @see https://storybook.js.org/docs/react/configure/theming#create-a-theme-quickstart
7 | */
8 | export default create({
9 | base: 'light',
10 | brandTitle: 'Next.js WordPress Starter',
11 | brandUrl: 'https://nextjs-wordpress-starter.vercel.app'
12 | })
13 |
--------------------------------------------------------------------------------
/.stylelintignore:
--------------------------------------------------------------------------------
1 | !/.*.js
2 | *.min.js
3 | .*cache
4 | .next/
5 | __tests__/
6 | backend/
7 | build/
8 | dist/
9 | docs/
10 | node_modules/
11 | public/
12 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | // https://stylelint.io/user-guide/configure
2 | module.exports = {
3 | extends: ['stylelint-config-standard'],
4 | rules: {
5 | 'at-rule-no-unknown': [
6 | true,
7 | {
8 | ignoreAtRules: [
9 | 'tailwind',
10 | 'layer',
11 | 'apply',
12 | 'variants',
13 | 'responsive',
14 | 'screen'
15 | ]
16 | }
17 | ],
18 | 'declaration-block-trailing-semicolon': null,
19 | 'max-line-length': null,
20 | 'no-descending-specificity': null,
21 | 'selector-class-pattern': null,
22 | 'string-quotes': 'single'
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "DavidAnson.vscode-markdownlint",
4 | "bradlc.vscode-tailwindcss",
5 | "christian-kohler.npm-intellisense",
6 | "christian-kohler.path-intellisense",
7 | "clinyong.vscode-css-modules",
8 | "coenraads.bracket-pair-colorizer-2",
9 | "csstools.postcss",
10 | "dbaeumer.vscode-eslint",
11 | "editorconfig.editorconfig",
12 | "esbenp.prettier-vscode",
13 | "graphql.vscode-graphql",
14 | "gruntfuggly.todo-tree",
15 | "mikestead.dotenv",
16 | "naumovs.color-highlight",
17 | "nucllear.vscode-extension-auto-import",
18 | "silvenon.mdx",
19 | "stylelint.vscode-stylelint",
20 | "zignd.html-css-class-completion"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | // Enable format with prettier on save, configure import organizing.
3 | "editor.formatOnSave": true,
4 | "editor.codeActionsOnSave": {
5 | "source.organizeImports": true
6 | },
7 | "[javascriptreact]": {
8 | "editor.defaultFormatter": "esbenp.prettier-vscode"
9 | },
10 |
11 | // This will get emmet working in JSX.
12 | "emmet.includeLanguages": {
13 | "javascript": "javascriptreact"
14 | },
15 | "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false
16 | }
17 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Please see for more information.
4 |
--------------------------------------------------------------------------------
/__tests__/jest/components/atoms/Breadcrumbs.test.js:
--------------------------------------------------------------------------------
1 | import Breadcrumbs from '@/components/atoms/Breadcrumbs'
2 | import {render} from '@testing-library/react'
3 |
4 | test('render Breadcrumbs', () => {
5 | const props = {
6 | breadcrumbs: [
7 | {
8 | text: 'Home',
9 | url: 'http://localhost:3000/'
10 | },
11 | {
12 | text: 'Blog',
13 | url: 'http://localhost:3000/blog'
14 | },
15 | {
16 | text: 'Lorem Ipsum',
17 | url: 'http://localhost:3000/2020/07/01/lorem-ipsum'
18 | }
19 | ]
20 | }
21 |
22 | const {container} = render()
23 |
24 | expect(container.firstElementChild).toHaveClass('breadcrumbs')
25 |
26 | const lists = container.firstElementChild.querySelectorAll('li')
27 |
28 | expect(lists).toHaveLength(3)
29 |
30 | // Breadcrumbs should be in order
31 | // Home -> Blog -> Lorem Ipsum
32 |
33 | expect(lists[0].querySelector('a')).toHaveAttribute(
34 | 'href',
35 | 'http://localhost:3000/'
36 | )
37 | expect(lists[0]).toHaveTextContent('Home')
38 |
39 | expect(lists[1].querySelector('a')).toHaveAttribute(
40 | 'href',
41 | 'http://localhost:3000/blog'
42 | )
43 | expect(lists[1]).toHaveTextContent('Blog')
44 |
45 | expect(lists[2].querySelector('a')).toHaveAttribute(
46 | 'href',
47 | 'http://localhost:3000/2020/07/01/lorem-ipsum'
48 | )
49 | expect(lists[2]).toHaveTextContent('Lorem Ipsum')
50 | })
51 |
--------------------------------------------------------------------------------
/__tests__/jest/components/atoms/Code.test.js:
--------------------------------------------------------------------------------
1 | import Code from '@/components/atoms/Code'
2 | import {render} from '@testing-library/react'
3 |
4 | test('render Code with id, className, content, and style props', () => {
5 | const props = {
6 | id: 'test-code-id',
7 | className: 'test-class-code',
8 | content: 'this is a code block!
',
9 | style: {
10 | backgroundColor: 'blue'
11 | }
12 | }
13 |
14 | const {container} = render(
)
15 |
16 | const parentDiv = container.querySelector('#test-code-id')
17 |
18 | expect(parentDiv).not.toBeNull()
19 | expect(parentDiv).toHaveStyle({
20 | backgroundColor: 'blue'
21 | })
22 |
23 | const codeDiv = container.querySelector('code')
24 | expect(codeDiv).toHaveClass('language-test-class-code')
25 | expect(codeDiv).toHaveTextContent('this is a code block!
')
26 | })
27 |
--------------------------------------------------------------------------------
/__tests__/jest/components/atoms/Inputs/Checkbox.test.js:
--------------------------------------------------------------------------------
1 | import Checkbox from '@/components/atoms/Inputs/Checkbox'
2 | import Form from '@/components/molecules/Form'
3 | import {render} from '@testing-library/react'
4 |
5 | test('render Checkbox with className, id, label, name, and value', () => {
6 | const props = {
7 | className: 'checkbox-test-cls',
8 | id: 'awesome-id',
9 | label: 'Are you awesome',
10 | name: 'awesome',
11 | value: 'am-awesome'
12 | }
13 |
14 | const form = (
15 |
19 | )
20 |
21 | const {container} = render(form)
22 |
23 | const checkboxContainer = container.querySelector('.checkbox-test-cls')
24 |
25 | expect(checkboxContainer).not.toBeNull()
26 | expect(checkboxContainer.querySelector('label')).toHaveAttribute(
27 | 'for',
28 | 'awesome-id'
29 | )
30 | expect(checkboxContainer).toHaveTextContent('Are you awesome')
31 |
32 | const checkboxInput = checkboxContainer.querySelector('input')
33 |
34 | expect(checkboxInput).toHaveAttribute('name', 'awesome')
35 | expect(checkboxInput).toHaveAttribute('type', 'checkbox')
36 | expect(checkboxInput).toHaveAttribute('value', 'am-awesome')
37 | })
38 |
--------------------------------------------------------------------------------
/__tests__/jest/components/atoms/Logo.test.js:
--------------------------------------------------------------------------------
1 | import Logo from '@/components/atoms/Logo'
2 | import {render} from '@testing-library/react'
3 |
4 | test('render Logo with className, and type-dark props', () => {
5 | const props = {
6 | className: 'logo-ctm-cls',
7 | type: 'dark'
8 | }
9 |
10 | const {container} = render()
11 |
12 | const logo = container.querySelector('svg')
13 |
14 | expect(logo).toHaveClass('logo-ctm-cls')
15 | expect(logo).toHaveAttribute('fill', '#414141')
16 | })
17 |
18 | test('render Logo with type-light prop', () => {
19 | const props = {
20 | type: 'light'
21 | }
22 |
23 | const {container} = render()
24 |
25 | const logo = container.querySelector('svg')
26 |
27 | expect(logo).toHaveAttribute('fill', '#f9fbfd')
28 | })
29 |
--------------------------------------------------------------------------------
/__tests__/jest/components/atoms/Quote.test.js:
--------------------------------------------------------------------------------
1 | import Quote from '@/components/atoms/Quote'
2 | import {render} from '@testing-library/react'
3 |
4 | test('render Quote with className, id, style and value props', () => {
5 | const props = {
6 | className: 'ctm-cls-q',
7 | id: 'ctm-id-q',
8 | value:
9 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sed molestie lorem.'
10 | }
11 |
12 | const {container} = render(
)
13 |
14 | expect(container.firstElementChild).toHaveClass('ctm-cls-q')
15 | expect(container.firstElementChild).toHaveAttribute('id', 'ctm-id-q')
16 | expect(container).toHaveTextContent(
17 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sed molestie lorem.'
18 | )
19 | })
20 |
21 | test('render Quote with citation and value props', () => {
22 | const props = {
23 | citation: 'Lorem',
24 | value: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
25 | }
26 |
27 | const {container} = render(
)
28 |
29 | expect(container).toHaveTextContent(
30 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
31 | )
32 | expect(container.querySelector('.cite')).toHaveTextContent('Lorem')
33 | })
34 |
--------------------------------------------------------------------------------
/__tests__/jest/components/atoms/RichText.test.js:
--------------------------------------------------------------------------------
1 | import RichText from '@/components/atoms/RichText'
2 | import {render} from '@testing-library/react'
3 |
4 | test('render RichText with children, className, dropCap and style props', () => {
5 | const props = {
6 | className: 'custom-rt-cls',
7 | dropCap: true,
8 | style: {
9 | backgroundColor: 'red'
10 | }
11 | }
12 |
13 | const {container} = render(
14 | This is a rich text example.
15 | )
16 |
17 | expect(container.firstElementChild).toHaveClass('dropcap custom-rt-cls')
18 | expect(container.firstElementChild).toHaveStyle({
19 | backgroundColor: 'red'
20 | })
21 | expect(container).toHaveTextContent('This is a rich text example.')
22 | })
23 |
24 | test('render RichText with attributes, children, id and tag props', () => {
25 | const props = {
26 | attributes: {
27 | 'data-att': true
28 | },
29 | id: 'rt-ctm-id',
30 | tag: 'span'
31 | }
32 |
33 | const {container} = render(
34 | This is a span example.
35 | )
36 |
37 | const richTextSpan = container.querySelector('span')
38 |
39 | expect(richTextSpan).not.toHaveClass('dropcap')
40 | expect(richTextSpan).toHaveAttribute('id', 'rt-ctm-id')
41 | expect(richTextSpan).toHaveAttribute('data-att', 'true')
42 | expect(richTextSpan).toHaveTextContent('This is a span example.')
43 | })
44 |
--------------------------------------------------------------------------------
/__tests__/jest/components/atoms/Separator.test.js:
--------------------------------------------------------------------------------
1 | import Separator from '@/components/atoms/Separator'
2 | import {render} from '@testing-library/react'
3 |
4 | test('render Separator with anchor, and className props', () => {
5 | const props = {
6 | anchor: 'test-anchor',
7 | className: 'test-cls'
8 | }
9 |
10 | const {container} = render()
11 |
12 | expect(container.firstElementChild).toHaveClass('test-cls')
13 | expect(container.firstElementChild).toHaveAttribute('id', 'test-anchor')
14 | })
15 |
--------------------------------------------------------------------------------
/__tests__/jest/components/atoms/Spacer.test.js:
--------------------------------------------------------------------------------
1 | import Spacer from '@/components/atoms/Spacer'
2 | import {render} from '@testing-library/react'
3 |
4 | test('render Spacer with height prop', () => {
5 | const props = {
6 | height: 12
7 | }
8 |
9 | const {container} = render()
10 |
11 | const computedHeight = 12 / 16
12 |
13 | expect(container.firstElementChild).toHaveStyle({
14 | height: `${computedHeight}rem`
15 | })
16 | })
17 |
--------------------------------------------------------------------------------
/__tests__/jest/components/molecules/ButtonGroup.test.js:
--------------------------------------------------------------------------------
1 | import ButtonGroup from '@/components/molecules/ButtonGroup'
2 | import {render} from '@testing-library/react'
3 |
4 | test('render ButtonGroup with id, orientation=horizontal, contentJustification=left, and children props', () => {
5 | const props = {
6 | id: 'test-id-btngrp',
7 | orientation: 'horizontal',
8 | contentJustification: 'left'
9 | }
10 |
11 | const {container} = render(
12 |
13 |
14 |
15 | )
16 |
17 | expect(container.firstElementChild).toHaveClass('horizontal left')
18 | expect(container.firstElementChild).toHaveAttribute('id', 'test-id-btngrp')
19 | expect(container.firstElementChild.innerHTML).toBe(
20 | ''
21 | )
22 | })
23 |
24 | test('render ButtonGroup with id, orientation=vertical, contentJustification=right, and children props', () => {
25 | const props = {
26 | orientation: 'vertical',
27 | contentJustification: 'right'
28 | }
29 |
30 | const {container} = render(
31 |
32 | Lorem ipsum
33 |
34 | )
35 |
36 | expect(container.firstElementChild).toHaveClass('vertical right')
37 | expect(container.firstElementChild.innerHTML).toBe('Lorem ipsum
')
38 | })
39 |
--------------------------------------------------------------------------------
/__tests__/jest/components/molecules/Form.test.js:
--------------------------------------------------------------------------------
1 | import Text from '@/components/atoms/Inputs/Text'
2 | import Form from '@/components/molecules/Form'
3 | import {render} from '@testing-library/react'
4 |
5 | test('render Form with children, className, formDefaults id, title, and onSubmit props', () => {
6 | const props = {
7 | className: 'test-form-cls',
8 | formDefaults: {
9 | username: '',
10 | password: ''
11 | },
12 | id: 'test-form-id',
13 | title: 'Test form',
14 | onSubmit: () => {}
15 | }
16 |
17 | const {container} = render(
18 |
23 | )
24 |
25 | const form = container.querySelector('#test-form-id')
26 |
27 | expect(form).toHaveClass('test-form-cls')
28 |
29 | expect(form.querySelector('h1')).toHaveTextContent('Login Form')
30 |
31 | expect(form.querySelector('input#username')).not.toBeNull()
32 | expect(form.querySelector('input#password')).not.toBeNull()
33 | expect(form.querySelector('button[type="submit"]')).not.toBeNull()
34 | })
35 |
--------------------------------------------------------------------------------
/backend/README.md:
--------------------------------------------------------------------------------
1 | # Backend Setup
2 |
3 | To learn more about setting up the backend, please [read the documentation](https://webdevstudios.github.io/nextjs-wordpress-starter/docs/backend).
4 |
--------------------------------------------------------------------------------
/components/atoms/Breadcrumbs/Breadcrumbs.js:
--------------------------------------------------------------------------------
1 | import cn from 'classnames'
2 | import Link from 'next/link'
3 | import PropTypes from 'prop-types'
4 | import styles from './Breadcrumbs.module.css'
5 |
6 | /**
7 | * Render the Breadcrumbs component.
8 | *
9 | * @author WebDevStudios
10 | * @param {object} props The component attributes as props.
11 | * @param {Array} props.breadcrumbs The breadcrumb array.
12 | * @return {Element} The Breadcrumbs component.
13 | */
14 | export default function Breadcrumbs({breadcrumbs}) {
15 | return (
16 | <>
17 | {!!breadcrumbs?.length && (
18 |
19 | {breadcrumbs.map((breadcrumb, index) => (
20 | -
21 |
22 | {breadcrumb?.text}
23 |
24 | {index < breadcrumbs.length - 1 && (
25 | »
26 | )}
27 |
28 | ))}
29 |
30 | )}
31 | >
32 | )
33 | }
34 |
35 | Breadcrumbs.propTypes = {
36 | breadcrumbs: PropTypes.array.isRequired
37 | }
38 |
--------------------------------------------------------------------------------
/components/atoms/Breadcrumbs/Breadcrumbs.module.css:
--------------------------------------------------------------------------------
1 | ul.breadcrumbs {
2 | @apply mb-8 flex;
3 |
4 | & li {
5 | @apply uppercase text-xs font-semibold p-0;
6 |
7 | & .sep {
8 | @apply px-2 opacity-50 font-normal;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/components/atoms/Breadcrumbs/Breadcrumbs.stories.mdx:
--------------------------------------------------------------------------------
1 | import {Meta, Story, Canvas} from '@storybook/addon-docs/blocks'
2 |
3 | import Breadcrumbs from '.'
4 |
5 | export const crumbs = [
6 | {
7 | text: 'Home',
8 | url: '/'
9 | },
10 | {
11 | text: 'About',
12 | url: '/about'
13 | },
14 | {
15 | text: 'Our Team',
16 | url: '/about/team'
17 | }
18 | ]
19 |
20 |
21 |
22 | # Breadcrumbs
23 |
24 | Use this component to display site breadcrumbs.
25 |
26 |
31 |
--------------------------------------------------------------------------------
/components/atoms/Breadcrumbs/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Breadcrumbs'
2 |
--------------------------------------------------------------------------------
/components/atoms/Button/Button.module.css:
--------------------------------------------------------------------------------
1 | .button {
2 | @apply inline-flex items-center justify-center rounded border cursor-pointer font-semibold mr-2 mb-2;
3 |
4 | & svg {
5 | @apply ml-4;
6 | }
7 |
8 | &.iconLeft {
9 | @apply flex-row-reverse;
10 |
11 | & svg {
12 | @apply mr-12 ml-0;
13 | }
14 | }
15 |
16 | &:hover,
17 | &:focus,
18 | &:active:not([disabled]) {
19 | @apply underline;
20 | }
21 |
22 | /* SIZES */
23 | &.lg {
24 | @apply px-4 py-3 text-lg;
25 | }
26 |
27 | &.md {
28 | @apply px-3 py-2;
29 | }
30 |
31 | &.sm {
32 | @apply px-1.5 py-0.5;
33 | }
34 |
35 | &.fluid {
36 | @apply block w-full text-center;
37 | }
38 |
39 | /* TYPES */
40 | &.primary {
41 | @apply bg-white;
42 |
43 | &.disabled {
44 | @apply opacity-50;
45 | }
46 | }
47 |
48 | &.secondary {
49 | @apply bg-black text-white;
50 |
51 | &.disabled {
52 | @apply opacity-50;
53 | }
54 | }
55 |
56 | &.disabled {
57 | @apply cursor-not-allowed;
58 | }
59 |
60 | &.iconOnly {
61 | & svg {
62 | @apply m-0;
63 | }
64 | }
65 |
66 | /* STYLES */
67 | &.styleOutline {
68 | border: 2px solid;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/components/atoms/Button/index.js:
--------------------------------------------------------------------------------
1 | export {ButtonInner, default} from './Button'
2 |
--------------------------------------------------------------------------------
/components/atoms/Code/Code.module.css:
--------------------------------------------------------------------------------
1 | .code {
2 | @apply mb-8;
3 |
4 | & > pre {
5 | @apply rounded;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/components/atoms/Code/Code.stories.mdx:
--------------------------------------------------------------------------------
1 | import {Meta, Story, Canvas} from '@storybook/addon-docs/blocks'
2 |
3 | import Code from '.'
4 |
5 |
6 |
7 | # Code
8 |
9 | Use this component to display a code block.
10 |
11 |
19 |
20 | ## Controls
21 |
22 | Play around with `
` props in the [Canvas tab of the Controls story](?path=/story/design-system-atoms-code--controls).
23 |
24 | export const Template = (args) =>
25 |
26 |
',
31 | className: 'html custom-class'
32 | }}
33 | >
34 | {Template.bind({})}
35 |
36 |
37 |
--------------------------------------------------------------------------------
/components/atoms/Code/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Code'
2 |
--------------------------------------------------------------------------------
/components/atoms/Columns/Columns.module.css:
--------------------------------------------------------------------------------
1 | .columns {
2 | @apply flex gap-8;
3 |
4 | &.columnStacked {
5 | @apply flex-col md:flex-row;
6 | }
7 |
8 | &.alignCenter {
9 | & .column {
10 | @apply justify-center;
11 | }
12 | }
13 |
14 | &.alignBottom {
15 | & .column {
16 | @apply justify-end;
17 | }
18 | }
19 |
20 | &.hasBackground {
21 | @apply p-8;
22 | }
23 |
24 | & .column {
25 | @apply flex flex-col w-full;
26 |
27 | &.hasBackground {
28 | @apply p-4;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/components/atoms/Columns/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Columns'
2 |
--------------------------------------------------------------------------------
/components/atoms/Container/Container.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import React from 'react'
3 | import styles from './Container.module.css'
4 | import cn from 'classnames'
5 |
6 | /**
7 | * Render the Container component.
8 | *
9 | * @param {object} props Container component props.
10 | * @param {object} props.children Container children.
11 | * @param {boolean} props.paddingTop Should container render top padding.
12 | * @param {boolean} props.paddingBtm Should container render bottom padding.
13 | * @return {Element} The Container component.
14 | */
15 | export default function Container({children, paddingTop, paddingBtm}) {
16 | return (
17 |
24 | {children && children}
25 |
26 | )
27 | }
28 |
29 | Container.propTypes = {
30 | children: PropTypes.node,
31 | paddingTop: PropTypes.bool,
32 | paddingBtm: PropTypes.bool
33 | }
34 |
35 | Container.defaultProps = {
36 | paddingTop: true,
37 | paddingBtm: true
38 | }
39 |
--------------------------------------------------------------------------------
/components/atoms/Container/Container.module.css:
--------------------------------------------------------------------------------
1 | .containerW {
2 | @apply container;
3 |
4 | &.paddingTop {
5 | @apply pt-12;
6 | }
7 |
8 | &.paddingBtm {
9 | @apply pb-12;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/components/atoms/Container/Container.stories.mdx:
--------------------------------------------------------------------------------
1 | import {Canvas, Meta, Story} from '@storybook/addon-docs/blocks'
2 | import Container from './'
3 |
4 |
5 |
6 | # Component
7 |
8 | Use this to wrap another component in a container. This will limit the max width of everything inside of it. See [the tailwind container docs](https://tailwindcss.com/docs/container) for details.
9 |
10 |
17 |
--------------------------------------------------------------------------------
/components/atoms/Container/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Container'
2 |
--------------------------------------------------------------------------------
/components/atoms/ExitPreview/ExitPreview.js:
--------------------------------------------------------------------------------
1 | import Link from 'next/link'
2 |
3 | /**
4 | * Renders an anchor to exit Preview Mode.
5 | *
6 | * @param {object} props The component as props.
7 | * @param {object} props.preview Checks if a preview exists.
8 | * @return {Element | null} The ExitPreview component.
9 | */
10 | export default function ExitPreview({preview}) {
11 | if (preview) {
12 | return (
13 |
14 | This page is a preview.
15 |
16 | Exit preview mode
17 |
18 |
19 | )
20 | }
21 |
22 | return null
23 | }
24 |
--------------------------------------------------------------------------------
/components/atoms/ExitPreview/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './ExitPreview.js'
2 |
--------------------------------------------------------------------------------
/components/atoms/Heading/Heading.js:
--------------------------------------------------------------------------------
1 | import createMarkup from '@/functions/createMarkup'
2 | import PropTypes from 'prop-types'
3 | import React from 'react'
4 |
5 | /**
6 | * Render the Heading component.
7 | *
8 | * @param {object} props The props object.
9 | * @param {string} props.children The elements or text you'd like to render inside the heading.
10 | * @param {string} props.className The optional classname.
11 | * @param {string} props.id The optional ID.
12 | * @param {object} props.style The style attributes.
13 | * @param {string} props.tag The tag name you'd like the heading to render as.
14 | * @return {Element} The Heading element.
15 | */
16 | export default function Heading({children, className, id, style, tag}) {
17 | if (typeof children === 'string') {
18 | return React.createElement(tag, {
19 | className,
20 | id,
21 | style,
22 | dangerouslySetInnerHTML: createMarkup(children)
23 | })
24 | } else {
25 | return React.createElement(
26 | tag,
27 | {
28 | className,
29 | id,
30 | style
31 | },
32 | children
33 | )
34 | }
35 | }
36 |
37 | Heading.propTypes = {
38 | children: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
39 | className: PropTypes.string,
40 | id: PropTypes.string,
41 | style: PropTypes.object,
42 | tag: PropTypes.oneOf(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
43 | }
44 |
45 | Heading.defaultProps = {
46 | tag: 'h1'
47 | }
48 |
--------------------------------------------------------------------------------
/components/atoms/Heading/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Heading'
2 |
--------------------------------------------------------------------------------
/components/atoms/Icon/index.js:
--------------------------------------------------------------------------------
1 | export {default, sizeToPx} from './Icon'
2 |
--------------------------------------------------------------------------------
/components/atoms/Image/Image.module.css:
--------------------------------------------------------------------------------
1 | .image {
2 | @apply mb-8;
3 |
4 | & img {
5 | @apply block rounded;
6 | }
7 |
8 | & .caption {
9 | @apply text-center text-xs pt-4;
10 | }
11 |
12 | &.hasImageFill {
13 | @apply mb-0;
14 | }
15 |
16 | & .imageFill {
17 | @apply object-cover rounded;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/components/atoms/Image/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Image'
2 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/Checkbox/Checkbox.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import {Field} from 'formik'
3 |
4 | /**
5 | * Render Checkbox component.
6 | *
7 | * @param {object} props The component attributes as props.
8 | * @param {string} props.className Input className.
9 | * @param {string|number} props.id Input id.
10 | * @param {string} props.label Input label.
11 | * @param {string} props.name Input name.
12 | * @param {string} props.value Input value.
13 | * @return {Element} The Checkbox component.
14 | */
15 | export default function Checkbox({className, id, label, name, value}) {
16 | return (
17 |
18 | {label && (
19 |
23 | )}
24 |
25 | )
26 | }
27 |
28 | Checkbox.propTypes = {
29 | className: PropTypes.string,
30 | id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
31 | label: PropTypes.string.isRequired,
32 | name: PropTypes.string.isRequired,
33 | value: PropTypes.string
34 | }
35 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/Checkbox/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Checkbox'
2 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/CheckboxGroup/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './CheckboxGroup'
2 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/InputError/InputError.js:
--------------------------------------------------------------------------------
1 | import PropTypes from 'prop-types'
2 | import {ErrorMessage} from 'formik'
3 | import styles from './InputError.module.css'
4 |
5 | /**
6 | * Render the InputError component.
7 | *
8 | * @param {object} props The component attributes as props.
9 | * @param {string|number} props.name Input id.
10 | * @return {Element} The InputError component.
11 | */
12 | export default function InputError({name}) {
13 | return (
14 |
15 |
16 |
17 | )
18 | }
19 |
20 | InputError.propTypes = {
21 | name: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
22 | }
23 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/InputError/InputError.module.css:
--------------------------------------------------------------------------------
1 | .inputError {
2 | color: #df1642;
3 | }
4 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/InputError/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './InputError'
2 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/Select/Select.module.css:
--------------------------------------------------------------------------------
1 | .select {
2 | & select {
3 | @apply block border;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/Select/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Select'
2 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/Text/Text.module.css:
--------------------------------------------------------------------------------
1 | .text {
2 | & input {
3 | @apply rounded;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/Text/index.js:
--------------------------------------------------------------------------------
1 | export {default} from './Text'
2 |
--------------------------------------------------------------------------------
/components/atoms/Inputs/index.js:
--------------------------------------------------------------------------------
1 | export {default as Checkbox} from './Checkbox'
2 | export {default as CheckboxGroup} from './CheckboxGroup'
3 | export {default as Select} from './Select'
4 | export {default as Text} from './Text'
5 |
--------------------------------------------------------------------------------
/components/atoms/Logo/Logo.stories.mdx:
--------------------------------------------------------------------------------
1 | import {Meta, Story, Canvas} from '@storybook/addon-docs/blocks'
2 |
3 | import Logo from '.'
4 |
5 |
6 |
7 | # Logo
8 |
9 | Use this component to display a button. Use it anywhere you would use an `` or `