├── .nvmrc
├── cypress.json
├── next-env.d.ts
├── public
├── favicon.ico
├── fonts
│ ├── Gilroy-Light
│ │ ├── font.ttf
│ │ ├── font.woff
│ │ └── font.woff2
│ └── Gilroy-ExtraBold
│ │ ├── font.ttf
│ │ ├── font.woff
│ │ └── font.woff2
└── zeit.svg
├── jest.setup.ts
├── src
├── styles
│ ├── types.d.ts
│ └── global.tsx
├── pages
│ ├── _app.tsx
│ ├── _document.tsx
│ └── index.tsx
└── components
│ └── Title
│ ├── Title.test.tsx
│ ├── Title.component.tsx
│ └── Title.stories.mdx
├── cypress
├── fixtures
│ └── example.json
├── tsconfig.json
├── support
│ ├── index.d.ts
│ ├── index.js
│ └── commands.js
├── plugins
│ └── index.js
└── integration
│ └── examples
│ ├── aliasing.spec.ts
│ └── actions.spec.ts
├── .prettierrc.yaml
├── .storybook
├── preview.js
└── main.js
├── .babelrc
├── .eslintignore
├── .prettierignore
├── .gitignore
├── .dependabot
└── config.yml
├── server
├── index.ts
└── tsconfig.json
├── .kodiak.toml
├── tsconfig.json
├── .github
└── workflows
│ ├── e2e.yml
│ ├── validation.yml
│ └── chromatic.yml
├── LICENSE
├── README.md
├── package.json
└── .eslintrc.js
/.nvmrc:
--------------------------------------------------------------------------------
1 | 12.16.1
2 |
--------------------------------------------------------------------------------
/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:3000"
3 | }
4 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eXamadeus/next-emotion/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/fonts/Gilroy-Light/font.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eXamadeus/next-emotion/HEAD/public/fonts/Gilroy-Light/font.ttf
--------------------------------------------------------------------------------
/public/fonts/Gilroy-Light/font.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eXamadeus/next-emotion/HEAD/public/fonts/Gilroy-Light/font.woff
--------------------------------------------------------------------------------
/jest.setup.ts:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/no-extraneous-dependencies
2 | import '@testing-library/jest-dom/extend-expect'
3 |
--------------------------------------------------------------------------------
/public/fonts/Gilroy-Light/font.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eXamadeus/next-emotion/HEAD/public/fonts/Gilroy-Light/font.woff2
--------------------------------------------------------------------------------
/public/fonts/Gilroy-ExtraBold/font.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eXamadeus/next-emotion/HEAD/public/fonts/Gilroy-ExtraBold/font.ttf
--------------------------------------------------------------------------------
/public/fonts/Gilroy-ExtraBold/font.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eXamadeus/next-emotion/HEAD/public/fonts/Gilroy-ExtraBold/font.woff
--------------------------------------------------------------------------------
/public/fonts/Gilroy-ExtraBold/font.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eXamadeus/next-emotion/HEAD/public/fonts/Gilroy-ExtraBold/font.woff2
--------------------------------------------------------------------------------
/src/styles/types.d.ts:
--------------------------------------------------------------------------------
1 | import { SerializedStyles } from '@emotion/core'
2 |
3 | export type Styling = SerializedStyles | [SerializedStyles, ...SerializedStyles[]]
4 |
--------------------------------------------------------------------------------
/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/cypress/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "es5",
5 | "lib": ["es5", "dom"],
6 | "types": ["cypress"]
7 | },
8 | "include": ["**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | trailingComma: es5
2 | tabWidth: 2
3 | semi: false
4 | singleQuote: true
5 | arrowParens: always
6 | bracketSpacing: true
7 | jsxSingleQuote: true
8 | jsxBracketSameLine: true
9 | quoteProps: consistent
10 | printWidth: 120
11 | proseWrap: always
12 |
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import { addDecorator } from '@storybook/react'
2 | import React from 'react'
3 | import { GlobalStyles } from '../src/styles/global'
4 |
5 | addDecorator((storyFn) => (
6 | <>
7 |
8 | {storyFn()}
9 | >
10 | ))
11 |
--------------------------------------------------------------------------------
/cypress/support/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare namespace Cypress {
4 | interface Chainable {
5 | /**
6 | * Example command here, this command does nothing
7 | */
8 | noop(): Chainable
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["next/babel", "@emotion/babel-preset-css-prop"],
3 | "env": {
4 | "production": {
5 | "plugins": ["@emotion/babel-plugin"]
6 | },
7 | "development": {
8 | "plugins": [["@emotion/babel-plugin", { "sourceMap": true }]]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 |
4 | # next.js
5 | /.next/
6 | /out/
7 |
8 | # output
9 | /build
10 | /storybook-static
11 |
12 | # misc
13 | **/tsconfig.json
14 | .DS_Store
15 | .env*
16 | .idea/
17 | .eslintcache
18 |
19 | # debug
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 |
9 | # next.js
10 | /.next/
11 | /out/
12 |
13 | # output
14 | /build
15 | /storybook-static
16 |
17 | # misc
18 | .DS_Store
19 | .env*
20 | .idea/
21 | .eslintcache
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 | /.pnp
4 | .pnp.js
5 |
6 | # testing
7 | /coverage
8 | cypress/videos
9 | cypresss/screenshots
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # output
16 | /build
17 | /storybook-static
18 |
19 | # misc
20 | .DS_Store
21 | .env*
22 | *.env
23 | .idea/
24 | .eslintcache
25 |
26 | # debug
27 | npm-debug.log*
28 | yarn-debug.log*
29 | yarn-error.log*
30 |
--------------------------------------------------------------------------------
/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import { AppComponent } from 'next/dist/next-server/lib/router/router'
2 | import Head from 'next/head'
3 | import * as React from 'react'
4 |
5 | const App: AppComponent = ({ Component, pageProps }) => {
6 | return (
7 | <>
8 |
9 | Next.js Template
10 |
11 |
12 | >
13 | )
14 | }
15 |
16 | export default App
17 |
--------------------------------------------------------------------------------
/src/components/Title/Title.test.tsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/dom'
2 | import { render } from '@testing-library/react'
3 | import * as React from 'react'
4 |
5 | import { Title } from './Title.component'
6 |
7 | describe('Title Component', () => {
8 | it('renders', async () => {
9 | render()
10 | expect(await screen.findByText('Example Component')).toBeInTheDocument()
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/.dependabot/config.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | update_configs:
3 | - package_manager: 'javascript'
4 | directory: '/'
5 | update_schedule: 'weekly'
6 | default_labels:
7 | - 'dependencies'
8 | - 'automerge'
9 | automerged_updates:
10 | - match:
11 | dependency_type: 'development'
12 | update_type: 'semver:minor'
13 | - match:
14 | dependency_type: 'production'
15 | update_type: 'semver:patch'
16 |
--------------------------------------------------------------------------------
/server/index.ts:
--------------------------------------------------------------------------------
1 | import { createServer } from 'http'
2 | import { parse } from 'url'
3 |
4 | import next from 'next'
5 |
6 | const port = parseInt(process.env.PORT || '3000', 10)
7 | const app = next({})
8 | const handle = app.getRequestHandler()
9 |
10 | app.prepare().then(() => {
11 | createServer((req, res) => {
12 | const parsedUrl = parse(req.url || '', true)
13 |
14 | handle(req, res, parsedUrl)
15 | }).listen(port, () => {
16 | console.log(`> Ready on http://localhost:${port}`)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/.kodiak.toml:
--------------------------------------------------------------------------------
1 | # .kodiak.toml
2 | # Minimal config. version is the only required field.
3 | version = 1
4 |
5 | [approve]
6 | auto_approve_usernames = ["dependabot","dependabot-preview"]
7 |
8 | [merge]
9 | method = "squash"
10 | delete_branch_on_merge = true
11 | notify_on_conflict = true
12 |
13 | [merge.message]
14 | title = "pull_request_title" # default: "github_default"
15 | body = "pull_request_body" # default: "github_default"
16 | # remove html comments to auto remove PR templates.
17 | strip_html_comments = true # default: false
18 | include_pull_request_author = true
19 |
--------------------------------------------------------------------------------
/src/components/Title/Title.component.tsx:
--------------------------------------------------------------------------------
1 | import { css } from '@emotion/core'
2 | import * as React from 'react'
3 |
4 | import { Styling } from '../../styles/types'
5 |
6 | const baseStyle = css`
7 | font-size: 3rem;
8 | `
9 |
10 | export type ExampleProps = {
11 | /**
12 | * Title content
13 | */
14 | content: string
15 | styles?: Styling
16 | }
17 |
18 | export const Title: React.FC = ({ content, styles }) => (
19 |
20 | {content}
21 |
22 | )
23 |
24 | Title.displayName = 'Title'
25 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "es5",
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "strict": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noEmit": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve"
17 | },
18 | "exclude": ["node_modules", "cypress"],
19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
20 | }
21 |
--------------------------------------------------------------------------------
/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "outDir": "dist",
6 | "target": "es2017",
7 | "isolatedModules": false,
8 | "lib": [
9 | // This is here only as a workaround
10 | // for https://github.com/DefinitelyTyped/DefinitelyTyped/issues/36082
11 | // It shouldn't be here, but I can't find anything better for now...
12 | "dom",
13 | "es2017",
14 | // Needed because of https://github.com/apollographql/graphql-subscriptions/issues/83
15 | "esnext.asynciterable"
16 | ]
17 | },
18 | "include": ["**/*.ts"]
19 | }
20 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: ['../**/*.stories.@(mdx|tsx)'],
3 | addons: ['@storybook/addon-actions', '@storybook/addon-links', '@storybook/addon-docs'],
4 | webpackFinal: async (config) => {
5 | config.module.rules.push({
6 | test: /\.(ts|tsx)$/,
7 | use: [
8 | require.resolve('babel-loader'),
9 | {
10 | loader: require.resolve('react-docgen-typescript-loader'),
11 | options: {
12 | tsconfigPath: require.resolve('../tsconfig.json'),
13 | },
14 | },
15 | ],
16 | })
17 | config.resolve.extensions.push('.ts', '.tsx')
18 | return config
19 | },
20 | }
21 |
--------------------------------------------------------------------------------
/src/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import Document, { Head, Html, Main, NextScript } from 'next/document'
2 | import * as React from 'react'
3 |
4 | import { GlobalStyles } from '../styles/global'
5 |
6 | class MyDocument extends Document {
7 | public render(): React.ReactElement {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | )
21 | }
22 | }
23 |
24 | export default MyDocument
25 |
--------------------------------------------------------------------------------
/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/src/components/Title/Title.stories.mdx:
--------------------------------------------------------------------------------
1 | import { css } from '@emotion/core'
2 | import { Meta, Story, Preview, ArgsTable } from '@storybook/addon-docs/blocks'
3 |
4 | import { Title } from './Title.component'
5 |
6 |
7 |
8 | # Title
9 |
10 | export const Template = (args) =>
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ## Controlled Example
19 |
20 |
26 | {Template.bind({})}
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/public/zeit.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/.github/workflows/e2e.yml:
--------------------------------------------------------------------------------
1 | name: 'Cypress'
2 | on:
3 | # Trigger the workflow on push or pull request, but only for the main branch
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | e2e:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout source code
16 | uses: actions/checkout@v1
17 | # setup node
18 | - name: Use Node.js
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: '12.16.1'
22 | # cache yarn packages
23 | - name: Restore Yarn cache
24 | uses: c-hive/gha-yarn-cache@v1
25 | # install packages and build application
26 | - name: Install dependencies via Yarn
27 | run: yarn install
28 | # run chromatic
29 | - name: Run E2E (Cypress)
30 | run: yarn e2e
31 |
--------------------------------------------------------------------------------
/.github/workflows/validation.yml:
--------------------------------------------------------------------------------
1 | name: 'Validation'
2 | on:
3 | # Trigger the workflow on push or pull request, but only for the main branch
4 | pull_request:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | validate:
10 | runs-on: ubuntu-latest
11 | steps:
12 | # checkout code
13 | - name: Checkout source code
14 | uses: actions/checkout@v1
15 | # setup node
16 | - name: Use Node.js
17 | uses: actions/setup-node@v1
18 | with:
19 | node-version: '12.16.1'
20 | # cache yarn packages
21 | - name: Restore Yarn cache
22 | uses: c-hive/gha-yarn-cache@v1
23 | # install packages and build application
24 | - name: Install dependencies via Yarn
25 | run: yarn install
26 | # run validation tests
27 | - name: Validate application
28 | run: yarn validate
29 |
--------------------------------------------------------------------------------
/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************************
3 | // This example plugins/index.js can be used to load plugins
4 | //
5 | // You can change the location of this file or turn off loading
6 | // the plugins file with the 'pluginsFile' configuration option.
7 | //
8 | // You can read more here:
9 | // https://on.cypress.io/plugins-guide
10 | // ***********************************************************
11 |
12 | // This function is called when a project is opened or re-opened (e.g. due to
13 | // the project's config changing)
14 |
15 | /**
16 | * @type {Cypress.PluginConfig}
17 | */
18 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
19 | module.exports = (on, config) => {
20 | // `on` is used to hook into various events Cypress emits
21 | // `config` is the resolved Cypress config
22 | }
23 |
--------------------------------------------------------------------------------
/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { css } from '@emotion/core'
2 | import React from 'react'
3 |
4 | import { Title } from '../components/Title/Title.component'
5 |
6 | const flexFill = css`
7 | flex: 1 0 100%;
8 | `
9 |
10 | const IndexPage: React.FC = () => (
11 |
35 | )
36 |
37 | export default IndexPage
38 |
--------------------------------------------------------------------------------
/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add("login", (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
26 |
27 | // An example noop command that does nothing...
28 | Cypress.Commands.add('noop', () => {
29 | return cy
30 | })
31 |
--------------------------------------------------------------------------------
/.github/workflows/chromatic.yml:
--------------------------------------------------------------------------------
1 | name: 'Chromatic'
2 | on:
3 | # Trigger the workflow on push or pull request, but only for the main branch
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | deploy:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout source code
16 | uses: actions/checkout@v1
17 | # setup node
18 | - name: Use Node.js
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: '12.16.1'
22 | # cache yarn packages
23 | - name: Restore Yarn cache
24 | uses: c-hive/gha-yarn-cache@v1
25 | # install packages and build application
26 | - name: Install dependencies via Yarn
27 | run: yarn install
28 | # run chromatic
29 | - name: Run Chromatic
30 | uses: chromaui/action@v1
31 | with:
32 | projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
33 | token: ${{ secrets.GITHUB_TOKEN }}
34 | buildScriptName: 'storybook:build'
35 | exitOnceUploaded: true
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Julian Coy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/cypress/integration/examples/aliasing.spec.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Aliasing', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/commands/aliasing')
6 | })
7 |
8 | it('.as() - alias a DOM element for later use', () => {
9 | // https://on.cypress.io/as
10 |
11 | // Alias a DOM element for use later
12 | // We don't have to traverse to the element
13 | // later in our code, we reference it with @
14 |
15 | cy.get('.as-table').find('tbody>tr').first().find('td').first().find('button').as('firstBtn')
16 |
17 | // when we reference the alias, we place an
18 | // @ in front of its name
19 | cy.get('@firstBtn').click()
20 |
21 | cy.get('@firstBtn').should('have.class', 'btn-success').and('contain', 'Changed')
22 | })
23 |
24 | it('.as() - alias a route for later use', () => {
25 | // Alias the route to wait for its response
26 | cy.intercept('GET', '**/comments/*').as('getComment')
27 |
28 | // we have code that gets a comment when
29 | // the button is clicked in scripts.js
30 | cy.get('.network-btn').click()
31 |
32 | // https://on.cypress.io/wait
33 | cy.wait('@getComment').its('response.statusCode').should('eq', 200)
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/src/styles/global.tsx:
--------------------------------------------------------------------------------
1 | import { css, Global } from '@emotion/core'
2 | import React from 'react'
3 |
4 | export const GlobalStyles: React.FunctionComponent = () => (
5 | <>
6 |
56 |
57 |
58 | >
59 | )
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://master--5ff5b5e3816aa60021b27e34.chromatic.com/)
2 | [](https://www.chromatic.com/library?appId=5ff5b5e3816aa60021b27e34&branch=master)
3 | [](https://dependabot.com)
4 |
5 | # Next.js Template
6 |
7 | 
8 |
9 | Want a template application with all 100's on Lighthouse? Look no further, this is a simple template application with
10 | the following stack:
11 |
12 | - [Next.js](https://nextjs.org/)
13 | - [Emotion](https://emotion.sh/)
14 | - [TypeScript](https://www.typescriptlang.org/)
15 | - [React](https://reactjs.org/)
16 | - [Cypress](https://cypress.io/)
17 | - [Storybook](https://storybook.js.org/) -
18 | [Deployed storybook example](https://master--5ff5b5e3816aa60021b27e34.chromatic.com/)
19 | - [Chromatic](https://www.chromatic.com/) -
20 | [Deployed library example](https://chromatic.com/library?appId=5ff5b5e3816aa60021b27e34&branch=master)
21 | - [GitHub](https://github.com/)
22 | - [Kodiak](https://kodiakhq.com/)
23 | - [Dependabot](https://dependabot.com/)
24 | - [Yarn](https://yarnpkg.com/)
25 | - [ESLint](https://eslint.org/)
26 | - [Prettier](https://prettier.io/)
27 | - [Husky](https://typicode.github.io/husky/#/)
28 |
29 | ## Installation
30 |
31 | You must have `yarn` and `node>=12.16.1` installed first. To install the dependencies run:
32 |
33 | ```shell
34 | yarn install
35 | ```
36 |
37 | To start the application in development mode run:
38 |
39 | ```shell
40 | yarn dev
41 | ```
42 |
43 | To run a static version of the site locally run:
44 |
45 | ```shell
46 | yarn static
47 | ```
48 |
49 | ## Testing
50 |
51 | Run unit tests:
52 |
53 | ```shell
54 | yarn test
55 | ```
56 |
57 | Run e2e tests:
58 |
59 | ```shell
60 | yarn cypress run
61 | ```
62 |
63 | ## Scripts provided
64 |
65 | - `install` install dependencies
66 | - `dev` run in dev mode with hot-module replacement @ http://localhost:3000/
67 | - `build` build next.js application
68 | - `export` export next.js static application to `./out`
69 | - `static` build/export/serve a static next.js application @ http://localhost:3000/
70 | - `validate` run tests/linter/type-checks/format-checks and build the application (the build checks for transpiler
71 | errors)
72 | - `test` run jest tests
73 | - `cypress` run cypress
74 | - `lint` run linter with `--fix`
75 | - `lint:test` run linter
76 | - `format` format code w/ prettier
77 | - `format:test` check for code formatting w/ prettier (won't modify code)
78 | - `storybook` runs storybook @ http://localhost:6006
79 | - `storybook:build` builds storybook output for static deployment (necessary for Chromatic)
80 |
81 | ## Husky Hooks
82 |
83 | - `pre-commit` lints the staged files and formats them automatically
84 | - `pre-push` runs the validate script
85 |
86 | ## Setup
87 |
88 | ### Chromatic & Github
89 |
90 | To enable Chromatic, set the `CHROMATIC_PROJECT_TOKEN` secret:
91 |
92 |
93 | ## Screenshots
94 |
95 | ### Chromatic - Storybook
96 |
97 | 
98 |
99 | ### Chromatic - Library
100 |
101 | 
102 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "juliancoy.com",
3 | "version": "1.0.0",
4 | "description": "Personal Website for Julian Coy",
5 | "repository": "git@github.com:eXamadeus/juliancoy.com.git",
6 | "author": "Julian Coy ",
7 | "license": "MIT",
8 | "private": false,
9 | "engines": {
10 | "node": ">=12.16.1"
11 | },
12 | "scripts": {
13 | "dev": "next dev",
14 | "build": "next build",
15 | "test": "jest",
16 | "cy:run": "cypress run",
17 | "e2e": "start-test 'yarn static' :3000 'yarn cy:run'",
18 | "export": "next export",
19 | "start": "next start",
20 | "static": "yarn build && next export && ts-node --files --project server/tsconfig.json server/index.ts",
21 | "format": "prettier --write --config .prettierrc.yaml .",
22 | "format:test": "prettier --check --config .prettierrc.yaml .",
23 | "lint": "eslint . --fix --cache --ext js,jsx,ts,tsx",
24 | "lint:test": "eslint . --cache --ext js,jsx,ts,tsx",
25 | "storybook": "start-storybook -s ./public -p 6006 --docs",
26 | "storybook:build": "build-storybook -s ./public --docs",
27 | "types:test": "tsc",
28 | "validate": "run-p --print-label types:test lint:test format:test test build"
29 | },
30 | "husky": {
31 | "hooks": {
32 | "pre-commit": "lint-staged",
33 | "pre-push": "yarn validate"
34 | }
35 | },
36 | "lint-staged": {
37 | "*.{ts,tsx,js,jsx}": "eslint --cache",
38 | "*.{ts,tsx,js,jsx,md,yaml,yml,json}": "prettier --write --config .prettierrc.yaml"
39 | },
40 | "dependencies": {
41 | "@emotion/core": "^10.1.1",
42 | "@emotion/react": "^11.4.0",
43 | "next": "^10.2.3",
44 | "react": "^17.0.2",
45 | "react-dom": "^17.0.2"
46 | },
47 | "devDependencies": {
48 | "@babel/core": "^7.14.8",
49 | "@babel/runtime": "^7.14.6",
50 | "@babel/plugin-transform-react-jsx": "^7.14.5",
51 | "@emotion/babel-plugin": "^11.3.0",
52 | "@emotion/babel-preset-css-prop": "^11.2.0",
53 | "@emotion/eslint-plugin": "^11.2.0",
54 | "@storybook/addon-actions": "^6.3.6",
55 | "@storybook/addon-docs": "^6.3.6",
56 | "@storybook/addon-links": "^6.3.4",
57 | "@storybook/addons": "^6.1.21",
58 | "@storybook/react": "^6.3.2",
59 | "@testing-library/dom": "^8.1.0",
60 | "@testing-library/jest-dom": "^5.14.1",
61 | "@testing-library/react": "^11.2.7",
62 | "@types/jest": "^26.0.24",
63 | "@types/next": "^9.0.0",
64 | "@types/node": "^16.3.1",
65 | "@types/styled-jsx": "^2.2.8",
66 | "@typescript-eslint/eslint-plugin": "^4.28.2",
67 | "@typescript-eslint/parser": "^4.28.4",
68 | "babel-loader": "^8.2.2",
69 | "chromatic": "^5.7.1",
70 | "cypress": "^7.7.0",
71 | "eslint": "^7.31.0",
72 | "eslint-config-defaults": "^9.0.0",
73 | "eslint-config-prettier": "^8.3.0",
74 | "eslint-plugin-import": "^2.23.4",
75 | "eslint-plugin-jest": "^24.3.6",
76 | "eslint-plugin-prettier": "^3.4.0",
77 | "eslint-plugin-react": "^7.24.0",
78 | "eslint-plugin-react-hooks": "^4.2.0",
79 | "eslint-plugin-unused-imports": "^1.1.2",
80 | "husky": "^7.0.1",
81 | "jest": "^26.6.3",
82 | "lint-staged": "^11.0.1",
83 | "npm-run-all": "^4.1.5",
84 | "prettier": "^2.3.2",
85 | "react-docgen-typescript-loader": "^3.7.2",
86 | "react-is": "^17.0.2",
87 | "regenerator-runtime": "^0.13.7",
88 | "start-server-and-test": "^1.13.0",
89 | "ts-node": "^10.1.0",
90 | "typescript": "^4.3.5",
91 | "webpack": "^5.45.1"
92 | },
93 | "jest": {
94 | "setupFilesAfterEnv": [
95 | "./jest.setup.ts"
96 | ],
97 | "testPathIgnorePatterns": [
98 | "./cypress"
99 | ]
100 | },
101 | "volta": {
102 | "node": "12.16.1",
103 | "yarn": "1.22.10"
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | ecmaVersion: 2018,
5 | sourceType: 'module',
6 | ecmaFeatures: {
7 | jsx: true,
8 | },
9 | },
10 | plugins: ['@typescript-eslint', 'jest', 'react-hooks', 'import', '@emotion', 'unused-imports'],
11 | extends: ['plugin:@typescript-eslint/recommended', 'plugin:jest/recommended', 'prettier', 'plugin:react/recommended'],
12 | env: {
13 | node: true,
14 | browser: true,
15 | jest: true,
16 | },
17 | settings: {
18 | 'react': {
19 | version: 'detect',
20 | },
21 | 'import/extensions': ['.js', '.jsx', '.ts', '.tsx'],
22 | 'import/parsers': {
23 | '@typescript-eslint/parser': ['.ts', '.tsx'],
24 | },
25 | 'import/resolver': {
26 | node: {
27 | extensions: ['.js', '.jsx', '.ts', '.tsx'],
28 | },
29 | },
30 | },
31 | rules: {
32 | // Opt-in
33 | '@typescript-eslint/no-explicit-any': 'error',
34 | '@typescript-eslint/explicit-member-accessibility': 'error',
35 | 'react-hooks/rules-of-hooks': 'error',
36 | 'react-hooks/exhaustive-deps': 'error',
37 | 'unused-imports/no-unused-imports-ts': 'error',
38 |
39 | // Opt-out
40 | '@typescript-eslint/explicit-function-return-type': 'off',
41 | 'react/prop-types': 'off',
42 | 'react/jsx-wrap-multilines': 'off',
43 | 'react/jsx-indent': 'off',
44 | 'react/display-name': 'off',
45 |
46 | // Remove restriction on `ForOfStatement` as it's just a highly opinionated rule that doesn't have much merit
47 | // see https://github.com/airbnb/javascript/issues/1271 for more info
48 | 'no-restricted-syntax': ['error', 'ForInStatement', 'LabeledStatement', 'WithStatement'],
49 |
50 | 'no-restricted-imports': [
51 | 'error',
52 | {
53 | paths: [
54 | // Emotion 11 is a mess...force the right imports...
55 | { name: '@emotion/react', message: 'Please import from "@emotion/core" instead.' },
56 | { name: '@emotion/css', message: 'Please import from "@emotion/core" instead.' },
57 | ],
58 | },
59 | ],
60 |
61 | // Configure
62 | 'import/no-extraneous-dependencies': [
63 | 'error',
64 | {
65 | devDependencies: false,
66 | optionalDependencies: false,
67 | peerDependencies: false,
68 | },
69 | ],
70 |
71 | 'import/order': [
72 | 'error',
73 | {
74 | 'groups': ['builtin', ['external', 'internal'], ['parent', 'sibling'], ['index']],
75 | 'newlines-between': 'always',
76 | 'alphabetize': {
77 | order: 'asc',
78 | caseInsensitive: true,
79 | },
80 | },
81 | ],
82 |
83 | // line spacing
84 | 'padding-line-between-statements': [
85 | 'error',
86 | // wildcard inclusions
87 | {
88 | blankLine: 'always',
89 | prev: ['multiline-block-like', 'multiline-const', 'multiline-expression'],
90 | next: '*',
91 | },
92 | {
93 | blankLine: 'always',
94 | prev: '*',
95 | next: ['multiline-block-like', 'multiline-const', 'multiline-expression', 'switch', 'return'],
96 | },
97 | // specific exclusions for case statements
98 | { blankLine: 'never', prev: 'case', next: 'multiline-block-like' },
99 | { blankLine: 'never', prev: 'multiline-block-like', next: 'case' },
100 | ],
101 | },
102 | overrides: [
103 | {
104 | files: '**/*.{stories,test}.tsx',
105 | rules: {
106 | 'import/no-extraneous-dependencies': 'off',
107 | },
108 | },
109 | // Cypress
110 | {
111 | files: 'cypress/**',
112 | rules: {
113 | 'jest/expect-expect': 'off',
114 | },
115 | },
116 | ],
117 | }
118 |
--------------------------------------------------------------------------------
/cypress/integration/examples/actions.spec.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | context('Actions', () => {
4 | beforeEach(() => {
5 | cy.visit('https://example.cypress.io/commands/actions')
6 | })
7 |
8 | // https://on.cypress.io/interacting-with-elements
9 |
10 | it('.type() - type into a DOM element', () => {
11 | // https://on.cypress.io/type
12 | cy.get('.action-email')
13 | .type('fake@email.com')
14 | .should('have.value', 'fake@email.com')
15 |
16 | // .type() with special character sequences
17 | .type('{leftarrow}{rightarrow}{uparrow}{downarrow}')
18 | .type('{del}{selectall}{backspace}')
19 |
20 | // .type() with key modifiers
21 | .type('{alt}{option}') //these are equivalent
22 | .type('{ctrl}{control}') //these are equivalent
23 | .type('{meta}{command}{cmd}') //these are equivalent
24 | .type('{shift}')
25 |
26 | // Delay each keypress by 0.1 sec
27 | .type('slow.typing@email.com', { delay: 100 })
28 | .should('have.value', 'slow.typing@email.com')
29 |
30 | cy.get('.action-disabled')
31 | // Ignore error checking prior to type
32 | // like whether the input is visible or disabled
33 | .type('disabled error checking', { force: true })
34 | .should('have.value', 'disabled error checking')
35 | })
36 |
37 | it('.focus() - focus on a DOM element', () => {
38 | // https://on.cypress.io/focus
39 | cy.get('.action-focus').focus().should('have.class', 'focus').prev().should('have.attr', 'style', 'color: orange;')
40 | })
41 |
42 | it('.blur() - blur off a DOM element', () => {
43 | // https://on.cypress.io/blur
44 | cy.get('.action-blur')
45 | .type('About to blur')
46 | .blur()
47 | .should('have.class', 'error')
48 | .prev()
49 | .should('have.attr', 'style', 'color: red;')
50 | })
51 |
52 | it('.clear() - clears an input or textarea element', () => {
53 | // https://on.cypress.io/clear
54 | cy.get('.action-clear')
55 | .type('Clear this text')
56 | .should('have.value', 'Clear this text')
57 | .clear()
58 | .should('have.value', '')
59 | })
60 |
61 | it('.submit() - submit a form', () => {
62 | // https://on.cypress.io/submit
63 | cy.get('.action-form').find('[type="text"]').type('HALFOFF')
64 |
65 | cy.get('.action-form').submit().next().should('contain', 'Your form has been submitted!')
66 | })
67 |
68 | it('.click() - click on a DOM element', () => {
69 | // https://on.cypress.io/click
70 | cy.get('.action-btn').click()
71 |
72 | // You can click on 9 specific positions of an element:
73 | // -----------------------------------
74 | // | topLeft top topRight |
75 | // | |
76 | // | |
77 | // | |
78 | // | left center right |
79 | // | |
80 | // | |
81 | // | |
82 | // | bottomLeft bottom bottomRight |
83 | // -----------------------------------
84 |
85 | // clicking in the center of the element is the default
86 | cy.get('#action-canvas').click()
87 |
88 | cy.get('#action-canvas').click('topLeft')
89 | cy.get('#action-canvas').click('top')
90 | cy.get('#action-canvas').click('topRight')
91 | cy.get('#action-canvas').click('left')
92 | cy.get('#action-canvas').click('right')
93 | cy.get('#action-canvas').click('bottomLeft')
94 | cy.get('#action-canvas').click('bottom')
95 | cy.get('#action-canvas').click('bottomRight')
96 |
97 | // .click() accepts an x and y coordinate
98 | // that controls where the click occurs :)
99 |
100 | cy.get('#action-canvas')
101 | .click(80, 75) // click 80px on x coord and 75px on y coord
102 | .click(170, 75)
103 | .click(80, 165)
104 | .click(100, 185)
105 | .click(125, 190)
106 | .click(150, 185)
107 | .click(170, 165)
108 |
109 | // click multiple elements by passing multiple: true
110 | cy.get('.action-labels>.label').click({ multiple: true })
111 |
112 | // Ignore error checking prior to clicking
113 | cy.get('.action-opacity>.btn').click({ force: true })
114 | })
115 |
116 | it('.dblclick() - double click on a DOM element', () => {
117 | // https://on.cypress.io/dblclick
118 |
119 | // Our app has a listener on 'dblclick' event in our 'scripts.js'
120 | // that hides the div and shows an input on double click
121 | cy.get('.action-div').dblclick().should('not.be.visible')
122 | cy.get('.action-input-hidden').should('be.visible')
123 | })
124 |
125 | it('.rightclick() - right click on a DOM element', () => {
126 | // https://on.cypress.io/rightclick
127 |
128 | // Our app has a listener on 'contextmenu' event in our 'scripts.js'
129 | // that hides the div and shows an input on right click
130 | cy.get('.rightclick-action-div').rightclick().should('not.be.visible')
131 | cy.get('.rightclick-action-input-hidden').should('be.visible')
132 | })
133 |
134 | it('.check() - check a checkbox or radio element', () => {
135 | // https://on.cypress.io/check
136 |
137 | // By default, .check() will check all
138 | // matching checkbox or radio elements in succession, one after another
139 | cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]').check().should('be.checked')
140 |
141 | cy.get('.action-radios [type="radio"]').not('[disabled]').check().should('be.checked')
142 |
143 | // .check() accepts a value argument
144 | cy.get('.action-radios [type="radio"]').check('radio1').should('be.checked')
145 |
146 | // .check() accepts an array of values
147 | cy.get('.action-multiple-checkboxes [type="checkbox"]').check(['checkbox1', 'checkbox2']).should('be.checked')
148 |
149 | // Ignore error checking prior to checking
150 | cy.get('.action-checkboxes [disabled]').check({ force: true }).should('be.checked')
151 |
152 | cy.get('.action-radios [type="radio"]').check('radio3', { force: true }).should('be.checked')
153 | })
154 |
155 | it('.uncheck() - uncheck a checkbox element', () => {
156 | // https://on.cypress.io/uncheck
157 |
158 | // By default, .uncheck() will uncheck all matching
159 | // checkbox elements in succession, one after another
160 | cy.get('.action-check [type="checkbox"]').not('[disabled]').uncheck().should('not.be.checked')
161 |
162 | // .uncheck() accepts a value argument
163 | cy.get('.action-check [type="checkbox"]').check('checkbox1').uncheck('checkbox1').should('not.be.checked')
164 |
165 | // .uncheck() accepts an array of values
166 | cy.get('.action-check [type="checkbox"]')
167 | .check(['checkbox1', 'checkbox3'])
168 | .uncheck(['checkbox1', 'checkbox3'])
169 | .should('not.be.checked')
170 |
171 | // Ignore error checking prior to unchecking
172 | cy.get('.action-check [disabled]').uncheck({ force: true }).should('not.be.checked')
173 | })
174 |
175 | it('.select() - select an option in a