├── .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<ExampleProps> = ({ content, styles }) => ( 19 | <h1 css={[baseStyle, styles]} aria-label={content}> 20 | {content} 21 | </h1> 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 | <Html lang={'en'}> 10 | <Head> 11 | <GlobalStyles /> 12 | <meta name='Description' content={'Next.js template'} /> 13 | <link rel='icon' href='/favicon.ico' /> 14 | </Head> 15 | <body> 16 | <Main /> 17 | <NextScript /> 18 | </body> 19 | </Html> 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 | <Meta title='Components/Title' component={Title} /> 7 | 8 | # Title 9 | 10 | export const Template = (args) => <Title {...args} /> 11 | 12 | <Preview> 13 | <Story name='Example'> 14 | <Title content={'Example Title'} /> 15 | </Story> 16 | </Preview> 17 | 18 | ## Controlled Example 19 | 20 | <Story 21 | name='Configurable' 22 | args={{ 23 | content: 'You can edit my props!', 24 | styles: { 'color': 'blue', 'font-style': 'italic', 'font-size': '2rem' }, 25 | }}> 26 | {Template.bind({})} 27 | </Story> 28 | 29 | <ArgsTable story={'Configurable'} /> 30 | -------------------------------------------------------------------------------- /public/zeit.svg: -------------------------------------------------------------------------------- 1 | <svg width="82" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <path fill="url(#prefix__paint0_linear)" d="M9.018 0l9.019 16H0L9.018 0z"/> 3 | <path fill="#333" fill-rule="evenodd" d="M51.634 12.028h-6.492V2.052h6.492v1.256H46.61v3.007h4.37V7.57h-4.37v3.202h5.024v1.255zm-14.063 0h-7.235v-1.096l5.342-7.624h-5.253V2.052h7.058v1.097l-5.342 7.623h5.43v1.256zm21.88 0h6.333v-1.256h-2.423V3.308h2.423V2.052h-6.332v1.256h2.441v7.465h-2.441v1.255zm18.22 0h-1.468v-8.72h-3.36V2.052h8.225v1.256H77.67v8.72z" clip-rule="evenodd"/> 4 | <defs> 5 | <linearGradient id="prefix__paint0_linear" x1="28.022" x2="16.189" y1="22.991" y2="8.569" gradientUnits="userSpaceOnUse"> 6 | <stop stop-color="#fff"/> 7 | <stop offset="1"/> 8 | </linearGradient> 9 | </defs> 10 | </svg> 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 | /// <reference types="cypress" /> 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 | <div 12 | css={css` 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | width: 100%; 17 | height: 100vh; 18 | `}> 19 | <div 20 | css={css` 21 | display: flex; 22 | flex-flow: row wrap; 23 | justify-content: flex-end; 24 | width: 23rem; 25 | `}> 26 | <Title styles={[flexFill]} content={'Next.js Template'} /> 27 | <h4 css={flexFill}> 28 | Designed by{' '} 29 | <a href='https://github.com/examadeus' target='_blank' rel='noreferrer'> 30 | @eXamadeus 31 | </a> 32 | </h4> 33 | </div> 34 | </div> 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 | /// <reference types="cypress" /> 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 | <Global 7 | styles={css` 8 | /** 9 | * @license 10 | * MyFonts Webfont Build ID 3897521, 2020-04-30T23:34:06-0400 11 | * 12 | * The fonts listed in this notice are subject to the End User License 13 | * Agreement(s) entered into by the website owner. All other parties are 14 | * explicitly restricted from using the Licensed Webfonts(s). 15 | * 16 | * You may obtain a valid license at the URLs below. 17 | * 18 | * Webfont: Gilroy-Light by Radomir Tinkov 19 | * URL: https://www.myfonts.com/fonts/radomir-tinkov/gilroy/light/ 20 | * 21 | * Webfont: Gilroy-ExtraBold by Radomir Tinkov 22 | * URL: https://www.myfonts.com/fonts/radomir-tinkov/gilroy/extra-bold/ 23 | * 24 | * 25 | * Webfonts copyright: Copyright &#x00A9; 2016 by Radomir Tinkov. All rights reserved. 26 | * 27 | * © 2020 MyFonts Inc 28 | */ 29 | 30 | @font-face { 31 | font-family: 'Gilroy-Light'; 32 | src: url('/fonts/Gilroy-Light/font.woff2') format('woff2'), 33 | url('/fonts/Gilroy-Light/font.woff') format('woff'); 34 | font-display: swap; 35 | } 36 | @font-face { 37 | font-family: 'Gilroy-ExtraBold'; 38 | src: url('/fonts/Gilroy-ExtraBold/font.woff2') format('woff2'), 39 | url('/fonts/Gilroy-ExtraBold/font.woff') format('woff'); 40 | font-display: swap; 41 | } 42 | 43 | html * { 44 | font-family: Gilroy-Light, Arial, sans-serif; 45 | } 46 | 47 | h1, 48 | h2, 49 | h3, 50 | h4, 51 | h5 { 52 | margin: 0; 53 | } 54 | `} 55 | /> 56 | <link rel='preload' href='/fonts/Gilroy-Light/font.woff2' as='font' type='font/woff2' crossOrigin={''} /> 57 | <link rel='preload' href='/fonts/Gilroy-ExtraBold/font.woff2' as='font' type='font/woff2' crossOrigin={''} /> 58 | </> 59 | ) 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Storybook](https://img.shields.io/badge/Chromatic-Storybook-green)](https://master--5ff5b5e3816aa60021b27e34.chromatic.com/) 2 | [![Chromatic Library](https://img.shields.io/badge/Chromatic-Library-green)](https://www.chromatic.com/library?appId=5ff5b5e3816aa60021b27e34&branch=master) 3 | [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=eXamadeus/next-emotion)](https://dependabot.com) 4 | 5 | # Next.js Template 6 | 7 | ![Screen Recording 2021-01-11 at 10 30 27 AM](https://user-images.githubusercontent.com/37161095/104201772-1852b380-53f8-11eb-9a78-16799e6d44eb.gif) 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 | <img width="1061" alt="Image 2020-05-09 at 9 14 37 PM" src="https://user-images.githubusercontent.com/37161095/81488693-24b7b880-923a-11ea-9be8-c9dedc0b4380.png"> 92 | 93 | ## Screenshots 94 | 95 | ### Chromatic - Storybook 96 | 97 | ![Storybook Example](https://user-images.githubusercontent.com/37161095/103788866-55403400-500d-11eb-9cdd-79df7cde6683.png) 98 | 99 | ### Chromatic - Library 100 | 101 | ![Chromatic Library Example](https://user-images.githubusercontent.com/37161095/103789042-8caee080-500d-11eb-91fc-3283c6a5b07b.png) 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 <julian@coy.cloud>", 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 | /// <reference types="cypress" /> 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 <select> element', () => { 176 | // https://on.cypress.io/select 177 | 178 | // at first, no option should be selected 179 | cy.get('.action-select').should('have.value', '--Select a fruit--') 180 | 181 | // Select option(s) with matching text content 182 | cy.get('.action-select').select('apples') 183 | // confirm the apples were selected 184 | // note that each value starts with "fr-" in our HTML 185 | cy.get('.action-select').should('have.value', 'fr-apples') 186 | 187 | cy.get('.action-select-multiple') 188 | .select(['apples', 'oranges', 'bananas']) 189 | // when getting multiple values, invoke "val" method first 190 | .invoke('val') 191 | .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas']) 192 | 193 | // Select option(s) with matching value 194 | cy.get('.action-select') 195 | .select('fr-bananas') 196 | // can attach an assertion right away to the element 197 | .should('have.value', 'fr-bananas') 198 | 199 | cy.get('.action-select-multiple') 200 | .select(['fr-apples', 'fr-oranges', 'fr-bananas']) 201 | .invoke('val') 202 | .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas']) 203 | 204 | // assert the selected values include oranges 205 | cy.get('.action-select-multiple').invoke('val').should('include', 'fr-oranges') 206 | }) 207 | 208 | it('.scrollIntoView() - scroll an element into view', () => { 209 | // https://on.cypress.io/scrollintoview 210 | 211 | // normally all of these buttons are hidden, 212 | // because they're not within 213 | // the viewable area of their parent 214 | // (we need to scroll to see them) 215 | cy.get('#scroll-horizontal button').should('not.be.visible') 216 | 217 | // scroll the button into view, as if the user had scrolled 218 | cy.get('#scroll-horizontal button').scrollIntoView().should('be.visible') 219 | 220 | cy.get('#scroll-vertical button').should('not.be.visible') 221 | 222 | // Cypress handles the scroll direction needed 223 | cy.get('#scroll-vertical button').scrollIntoView().should('be.visible') 224 | 225 | cy.get('#scroll-both button').should('not.be.visible') 226 | 227 | // Cypress knows to scroll to the right and down 228 | cy.get('#scroll-both button').scrollIntoView().should('be.visible') 229 | }) 230 | 231 | it('.trigger() - trigger an event on a DOM element', () => { 232 | // https://on.cypress.io/trigger 233 | 234 | // To interact with a range input (slider) 235 | // we need to set its value & trigger the 236 | // event to signal it changed 237 | 238 | // Here, we invoke jQuery's val() method to set 239 | // the value and trigger the 'change' event 240 | cy.get('.trigger-input-range') 241 | .invoke('val', 25) 242 | .trigger('change') 243 | .get('input[type=range]') 244 | .siblings('p') 245 | .should('have.text', '25') 246 | }) 247 | 248 | it('cy.scrollTo() - scroll the window or element to a position', () => { 249 | // https://on.cypress.io/scrollto 250 | 251 | // You can scroll to 9 specific positions of an element: 252 | // ----------------------------------- 253 | // | topLeft top topRight | 254 | // | | 255 | // | | 256 | // | | 257 | // | left center right | 258 | // | | 259 | // | | 260 | // | | 261 | // | bottomLeft bottom bottomRight | 262 | // ----------------------------------- 263 | 264 | // if you chain .scrollTo() off of cy, we will 265 | // scroll the entire window 266 | cy.scrollTo('bottom') 267 | 268 | cy.get('#scrollable-horizontal').scrollTo('right') 269 | 270 | // or you can scroll to a specific coordinate: 271 | // (x axis, y axis) in pixels 272 | cy.get('#scrollable-vertical').scrollTo(250, 250) 273 | 274 | // or you can scroll to a specific percentage 275 | // of the (width, height) of the element 276 | cy.get('#scrollable-both').scrollTo('75%', '25%') 277 | 278 | // control the easing of the scroll (default is 'swing') 279 | cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' }) 280 | 281 | // control the duration of the scroll (in ms) 282 | cy.get('#scrollable-both').scrollTo('center', { duration: 2000 }) 283 | }) 284 | }) 285 | --------------------------------------------------------------------------------