From 785563a71c374a53f13ea2ca6d1f006d20fb76b3 Mon Sep 17 00:00:00 2001 From: Rui Saraiva Date: Sat, 4 Dec 2021 18:32:53 +0000 Subject: [PATCH] feat: upgrade dependencies (#480) * feat: upgrade dependencies * chore: update linter and CI workflows * chore: upgrade browserslist --- .babelrc | 14 - .eslintrc.json | 5 +- .github/workflows/lint.yml | 9 +- .github/workflows/release.yml | 7 +- .github/workflows/tests.yml | 9 +- .husky/commit-msg | 4 + .husky/pre-commit | 4 + .husky/pre-push | 4 + .huskyrc.json | 7 - .nvmrc | 2 +- .prettierignore | 3 + .storybook/addons.js | 6 - .storybook/config.jsx | 72 - .storybook/main.js | 36 +- .storybook/mock-router.js | 22 - .storybook/preview.js | 114 + .storybook/styles.css | 4 +- .storybook/theme.js | 37 + .storybook/webpack.config.js | 44 - .stylelintrc.json | 6 +- components/button/index.module.scss | 14 +- components/button/index.stories.tsx | 32 +- components/button/index.tsx | 13 +- components/checkbox/index.module.scss | 3 +- components/checkbox/index.stories.tsx | 36 +- components/checkbox/index.tsx | 11 +- components/dark-mode-toggle/index.module.scss | 15 +- components/dark-mode-toggle/index.stories.tsx | 20 +- components/dark-mode-toggle/index.tsx | 8 +- components/drag-and-drop/index.module.scss | 39 +- components/drag-and-drop/index.stories.tsx | 36 +- components/drag-and-drop/index.tsx | 7 +- components/favycon-error/index.module.scss | 1 + components/favycon-info/index.module.scss | 24 +- components/favycon-wizard/index.module.scss | 4 +- components/header/index.module.scss | 6 +- components/index.stories.mdx | 9 + components/index.stories.tsx | 27 - components/lazy-image/index.tsx | 1 + components/media-query-provider/index.tsx | 2 +- components/svgs/svg-checkbox.module.scss | 2 +- components/svgs/svg-drop-zone.module.scss | 2 +- components/svgs/svg-favycon.module.scss | 12 +- components/svgs/svg-image-upload.module.scss | 2 +- components/svgs/svg-info.module.scss | 4 +- components/tool-title/index.module.scss | 4 +- components/tool-title/index.stories.tsx | 26 +- components/tool-title/index.tsx | 2 +- components/typography/index.module.scss | 8 +- components/typography/index.stories.tsx | 59 +- components/typography/index.tsx | 2 +- cypress/integration/drag-and-drop.spec.js | 3 + hooks/use-dark-mode.ts | 86 + next-env.d.ts | 5 +- package.json | 149 +- pages/_document.tsx | 7 +- public/js/noflash.js | 12 +- public/logo-horizontal.svg | 9 + styles/font.scss | 20 +- styles/main.scss | 27 +- styles/nprogress.scss | 1 + styles/reset.scss | 6 + styles/variables.scss | 36 +- tsconfig.json | 3 +- utils/device.ts | 1 + utils/files.ts | 1 + yarn.lock | 13875 ++++++++-------- 67 files changed, 7648 insertions(+), 7433 deletions(-) delete mode 100644 .babelrc create mode 100755 .husky/commit-msg create mode 100755 .husky/pre-commit create mode 100755 .husky/pre-push delete mode 100644 .huskyrc.json create mode 100644 .prettierignore delete mode 100644 .storybook/addons.js delete mode 100644 .storybook/config.jsx delete mode 100644 .storybook/mock-router.js create mode 100644 .storybook/preview.js create mode 100644 .storybook/theme.js delete mode 100644 .storybook/webpack.config.js create mode 100644 components/index.stories.mdx delete mode 100644 components/index.stories.tsx create mode 100644 hooks/use-dark-mode.ts create mode 100644 public/logo-horizontal.svg diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 35069a8..0000000 --- a/.babelrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "presets": ["next/babel"], - "plugins": [ - [ - "prismjs", - { - "languages": ["markup"], - "plugins": [], - "theme": "default", - "css": false - } - ] - ] -} diff --git a/.eslintrc.json b/.eslintrc.json index 0cb22be..10743d9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,10 +1,9 @@ { "parser": "@typescript-eslint/parser", "extends": [ - "react-app", + "next", "plugin:prettier/recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended", + "plugin:storybook/recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking" ], diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2b59849..7221182 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -9,13 +9,16 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: Use Node.js 14 + - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: "14" + node-version-file: '.nvmrc' + cache: 'yarn' - name: Install dependencies - uses: bahmutov/npm-install@v1 + run: yarn install --frozen-lockfile - name: Lint Scripts run: yarn lint:scripts - name: Lint CSS run: yarn lint:styles + - name: Lint JSON + run: yarn lint:json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 40ca75c..b835508 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,13 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: Use Node.js 14 + - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: '14' + node-version-file: '.nvmrc' + cache: 'yarn' - name: Install dependencies - uses: bahmutov/npm-install@v1 + run: yarn install --frozen-lockfile - name: Cache Next.js build cache uses: actions/cache@v2 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 859e087..cb02a4c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,17 +5,18 @@ on: pull_request jobs: test_e2e: name: Run Cypress - runs-on: ubuntu-16.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js 14 + - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: "14" + node-version-file: '.nvmrc' + cache: 'yarn' - name: Install dependencies - uses: bahmutov/npm-install@v1 + run: yarn install --frozen-lockfile - name: Cache Next.js build cache uses: actions/cache@v2 diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..3b4299e --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx --no -- commitlint --edit "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..c37466e --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged \ No newline at end of file diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 0000000..dba9310 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npm run lint \ No newline at end of file diff --git a/.huskyrc.json b/.huskyrc.json deleted file mode 100644 index 932578e..0000000 --- a/.huskyrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "hooks": { - "pre-commit": "lint-staged", - "pre-push": "npm run lint", - "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" - } -} diff --git a/.nvmrc b/.nvmrc index 2a0dc9a..6a9d9e2 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14.16.0 +14.18 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..8d31df2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +node_modules +.next +storybook-static \ No newline at end of file diff --git a/.storybook/addons.js b/.storybook/addons.js deleted file mode 100644 index 661c576..0000000 --- a/.storybook/addons.js +++ /dev/null @@ -1,6 +0,0 @@ -import '@storybook/addon-actions/register' -import '@storybook/addon-links/register' -import '@storybook/addon-knobs/register' -import '@storybook/addon-viewport/register' -import '@storybook/addon-storysource/register' -import '@storybook/addon-backgrounds/register' diff --git a/.storybook/config.jsx b/.storybook/config.jsx deleted file mode 100644 index 77c243b..0000000 --- a/.storybook/config.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import { addDecorator, addParameters, configure } from '@storybook/react' -import { withConsole } from '@storybook/addon-console' -import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport' -import { RouterContext } from 'next/dist/next-server/lib/router-context' -import { myRouter } from './mock-router' -import 'styles/main.scss' -import './styles.css' - -addDecorator((storyFn, context) => withConsole()(storyFn)(context)) - -addParameters({ - options: { - storySort: (a, b) => { - if (a[1].kind === 'Welcome') { - return -1 - } - - return a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, { numeric: true }) - }, - }, -}) - -const customViewports = { - responsive: { - name: 'Responsive', - styles: { - height: '100%', - width: '100%', - }, - type: 'desktop', - }, - desktop1280: { - name: 'Desktop 1280px', - styles: { - height: '800px', - width: '1280px', - }, - type: 'desktop', - }, - desktop1440: { - name: 'Desktop 1440px', - styles: { - height: '900px', - width: '1440px', - }, - type: 'desktop', - }, -} - -addParameters({ - viewport: { - viewports: { - ...INITIAL_VIEWPORTS, - ...customViewports, - }, - defaultViewport: 'responsive', - }, - options: { - showPanel: true, - }, -}) - -const Theme = (StoryFn) => ( - - - -) - -addDecorator(Theme) - -// automatically import all files ending in *.stories.tsx -configure(require.context('../components', true, /\.stories\.tsx$/), module) diff --git a/.storybook/main.js b/.storybook/main.js index 4d89fb4..2c69a3a 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,8 +1,33 @@ const path = require('path') -// Export a function. Accept the base config as the only param. +/** + * @type {import('@storybook/react/types').StorybookConfig} + */ module.exports = { - webpackFinal: async (config) => { + core: { + builder: 'webpack5', + }, + reactOptions: { + fastRefresh: true, + }, + staticDirs: ['../public'], + stories: ['../components/**/*.stories.mdx', '../components/**/*.stories.@(js|jsx|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + 'storybook-addon-next-router', + { + name: '@storybook/addon-postcss', + options: { + postcssLoaderOptions: { + implementation: require('postcss'), + }, + }, + }, + '@storybook/addon-a11y', + 'storybook-dark-mode', + ], + webpackFinal: async (config, { configType }) => { config.module.rules.push({ test: /\.scss$/, use: [ @@ -19,7 +44,12 @@ module.exports = { include: path.resolve(__dirname, '../'), }) - // merge whatever from nextConfig into the webpack config storybook will use + config.resolve.alias['components'] = path.join(__dirname, '../components') + config.resolve.alias['pages'] = path.join(__dirname, '../pages') + config.resolve.alias['styles'] = path.join(__dirname, '../styles') + config.resolve.alias['utils'] = path.join(__dirname, '../utils') + config.resolve.alias['hooks'] = path.join(__dirname, '../hooks') + return config }, } diff --git a/.storybook/mock-router.js b/.storybook/mock-router.js deleted file mode 100644 index ed8f1e1..0000000 --- a/.storybook/mock-router.js +++ /dev/null @@ -1,22 +0,0 @@ -import Router from 'next/router' - -const commonRouterData = { - push: () => new Promise((resolve) => resolve(true)), - replace: () => new Promise((resolve) => resolve(true)), - prefetch: () => new Promise((resolve) => resolve()), - reload: () => {}, - back: () => {}, -} - -Router.router = { - ...commonRouterData, -} - -export const myRouter = { - ...commonRouterData, - pathname: '/', - route: '/', - query: {}, - asPath: '/', - isFallback: false, -} diff --git a/.storybook/preview.js b/.storybook/preview.js new file mode 100644 index 0000000..4d17685 --- /dev/null +++ b/.storybook/preview.js @@ -0,0 +1,114 @@ +import { useEffect, useState } from 'react' +import { RouterContext } from 'next/dist/shared/lib/router-context' +import * as NextImage from 'next/image' +import addons from '@storybook/addons' +import { withConsole } from '@storybook/addon-console' +import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport' +import { addDecorator } from '@storybook/react' +import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode' +import { useDarkMode } from '../hooks/use-dark-mode' +import * as themes from './theme' +import 'styles/main.scss' +import './styles.css' + +const OriginalNextImage = NextImage.default + +Object.defineProperty(NextImage, 'default', { + configurable: true, + value: (props) => , +}) + +addDecorator((storyFn, context) => withConsole()(storyFn)(context)) + +const customViewports = { + responsive: { + name: 'Responsive', + styles: { + height: '100%', + width: '100%', + }, + type: 'desktop', + }, + desktop1280: { + name: 'Desktop 1280px', + styles: { + height: '800px', + width: '1280px', + }, + type: 'desktop', + }, + desktop1440: { + name: 'Desktop 1440px', + styles: { + height: '900px', + width: '1440px', + }, + type: 'desktop', + }, +} + +export const parameters = { + actions: { argTypesRegex: '^on[A-Z].*' }, + viewport: { + viewports: { + ...INITIAL_VIEWPORTS, + ...customViewports, + }, + defaultViewport: 'responsive', + }, + darkMode: { + dark: themes.dark, + light: themes.light, + darkClass: 'dark', + lightClass: 'light', + classTarget: 'html', + stylePreview: true, + }, + nextRouter: { + Provider: RouterContext.Provider, + }, + options: { + storySort: (a, b) => { + if (a[1].kind === 'Welcome') { + return -1 + } + + return a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, { numeric: true }) + }, + }, +} + +const channel = addons.getChannel() + +function ThemeWrapper(props) { + const [isDark, setDark] = useState(false) + const darkMode = useDarkMode(false, { + classNameDark: 'dark', + classNameLight: 'light', + element: document.documentElement, + }) + + useEffect(() => { + channel.on(DARK_MODE_EVENT_NAME, setDark) + return () => channel.off(DARK_MODE_EVENT_NAME, setDark) + }, [setDark]) + + useEffect(() => { + if (isDark) { + darkMode.enable() + } else { + darkMode.disable() + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isDark]) + + return props.children +} + +export const decorators = [ + (Story) => ( + + + + ), +] diff --git a/.storybook/styles.css b/.storybook/styles.css index b8b569f..9d43937 100644 --- a/.storybook/styles.css +++ b/.storybook/styles.css @@ -4,10 +4,10 @@ transition: background 0.2s ease; } -.sb-show-main.light-mode { +.sb-show-main.light { background: #fafafa; } -.sb-show-main.dark-mode { +.sb-show-main.dark { background: #101114; } diff --git a/.storybook/theme.js b/.storybook/theme.js new file mode 100644 index 0000000..e770251 --- /dev/null +++ b/.storybook/theme.js @@ -0,0 +1,37 @@ +import { create } from '@storybook/theming' + +const baseStyles = { + // Typography + fontBase: '"Manrope", sans-serif', + fontCode: 'monospace', + + brandTitle: 'Favycon', + brandUrl: 'https://favycon.vercel.app', + brandImage: '/logo-horizontal.svg', +} + +export const light = create({ + base: 'light', + ...baseStyles, + + // Text colors + textColor: '#0e141a', + textInverseColor: '#e4ebf2', + + // Background colors + appContentBg: '#fafafa', +}) + +export const dark = create({ + base: 'dark', + ...baseStyles, + + // Text colors + textColor: '#e4ebf2', + textInverseColor: '#0e141a', + + // Background colors + appBg: '#1e262f', + appContentBg: '#0e141a', + barBg: '#1e262f', +}) diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js deleted file mode 100644 index f3a86d3..0000000 --- a/.storybook/webpack.config.js +++ /dev/null @@ -1,44 +0,0 @@ -const path = require('path') -const WebpackNotifierPlugin = require('webpack-notifier') - -module.exports = ({ config, mode }) => { - config.module.rules.push({ - test: /\.stories\.tsx?$/, - loaders: [ - { - loader: require.resolve('@storybook/source-loader'), - options: { parser: 'typescript' }, - }, - ], - enforce: 'pre', - }) - - config.module.rules.push({ - test: /\.(ts|tsx)$/, - loader: require.resolve('babel-loader'), - options: { - presets: [['react-app', { flow: false, typescript: true }]], - }, - }) - - config.resolve.extensions.push('.ts', '.tsx') - - config.resolve.alias['components'] = path.join(__dirname, '../components') - config.resolve.alias['pages'] = path.join(__dirname, '../pages') - config.resolve.alias['styles'] = path.join(__dirname, '../styles') - config.resolve.alias['utils'] = path.join(__dirname, '../utils') - config.resolve.alias['hooks'] = path.join(__dirname, '../hooks') - - if (mode === 'DEVELOPMENT') { - config.plugins.push( - new WebpackNotifierPlugin({ - title: 'Favycon Storybook', - excludeWarnings: true, - alwaysNotify: true, - contentImage: path.join(__dirname, '../public/favicon.png'), - }) - ) - } - - return config -} diff --git a/.stylelintrc.json b/.stylelintrc.json index 5f77f7b..f26fcd7 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,4 +1,5 @@ { + "customSyntax": "postcss-scss", "extends": [ "stylelint-config-recess-order", "stylelint-config-standard", @@ -13,6 +14,9 @@ { "ignoreAtRules": ["mixin", "include"] } - ] + ], + "font-family-name-quotes": null, + "selector-class-pattern": null, + "color-function-notation": "legacy" } } diff --git a/components/button/index.module.scss b/components/button/index.module.scss index 1a83a6a..6af9b1b 100644 --- a/components/button/index.module.scss +++ b/components/button/index.module.scss @@ -2,7 +2,7 @@ .base { padding: 5px 12px; - font-family: Manrope, Arial, Helvetica Neue, Helvetica, sans-serif; + font-family: Manrope, Arial, '"Helvetica Neue"', Helvetica, sans-serif; font-size: $font-size-title; line-height: $line-height-title; color: $black; @@ -43,7 +43,7 @@ } } -:global(.dark-mode) .primary { +:global(.dark) .primary { color: $white; background-color: $dark-black; border: 1px solid $dark-black; @@ -64,7 +64,7 @@ } } -:global(.dark-mode) .transparent { +:global(.dark) .transparent { @include breakpoint-md { background: $dark-grey; } @@ -113,7 +113,7 @@ color: $grey; } -:global(.dark-mode) .gray { +:global(.dark) .gray { color: $grey-dark; @include breakpoint-md { @@ -133,7 +133,7 @@ } } -:global(.dark-mode) .link { +:global(.dark) .link { color: $white; &:hover { @@ -151,7 +151,7 @@ } } -:global(.dark-mode) .bgLink { +:global(.dark) .bgLink { background: $link-dark; border-color: $link-dark; @@ -171,7 +171,7 @@ } } -:global(.dark-mode) .bgGreen { +:global(.dark) .bgGreen { background: $green; border-color: $green; diff --git a/components/button/index.stories.tsx b/components/button/index.stories.tsx index af2ddfe..e89f371 100644 --- a/components/button/index.stories.tsx +++ b/components/button/index.stories.tsx @@ -1,30 +1,14 @@ -import { withKnobs, text, select, boolean } from '@storybook/addon-knobs' -import { Button } from '.' -import useDarkMode from 'use-dark-mode' +import { ComponentMeta, Story } from '@storybook/react' +import { Button, ButtonProps } from '.' export default { title: 'Button', - decorators: [withKnobs], -} + component: Button, +} as ComponentMeta -export const ButtonDesktop = () => { - const { toggle } = useDarkMode() - const variant = select('variant', ['primary', 'transparent'], 'primary') - const weight = select('weight', ['regular', 'medium', 'semiBold', 'bold'], 'bold') - const body = text('body', 'Mac app coming soon') - const disabled = boolean('disabled', false) - return ( -
- -
-
- -
- ) -} +const Template: Story = (args) => - - ) -} - -DragAndDropDesktop.story = { - name: 'Desktop', -} +export const Desktop = Template.bind({}) +Desktop.args = {} diff --git a/components/drag-and-drop/index.tsx b/components/drag-and-drop/index.tsx index 90aad9f..ff71dd5 100644 --- a/components/drag-and-drop/index.tsx +++ b/components/drag-and-drop/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @next/next/no-img-element */ import dynamic from 'next/dynamic' import React, { useState, useEffect, useCallback, useRef } from 'react' import classnames from 'classnames' @@ -33,7 +34,7 @@ import styles from './index.module.scss' const Modal = dynamic(() => import('react-modal')) const Clipboard = dynamic(() => import('react-clipboard.js')) -type DragAndDropProps = { +export type DragAndDropProps = { onFile: (hasFile: boolean) => void onGenerate: (image: File, pwa: boolean, darkMode: boolean) => Promise onError: (message: string) => void @@ -135,8 +136,8 @@ const DragAndDrop = ({ onFile, onGenerate, onError }: DragAndDropProps) => { setIsLoading(true) const zip = await onGenerate(file, pwa, darkMode) setZipData(zip) - } catch (error) { - onError(error.message || 'Something went wrong. Please try again.') + } catch (error: any) { + onError((error.message as string) || 'Something went wrong. Please try again.') } finally { setIsLoading(false) } diff --git a/components/favycon-error/index.module.scss b/components/favycon-error/index.module.scss index 41f4cb1..8f14a0c 100644 --- a/components/favycon-error/index.module.scss +++ b/components/favycon-error/index.module.scss @@ -45,6 +45,7 @@ 0% { stroke-dashoffset: 0; } + 100% { stroke-dashoffset: 44; } diff --git a/components/favycon-info/index.module.scss b/components/favycon-info/index.module.scss index 1dc8e89..6ee1bbc 100644 --- a/components/favycon-info/index.module.scss +++ b/components/favycon-info/index.module.scss @@ -41,7 +41,7 @@ border: 1px solid $light-grey !important; } -:global(.dark-mode) .button { +:global(.dark) .button { color: $off-white-dark !important; background: $dark-grey !important; border: 1px solid $dark-grey !important; @@ -62,8 +62,8 @@ } } -:global(.dark-mode) .firstParagraph, -:global(.dark-mode) .secondParagraph { +:global(.dark) .firstParagraph, +:global(.dark) .secondParagraph { color: $white-dark !important; } @@ -99,7 +99,7 @@ } } -:global(.dark-mode) .footer { +:global(.dark) .footer { button, a { color: $white-dark; @@ -120,7 +120,7 @@ background-color: rgba($light-grey, 0.64); } -:global(.dark-mode) .overlay { +:global(.dark) .overlay { background-color: rgba($black, 0.8); } @@ -151,7 +151,7 @@ } } -:global(.dark-mode) .content { +:global(.dark) .content { background: $dark-black; @include breakpoint-md { @@ -177,7 +177,7 @@ } } -:global(.dark-mode) .modalContainer { +:global(.dark) .modalContainer { background: $dark-black; } @@ -228,7 +228,7 @@ margin-bottom: 24px; } -:global(.dark-mode) .subTitle { +:global(.dark) .subTitle { @include breakpoint-md { color: $white-dark !important; } @@ -261,7 +261,7 @@ margin-right: 16px; } -:global(.dark-mode) .personRole { +:global(.dark) .personRole { @include breakpoint-md { color: $white-dark !important; } @@ -307,7 +307,7 @@ } /* stylelint-disable-next-line */ -:global(.dark-mode) .personTwitter p { +:global(.dark) .personTwitter p { color: #55acee; &:hover { @@ -315,7 +315,7 @@ } } -:global(.dark-mode) .personTwitter.personGithub p { +:global(.dark) .personTwitter.personGithub p { color: $white; &:hover { @@ -323,7 +323,7 @@ } } -:global(.dark-mode) .personTwitter.personGithub p svg path { +:global(.dark) .personTwitter.personGithub p svg path { fill: $white; } diff --git a/components/favycon-wizard/index.module.scss b/components/favycon-wizard/index.module.scss index 200d582..d96b07c 100644 --- a/components/favycon-wizard/index.module.scss +++ b/components/favycon-wizard/index.module.scss @@ -102,7 +102,7 @@ transition: all 0.2s ease; } -:global(.light-mode) { +:global(.light) { .imageLight { opacity: 1; } @@ -113,7 +113,7 @@ } } -:global(.dark-mode) { +:global(.dark) { .imageLight { visibility: hidden; opacity: 0; diff --git a/components/header/index.module.scss b/components/header/index.module.scss index 7a9e504..fe67b3d 100644 --- a/components/header/index.module.scss +++ b/components/header/index.module.scss @@ -6,12 +6,12 @@ .container { height: 68px; - padding: 20px 20px 0 20px; + padding: 20px 20px 0; margin: 0 auto; @include breakpoint-md { height: 74px; - padding: 26px 26px 0 26px; + padding: 26px 26px 0; } } @@ -56,7 +56,7 @@ } } -:global(.dark-mode) .item { +:global(.dark) .item { background: $darker-black; box-shadow: 0 0 10px rgba($darker-black, 0.5); diff --git a/components/index.stories.mdx b/components/index.stories.mdx new file mode 100644 index 0000000..b624f0f --- /dev/null +++ b/components/index.stories.mdx @@ -0,0 +1,9 @@ +import { Meta } from '@storybook/addon-docs' + + + +# Welcome to Favycon Storybook. + +This is a development environment for the application UI components. + +A story is a unique state one or more components. You can have as many stories as you want. Basically a story is virtual test case. diff --git a/components/index.stories.tsx b/components/index.stories.tsx deleted file mode 100644 index 76b71ba..0000000 --- a/components/index.stories.tsx +++ /dev/null @@ -1,27 +0,0 @@ -export default { - title: 'Welcome', -} - -export const toStorybook = () => ( -
-

Welcome to Favycon Storybook.

-

This is a development environment for the application UI components.

-

- A story is a unique state one or more components. You can have as many stories as you want. Basically a story is - virtual test case. -

- -
-) - -toStorybook.story = { - name: 'to Storybook', -} diff --git a/components/lazy-image/index.tsx b/components/lazy-image/index.tsx index 68895e1..16fc3d6 100644 --- a/components/lazy-image/index.tsx +++ b/components/lazy-image/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @next/next/no-img-element */ import React, { useRef, useEffect } from 'react' import { LazyImageFull, ImageState } from 'react-lazy-images' import objectFitImages from 'object-fit-images' diff --git a/components/media-query-provider/index.tsx b/components/media-query-provider/index.tsx index e0dd92f..d99027d 100644 --- a/components/media-query-provider/index.tsx +++ b/components/media-query-provider/index.tsx @@ -1,6 +1,6 @@ import React, { createContext, useContext, useMemo } from 'react' +import { useMedia } from 'react-use' import PropTypes from 'prop-types' -import useMedia from 'use-media' type MediaQueryProviderProps = { children: PropTypes.ReactNodeLike diff --git a/components/svgs/svg-checkbox.module.scss b/components/svgs/svg-checkbox.module.scss index 72fa6b1..cffc9cd 100644 --- a/components/svgs/svg-checkbox.module.scss +++ b/components/svgs/svg-checkbox.module.scss @@ -12,7 +12,7 @@ } } -:global(.dark-mode) .unchecked { +:global(.dark) .unchecked { fill: $dark-grey; stroke: $darker-black; stroke-width: 1.5px; diff --git a/components/svgs/svg-drop-zone.module.scss b/components/svgs/svg-drop-zone.module.scss index 6419ac1..677fe8d 100644 --- a/components/svgs/svg-drop-zone.module.scss +++ b/components/svgs/svg-drop-zone.module.scss @@ -9,6 +9,6 @@ } } -:global(.dark-mode) .rect { +:global(.dark) .rect { stroke: $off-white-dark; } diff --git a/components/svgs/svg-favycon.module.scss b/components/svgs/svg-favycon.module.scss index 220f90d..a9c3b43 100644 --- a/components/svgs/svg-favycon.module.scss +++ b/components/svgs/svg-favycon.module.scss @@ -5,7 +5,7 @@ transition: fill 0.2s ease; } -:global(.dark-mode) .background { +:global(.dark) .background { fill: #495c70; } @@ -14,7 +14,7 @@ transition: fill 0.2s ease; } -:global(.dark-mode) .foreground { +:global(.dark) .foreground { fill: #1e262f; } @@ -23,7 +23,7 @@ transition: fill 0.2s ease; } -:global(.dark-mode) .top { +:global(.dark) .top { fill: #f6a044; } @@ -32,7 +32,7 @@ transition: fill 0.2s ease; } -:global(.dark-mode) .corner { +:global(.dark) .corner { fill: #f9cdff; } @@ -41,7 +41,7 @@ transition: fill 0.2s ease; } -:global(.dark-mode) .left { +:global(.dark) .left { fill: #728dff; } @@ -50,6 +50,6 @@ transition: fill 0.2s ease; } -:global(.dark-mode) .star { +:global(.dark) .star { fill: #728dff; } diff --git a/components/svgs/svg-image-upload.module.scss b/components/svgs/svg-image-upload.module.scss index 4532ef1..3e95075 100644 --- a/components/svgs/svg-image-upload.module.scss +++ b/components/svgs/svg-image-upload.module.scss @@ -51,7 +51,7 @@ } } -:global(.dark-mode) .rect { +:global(.dark) .rect { fill: #495c70; &.active { diff --git a/components/svgs/svg-info.module.scss b/components/svgs/svg-info.module.scss index 40280c1..82b4939 100644 --- a/components/svgs/svg-info.module.scss +++ b/components/svgs/svg-info.module.scss @@ -9,7 +9,7 @@ } } -:global(.dark-mode) .background { +:global(.dark) .background { fill: $dark-grey; &.active { @@ -26,7 +26,7 @@ } } -:global(.dark-mode) .text { +:global(.dark) .text { fill: $off-white-dark; &.active { diff --git a/components/tool-title/index.module.scss b/components/tool-title/index.module.scss index 19d4537..371bbf9 100644 --- a/components/tool-title/index.module.scss +++ b/components/tool-title/index.module.scss @@ -16,9 +16,9 @@ } } -:global(.dark-mode) .root { +:global(.dark) .root { h1 { - color: #e4ebf2; + color: $off-white-dark; } span { diff --git a/components/tool-title/index.stories.tsx b/components/tool-title/index.stories.tsx index 68bd547..6faca0e 100644 --- a/components/tool-title/index.stories.tsx +++ b/components/tool-title/index.stories.tsx @@ -1,24 +1,12 @@ -import { withKnobs } from '@storybook/addon-knobs' -import useDarkMode from 'use-dark-mode' -import { Button } from 'components/button' -import { ToolTitle } from '.' +import { ComponentMeta, Story } from '@storybook/react' +import { ToolTitle, ToolTitleProps } from '.' export default { title: 'Tool Title', - decorators: [withKnobs], -} + component: ToolTitle, +} as ComponentMeta -export const ToolTitleDesktop = () => { - const { toggle } = useDarkMode(false) - return ( -
- -
- -
- ) -} +const Template: Story = (args) => -ToolTitleDesktop.story = { - name: 'desktop', -} +export const Desktop = Template.bind({}) +Desktop.args = {} diff --git a/components/tool-title/index.tsx b/components/tool-title/index.tsx index daa10c6..72242ee 100644 --- a/components/tool-title/index.tsx +++ b/components/tool-title/index.tsx @@ -3,7 +3,7 @@ import { SvgFavycon } from 'components/svgs/svg-favycon' import styles from './index.module.scss' -type ToolTitleProps = { +export type ToolTitleProps = { hideLogo?: boolean small?: boolean } diff --git a/components/typography/index.module.scss b/components/typography/index.module.scss index 127d07e..8b3abae 100644 --- a/components/typography/index.module.scss +++ b/components/typography/index.module.scss @@ -106,7 +106,7 @@ transition: color 0.2s ease; } -:global(.dark-mode) .black { +:global(.dark) .black { color: $off-white; } @@ -114,7 +114,7 @@ color: $grey; } -:global(.dark-mode) .gray { +:global(.dark) .gray { color: $grey-dark; } @@ -123,7 +123,7 @@ transition: color 0.2s ease; } -:global(.dark-mode) .white:not(.colorImmutable) { +:global(.dark) .white:not(.colorImmutable) { color: $black; } @@ -136,6 +136,6 @@ transition: color 0.2s ease; } -:global(.dark-mode) .muted { +:global(.dark) .muted { color: $grey-dark; } diff --git a/components/typography/index.stories.tsx b/components/typography/index.stories.tsx index 621301e..fff69de 100644 --- a/components/typography/index.stories.tsx +++ b/components/typography/index.stories.tsx @@ -1,34 +1,37 @@ -import { withKnobs, text, select } from '@storybook/addon-knobs' -import useDarkMode from 'use-dark-mode' -import { Button } from 'components/button' -import { Typography } from '.' +import { ComponentMeta, Story } from '@storybook/react' +import { Typography, TypographyProps } from '.' export default { title: 'Typography', - decorators: [withKnobs], -} + component: Typography, + argTypes: { + variant: { + control: { + type: 'select', + }, + options: ['h1', 'smallBody', 'regularBody', 'mediumBody', 'largeBody', 'footer', 'superscript'], + }, + weight: { + control: { + type: 'select', + }, + options: ['regular', 'medium', 'semiBold', 'bold'], + }, + color: { + control: { + type: 'select', + }, + options: ['black', 'gray', 'white'], + }, + }, +} as ComponentMeta -export const TypographyDesktop = () => { - const { toggle } = useDarkMode(false) - const variant = select( - 'variant', - ['h1', 'smallBody', 'regularBody', 'mediumBody', 'largeBody', 'footer', 'superscript'], - 'h1' - ) - const weight = select('weight', ['regular', 'medium', 'semiBold', 'bold'], 'regular') - const color = select('color', ['black', 'gray', 'white'], 'black') - const body = text('body', 'Some text') - return ( -
- - {body} - -
- -
- ) -} +const Template: Story = (args) => -TypographyDesktop.story = { - name: 'Desktop', +export const Desktop = Template.bind({}) +Desktop.args = { + variant: 'h1', + weight: 'regular', + color: 'black', + children: 'Some text', } diff --git a/components/typography/index.tsx b/components/typography/index.tsx index 06a2f33..3333d29 100644 --- a/components/typography/index.tsx +++ b/components/typography/index.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames' import styles from './index.module.scss' -type TypographyProps = { +export type TypographyProps = { children: PropTypes.ReactNodeLike variant: | 'h1' diff --git a/cypress/integration/drag-and-drop.spec.js b/cypress/integration/drag-and-drop.spec.js index da943fa..c899545 100644 --- a/cypress/integration/drag-and-drop.spec.js +++ b/cypress/integration/drag-and-drop.spec.js @@ -8,6 +8,9 @@ describe('Drag & Drop', () => { cy.wait(250) }) + /** + * @param {string} fixture + */ function dropFile(fixture) { cy.get('[data-cy="drag-and-drop"]').attachFile(fixture, { subjectType: 'drag-n-drop', diff --git a/hooks/use-dark-mode.ts b/hooks/use-dark-mode.ts new file mode 100644 index 0000000..6120603 --- /dev/null +++ b/hooks/use-dark-mode.ts @@ -0,0 +1,86 @@ +import { useEffect, useCallback, useMemo } from 'react' +import { useEvent, useLocalStorage } from 'react-use' + +type DarkModeConfig = { + element?: HTMLElement + classNameDark?: string + classNameLight?: string + onChange?: (val: boolean) => void + storageKey?: string + global?: typeof globalThis +} + +type MediaQueryEvents = { + addEventListener: (_: string, handler: (ev: MediaQueryListEvent) => void) => void + removeEventListener: (_: string, handler: (ev: MediaQueryListEvent) => void) => void +} + +// eslint-disable-next-line @typescript-eslint/no-empty-function +const noop = () => {} + +const mockElement = { + classList: { + add: noop, + remove: noop, + }, +} + +const preferDarkQuery = '(prefers-color-scheme: dark)' + +export const useDarkMode = ( + initialValue = false, + { + element, + classNameDark, + classNameLight, + onChange, + storageKey = 'darkMode', + global: glbl = global, + }: DarkModeConfig = {} +) => { + const mql = glbl.matchMedia ? glbl.matchMedia(preferDarkQuery) : ({} as MediaQueryList) + + const mediaQueryEventTarget: MediaQueryEvents = { + addEventListener: (_, handler) => mql.addListener && mql.addListener(handler), + removeEventListener: (_, handler) => mql.removeListener && mql.removeListener(handler), + } + + const isColorSchemeQuerySupported = mql.media === preferDarkQuery + + const getInitialValue = (usersInitialState: boolean) => + isColorSchemeQuerySupported ? mql.matches : usersInitialState + + // Mock element if SSR else real body element. + const defaultElement = (glbl.document && glbl.document.body) || mockElement + + const getDefaultOnChange = useCallback( + (element = defaultElement, classNameDark = 'dark-mode', classNameLight = 'light-mode') => + (val: boolean) => { + element.classList.add(val ? classNameDark : classNameLight) + element.classList.remove(val ? classNameLight : classNameDark) + }, + [defaultElement] + ) + + const [state, setState] = useLocalStorage(storageKey, getInitialValue(initialValue)) + + const stateChangeCallback = useMemo( + () => onChange || getDefaultOnChange(element, classNameDark, classNameLight), + [onChange, element, classNameDark, classNameLight, getDefaultOnChange] + ) + + // Call the onChange handler + useEffect(() => { + stateChangeCallback(state || false) + }, [stateChangeCallback, state]) + + // Listen for media changes and set state. + useEvent('change', ({ matches }) => setState(matches), mediaQueryEventTarget) + + return { + value: state, + enable: useCallback(() => setState(true), [setState]), + disable: useCallback(() => setState(false), [setState]), + toggle: useCallback(() => setState(!state), [state, setState]), + } +} diff --git a/next-env.d.ts b/next-env.d.ts index 7b7aa2c..4f11a03 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,2 +1,5 @@ /// -/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/package.json b/package.json index e5c5970..412fe25 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "favycon", "version": "1.0.0", "description": "A favicon generator tool", + "private": true, "repository": { "type": "git", "url": "git+https://github.com/ruisaraiva19/favycon.git" @@ -30,107 +31,109 @@ "build": "next build", "start": "next start", "lint": "npm-run-all --parallel lint:*", - "lint:scripts": "eslint --ext js,jsx,ts,tsx .", - "lint:styles": "stylelint ./**/*.{css,scss}", - "storybook": "start-storybook -s ./public -p 6006", - "storybook:build": "build-storybook -s ./public", + "lint:scripts": "eslint .", + "lint:styles": "npx stylelint \"**/*.{css,scss}\"", + "lint:json": "prettier --check \"**/*.json\"", + "postinstall": "husky install", + "storybook": "start-storybook -p 6006", + "storybook:build": "build-storybook", "commit": "git-cz", "e2e": "start-test dev 3000 cy:open", "cy:open": "cypress open" }, "dependencies": { - "@fiahfy/ico-convert": "0.0.7", - "adm-zip": "0.4.16", - "blurhash": "1.1.3", + "@fiahfy/ico-convert": "0.0.12", + "adm-zip": "0.5.9", + "blurhash": "1.1.4", "classnames": "2.3.1", - "multer": "1.4.2", - "next": "10.2.3", + "multer": "1.4.3", + "next": "12.0.6", "nprogress": "0.2.0", "object-fit-images": "3.2.4", - "prismjs": "1.24.1", + "prismjs": "1.25.0", "prop-types": "15.7.2", - "react": "16.14.0", + "react": "17.0.2", "react-blurhash": "0.1.3", "react-clipboard.js": "2.0.16", - "react-dom": "16.14.0", - "react-dropzone": "11.3.4", + "react-dom": "17.0.2", + "react-dropzone": "11.4.2", "react-lazy-images": "1.1.0", - "react-modal": "3.14.3", + "react-modal": "3.14.4", "react-transition-group": "4.4.2", - "react-use-gesture": "7.0.16", - "sharp": "0.25.0", - "use-dark-mode": "2.3.1", - "use-media": "1.4.0" + "react-use": "17.3.1", + "react-use-gesture": "7.0.16" }, "devDependencies": { - "@babel/core": "7.14.6", - "@commitlint/cli": "12.1.4", - "@commitlint/config-conventional": "12.1.4", - "@storybook/addon-actions": "5.3.21", - "@storybook/addon-backgrounds": "5.3.21", - "@storybook/addon-console": "1.2.2", - "@storybook/addon-knobs": "5.3.21", - "@storybook/addon-links": "5.3.21", - "@storybook/addon-storysource": "5.3.21", - "@storybook/addon-viewport": "5.3.21", - "@storybook/addons": "5.3.21", - "@storybook/react": "5.3.21", - "@storybook/source-loader": "5.3.21", - "@types/adm-zip": "0.4.33", + "@babel/core": "7.16.0", + "@commitlint/cli": "15.0.0", + "@commitlint/config-conventional": "15.0.0", + "@storybook/addon-a11y": "6.4.7", + "@storybook/addon-actions": "6.4.7", + "@storybook/addon-console": "1.2.3", + "@storybook/addon-docs": "6.4.7", + "@storybook/addon-essentials": "6.4.7", + "@storybook/addon-links": "6.4.7", + "@storybook/addon-postcss": "2.0.0", + "@storybook/addons": "6.4.7", + "@storybook/api": "6.4.7", + "@storybook/builder-webpack5": "6.4.7", + "@storybook/client-api": "6.4.7", + "@storybook/components": "6.4.7", + "@storybook/core-events": "6.4.7", + "@storybook/manager-webpack5": "6.4.7", + "@storybook/node-logger": "6.4.7", + "@storybook/react": "6.4.7", + "@storybook/theming": "6.4.7", + "@types/adm-zip": "0.4.34", "@types/multer": "1.4.7", - "@types/node": "14.14.34", + "@types/node": "16.11.11", "@types/nprogress": "0.2.0", "@types/object-fit-images": "3.2.2", "@types/prismjs": "1.16.6", - "@types/react": "17.0.14", - "@types/react-modal": "3.12.1", - "@types/react-transition-group": "4.4.2", - "@types/sharp": "0.25.1", - "@typescript-eslint/eslint-plugin": "4.28.3", - "@typescript-eslint/parser": "4.28.3", + "@types/react": "17.0.37", + "@types/react-modal": "3.13.1", + "@types/react-transition-group": "4.4.4", + "@types/sharp": "0.29.4", + "@typescript-eslint/eslint-plugin": "5.5.0", + "@typescript-eslint/parser": "5.5.0", "all-contributors-cli": "6.20.0", - "babel-eslint": "10.1.0", - "babel-loader": "8.2.2", - "babel-plugin-prismjs": "2.1.0", - "babel-preset-react-app": "10.0.0", + "babel-loader": "8.2.3", "commitizen": "4.2.4", - "cypress": "5.6.0", - "cypress-file-upload": "4.1.1", + "cypress": "9.1.1", + "cypress-file-upload": "5.0.8", "cz-conventional-changelog": "3.3.0", "dotenv": "10.0.0", - "eslint": "7.30.0", + "eslint": "8.4.0", + "eslint-config-next": "12.0.6", "eslint-config-prettier": "8.3.0", - "eslint-config-react": "1.1.7", - "eslint-config-react-app": "6.0.0", - "eslint-plugin-cypress": "2.11.3", - "eslint-plugin-flowtype": "5.8.0", - "eslint-plugin-import": "2.23.4", - "eslint-plugin-jsx-a11y": "6.4.1", - "eslint-plugin-prettier": "3.4.0", - "eslint-plugin-react": "7.24.0", - "eslint-plugin-react-hooks": "4.2.0", - "husky": "4.3.8", - "lint-staged": "11.0.0", + "eslint-plugin-cypress": "2.12.1", + "eslint-plugin-prettier": "4.0.0", + "eslint-plugin-storybook": "0.5.3", + "husky": "7.0.4", + "lint-staged": "12.1.2", "npm-run-all": "4.1.5", + "postcss": "8.4.4", "postcss-100vh-fix": "1.0.2", "postcss-flexbugs-fixes": "5.0.2", - "postcss-preset-env": "6.7.0", + "postcss-preset-env": "7.0.1", + "postcss-scss": "4.0.2", "prettier": "2.3.2", - "react-refresh": "0.10.0", - "regenerator-runtime": "0.13.7", - "sass": "1.35.2", - "sass-loader": "12.1.0", - "semantic-release": "17.4.4", - "start-server-and-test": "1.12.6", - "stylelint": "13.13.1", - "stylelint-config-css-modules": "2.2.0", - "stylelint-config-prettier": "8.0.2", - "stylelint-config-recess-order": "2.4.0", - "stylelint-config-standard": "22.0.0", - "stylelint-prettier": "1.2.0", - "stylelint-scss": "3.19.0", - "typescript": "4.3.5", - "webpack-notifier": "1.13.0" + "react-refresh": "0.11.0", + "sass": "1.44.0", + "sass-loader": "12.3.0", + "semantic-release": "18.0.1", + "start-server-and-test": "1.14.0", + "storybook-addon-next-router": "3.1.0", + "storybook-dark-mode": "1.0.8", + "stylelint": "14.1.0", + "stylelint-config-css-modules": "2.3.0", + "stylelint-config-prettier": "9.0.3", + "stylelint-config-recess-order": "3.0.0", + "stylelint-config-standard": "24.0.0", + "stylelint-prettier": "2.0.0", + "stylelint-scss": "4.0.0", + "typescript": "4.4.4", + "webpack": "5.64.4" }, "config": { "commitizen": { diff --git a/pages/_document.tsx b/pages/_document.tsx index 9ed2981..a5ee984 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @next/next/no-sync-scripts */ import Document, { Html, Head, Main, NextScript } from 'next/document' const isProd = process.env.NODE_ENV === 'production' @@ -7,7 +8,9 @@ class MyDocument extends Document { return ( - {isProd &&