diff --git a/.env.example b/.env.example index 3a4e274..fec4611 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,3 @@ NEXT_PUBLIC_RPC_URL= # Example: https://localhost:8545 -NEXT_PUBLIC_PROJECT_ID= # API from WalletConnect +NEXT_PUBLIC_PROJECT_ID= # ProjectID from WalletConnect NEXT_PUBLIC_ALCHEMY_KEY= # API key from Alchemy \ No newline at end of file diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index ab53c89..b288a84 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,4 +1,4 @@ -name: Build and Test +name: Build and Format on: pull_request: @@ -10,32 +10,24 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + with: + version: 8 - name: Install Node uses: actions/setup-node@v3 with: - node-version: "21.4" - registry-url: "https://registry.npmjs.org" - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" - - - uses: actions/cache@v3 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- + node-version: '21.4' + registry-url: 'https://registry.npmjs.org' + cache: 'pnpm' - name: Install project dependencies - run: yarn --prefer-offline - id: install + run: pnpm install --frozen-lockfile --prefer-frozen-lockfile - name: Build project dependencies - run: yarn run build - id: build + run: pnpm run build + env: + NEXT_PUBLIC_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_PROJECT_ID }} check: name: Check for crypto @@ -58,32 +50,29 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "21.4" - registry-url: "https://registry.npmjs.org" - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: pnpm/action-setup@v2 + with: + version: 8.15 - - uses: actions/cache@v3 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) + - uses: actions/setup-node@v4 with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- + node-version: '21.4' + cache: 'pnpm' - - name: Install project dependencies - run: yarn --prefer-offline - id: install + - name: Install Dependencies + run: pnpm install + - name: Build the project + run: pnpm run build + - name: Run Prettier - run: yarn prettier + run: pnpm prettier - name: Run Linter - run: yarn lint + run: pnpm lint - - name: Run Tests - run: yarn test + - name: Cypress run + uses: cypress-io/github-action@v5 + with: + start: pnpm start, pnpm test diff --git a/cypress.config.ts b/cypress.config.ts index 17161e3..5bed49b 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,9 +1,7 @@ -import { defineConfig } from "cypress"; +import { defineConfig } from 'cypress'; export default defineConfig({ e2e: { - setupNodeEvents(on, config) { - // implement node event listeners here - }, + baseUrl: 'http://localhost:3000', }, }); diff --git a/cypress/cypress.d.ts b/cypress/cypress.d.ts new file mode 100644 index 0000000..256fee8 --- /dev/null +++ b/cypress/cypress.d.ts @@ -0,0 +1,5 @@ +declare namespace Cypress { + interface Chainable { + getByTestId(testId: string): Chainable>; + } +} diff --git a/cypress/e2e/fundamentals.cy.ts b/cypress/e2e/fundamentals.cy.ts deleted file mode 100644 index 1360e51..0000000 --- a/cypress/e2e/fundamentals.cy.ts +++ /dev/null @@ -1,5 +0,0 @@ -describe('template spec', () => { - it('passes', () => { - cy.visit('https://example.cypress.io'); - }); -}); diff --git a/cypress/e2e/spec.cy.ts b/cypress/e2e/spec.cy.ts new file mode 100644 index 0000000..e9e7f2a --- /dev/null +++ b/cypress/e2e/spec.cy.ts @@ -0,0 +1,6 @@ +describe('Renders every component', () => { + it('Renders App component', () => { + cy.visit('/'); + cy.getByTestId('boilerplate-title').should('exist'); + }); +}); diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index 02e4254..0000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 698b01a..5c74ee9 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -1,37 +1,4 @@ /// -// *********************************************** -// This example commands.ts shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add('login', (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) -// -// declare global { -// namespace Cypress { -// interface Chainable { -// login(email: string, password: string): Chainable -// drag(subject: string, options?: Partial): Chainable -// dismiss(subject: string, options?: Partial): Chainable -// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable -// } -// } -// } \ No newline at end of file +Cypress.Commands.add('getByTestId', (testId) => { + return cy.get(`[data-testid="${testId}"]`); +}); diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index f80f74f..1221b17 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -1,20 +1 @@ -// *********************************************************** -// This example support/e2e.ts is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') \ No newline at end of file +import './commands'; diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 0000000..5ed0d7b --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "types": ["cypress"], + }, + "include": ["**/*.ts"] + } + \ No newline at end of file diff --git a/package.json b/package.json index e529be9..ed70754 100644 --- a/package.json +++ b/package.json @@ -11,17 +11,18 @@ "scripts": { "dev": "next dev", "build": "next build", - "start": "next start", + "start": "next start -p 3000", "preview": "next start", "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", - "lint:fix": "yarn run lint -- --fix", + "lint:fix": "pnpm run lint -- --fix", "prettier": "prettier src --check", - "prettier:fix": "yarn run prettier -- --write", - "format": "yarn run prettier:fix && yarn run lint:fix", - "format:check": "yarn run prettier && yarn run lint", + "prettier:fix": "pnpm run prettier -- --write", + "format": "pnpm run prettier:fix && yarn run lint:fix", + "format:check": "pnpm run prettier && yarn run lint", "prepare": "husky install && wonderland-crypto-husky-checks install", - "test": "yarn cypress run", - "test:watch": "yarn cypress open" + "cypress:open": "cypress open", + "cypress:run": "cypress run", + "test": "pnpm cypress:run" }, "lint-staged": { "src/**/*.{js,jsx,ts,tsx}": "eslint --cache --fix", @@ -58,7 +59,6 @@ "cypress": "13.7.0", "eslint": "8.45.0", "eslint-config-prettier": "9.0.0", - "eslint-formatter-codeframe": "7.32.1", "eslint-plugin-import": "2.28.1", "eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-prettier": "5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3dba5b..69885b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,9 +91,6 @@ devDependencies: eslint-config-prettier: specifier: 9.0.0 version: 9.0.0(eslint@8.45.0) - eslint-formatter-codeframe: - specifier: 7.32.1 - version: 7.32.1 eslint-plugin-import: specifier: 2.28.1 version: 2.28.1(@typescript-eslint/parser@6.0.0)(eslint@8.45.0) @@ -173,12 +170,6 @@ packages: chokidar: 3.6.0 dev: true - /@babel/code-frame@7.12.11: - resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} - dependencies: - '@babel/highlight': 7.24.1 - dev: true - /@babel/code-frame@7.24.1: resolution: {integrity: sha512-bC49z4spJQR3j8vFtJBLqzyzFV0ciuL5HYX7qfSl3KEqeMVV+eTquRvmXxpvB0AMubRrvv7y5DILiLLPi57Ewg==} engines: {node: '>=6.9.0'} @@ -5824,14 +5815,6 @@ packages: eslint: 8.45.0 dev: true - /eslint-formatter-codeframe@7.32.1: - resolution: {integrity: sha512-DK/3Q3+zVKq/7PdSYiCxPrsDF8H/TRMK5n8Hziwr4IMkMy+XiKSwbpj25AdajS63I/B61Snetq4uVvX9fOLyAg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - '@babel/code-frame': 7.12.11 - chalk: 4.1.2 - dev: true - /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: diff --git a/src/config/env.ts b/src/config/env.ts index 4ba9aa4..4d3ba54 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -1,11 +1,13 @@ import { Env } from '~/types'; export const getEnv = (): Env => { - const { NEXT_PUBLIC_RPC_URL, NEXT_PUBLIC_PROJECT_ID, NEXT_PUBLIC_ALCHEMY_KEY } = process.env; + const NEXT_PUBLIC_RPC_URL = process.env.NEXT_PUBLIC_RPC_URL; + const NEXT_PUBLIC_PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID; + const NEXT_PUBLIC_ALCHEMY_KEY = process.env.NEXT_PUBLIC_ALCHEMY_KEY; return { - RPC_URL: NEXT_PUBLIC_RPC_URL || '', - PROJECT_ID: NEXT_PUBLIC_PROJECT_ID || '', - ALCHEMY_KEY: NEXT_PUBLIC_ALCHEMY_KEY || '', + RPC_URL: NEXT_PUBLIC_RPC_URL as string, + PROJECT_ID: NEXT_PUBLIC_PROJECT_ID as string, + ALCHEMY_KEY: NEXT_PUBLIC_ALCHEMY_KEY as string, }; }; diff --git a/src/containers/Footer/index.tsx b/src/containers/Footer/index.tsx index 618e88c..6fc8f48 100644 --- a/src/containers/Footer/index.tsx +++ b/src/containers/Footer/index.tsx @@ -1,6 +1,8 @@ import { styled } from '@mui/material/styles'; import { useCustomTheme } from '~/hooks/useTheme'; +import { FOOTER_HEIGHT } from '~/utils'; + export const Footer = () => { return ( @@ -17,13 +19,13 @@ const FooterContainer = styled('footer')(() => { const { currentTheme } = useCustomTheme(); return { display: 'flex', - height: '8rem', + height: `${FOOTER_HEIGHT}rem`, padding: '0 8rem', alignItems: 'center', justifyContent: 'space-between', backgroundColor: currentTheme.backgroundSecondary, borderTop: currentTheme.border, - width: '100vw', + width: '100%', }; }); diff --git a/src/containers/Header/index.tsx b/src/containers/Header/index.tsx index 15a55de..2caa8a9 100644 --- a/src/containers/Header/index.tsx +++ b/src/containers/Header/index.tsx @@ -5,6 +5,7 @@ import LightModeIcon from '@mui/icons-material/LightMode'; import DarkModeIcon from '@mui/icons-material/DarkMode'; import { useCustomTheme } from '~/hooks/useTheme'; +import { zIndex, HEADER_HEIGHT } from '~/utils'; export const Header = () => { const { changeTheme, theme } = useCustomTheme(); @@ -12,7 +13,7 @@ export const Header = () => { return ( Logo - {theme === 'dark' ? : } + {theme === 'dark' ? : } ); @@ -23,13 +24,13 @@ const StyledHeader = styled('header')(() => { const { currentTheme } = useCustomTheme(); return { display: 'flex', - height: '8rem', + height: `${HEADER_HEIGHT}rem`, padding: '0 8rem', alignItems: 'center', justifyContent: 'space-between', backgroundColor: currentTheme.backgroundSecondary, - width: '100vw', - zIndex: 100, + width: '100%', + zIndex: zIndex.HEADER, }; }); @@ -38,3 +39,8 @@ const Logo = styled('h1')({ fontWeight: 'bold', cursor: 'pointer', }); + +const SIconButton = styled(IconButton)({ + position: 'absolute', + left: '50%', +}); diff --git a/src/containers/Landing/index.tsx b/src/containers/Landing/index.tsx index a14cd3f..6b217cf 100644 --- a/src/containers/Landing/index.tsx +++ b/src/containers/Landing/index.tsx @@ -1,9 +1,11 @@ import { styled } from '@mui/material/styles'; +import { MAIN_HEIGHT } from '~/utils'; + export const Landing = () => { return ( -

Web3 React Boilerplate

+

Web3 React Boilerplate

); }; @@ -11,7 +13,7 @@ export const Landing = () => { const LandingContainer = styled('div')({ display: 'flex', flexDirection: 'column', - height: 'calc(100vh - 16rem)', + height: `calc(100vh - ${MAIN_HEIGHT}rem)`, padding: '0 8rem', alignItems: 'center', justifyContent: 'center', diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index 70a1a96..e3e15c0 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -32,15 +32,8 @@ const MainContent = styled(Box)` width: 100%; max-width: 100%; overflow-x: hidden; - - max-width: 120rem; - padding: 0 4rem; - min-height: 100vh; + min-height: 100%; margin: 0 auto; - - @media (max-width: 600px) { - padding: 0 1.6rem; - } `; const NoScriptMessage = styled('noscript')(() => { diff --git a/src/providers/Web3ModalProvider.tsx b/src/providers/WalletProvider.tsx similarity index 92% rename from src/providers/Web3ModalProvider.tsx rename to src/providers/WalletProvider.tsx index 8e0c9eb..8bd8753 100644 --- a/src/providers/Web3ModalProvider.tsx +++ b/src/providers/WalletProvider.tsx @@ -13,7 +13,7 @@ type Props = { const queryClient = new QueryClient(); -export function Web3ModalProvider({ children }: Props) { +export function WalletProvider({ children }: Props) { return ( diff --git a/src/providers/index.tsx b/src/providers/index.tsx index 9c6ed54..3047956 100644 --- a/src/providers/index.tsx +++ b/src/providers/index.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react'; import { StateProvider } from './StateProvider'; import { ThemeProvider } from './ThemeProvider'; -import { Web3ModalProvider } from './Web3ModalProvider'; +import { WalletProvider } from './WalletProvider'; type Props = { children: ReactNode; @@ -12,7 +12,7 @@ export const Providers = ({ children }: Props) => { return ( - {children} + {children} ); diff --git a/src/utils/Variables.ts b/src/utils/Variables.ts index 32d9458..f8caa97 100644 --- a/src/utils/Variables.ts +++ b/src/utils/Variables.ts @@ -20,3 +20,14 @@ export const fontSize = { MEDIUM: '1.4rem', SMALL: '1.2rem', }; + +export const zIndex = { + HEADER: 100, + MODAL: 200, + BACKDROP: -1, + TOAST: 500, +}; + +export const HEADER_HEIGHT = 8; // Header height in rem units +export const FOOTER_HEIGHT = 8; // Footer height in rem units +export const MAIN_HEIGHT = HEADER_HEIGHT + FOOTER_HEIGHT; diff --git a/tsconfig.json b/tsconfig.json index bcd800d..069e30f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "module": "ESNext", "esModuleInterop": true, "skipLibCheck": true, - "moduleResolution": "bundler", + "moduleResolution": "node", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, @@ -21,10 +21,11 @@ "incremental": true, "plugins": [{ "name": "next" }], "baseUrl": ".", + "types": ["cypress"], "paths": { "~/*": ["src/*"] } }, - "include": ["./src", "./dist/types/**/*.ts", "./next-env.d.ts"], + "include": ["./src", "./dist/types/**/*.ts", "./next-env.d.ts", "./cypress/**/*.ts"], "exclude": ["./node_modules"] } \ No newline at end of file