diff --git a/.storybook/babel-storybook-config.ts b/.storybook/babel-storybook-config.ts index 8faa8b51857..4cde75eab03 100644 --- a/.storybook/babel-storybook-config.ts +++ b/.storybook/babel-storybook-config.ts @@ -1,4 +1,6 @@ -export const babelConfig = { +import { TransformOptions } from "@babel/core" + +export const babelConfig: TransformOptions = { sourceType: "unambiguous", presets: [ [ diff --git a/.storybook/i18next.ts b/.storybook/i18next.ts new file mode 100644 index 00000000000..8189c93bc8c --- /dev/null +++ b/.storybook/i18next.ts @@ -0,0 +1,41 @@ +import i18n, { Resource } from "i18next" +import { initReactI18next } from "gatsby-plugin-react-i18next" + +export const baseLocales = { + en: { title: "English", left: "En" }, + zh: { title: "中国人", left: "Zh" }, + ru: { title: "Русский", left: "Ru" }, + uk: { title: "українська", left: "Uk" }, +} + +// Only i18n files named in this array are being exposed to Storybook. Add filenames as necessary. +const ns = ["common", "page-about", "page-upgrades", "page-developers-index"] +const supportedLngs = Object.keys(baseLocales) + +/** + * Taking the ns array and combining all the ids + * under a single ns per language, set to the default of "translation" + */ +const resources: Resource = ns.reduce((acc, n) => { + supportedLngs.forEach((lng) => { + if (!acc[lng]) acc[lng] = {} + acc[lng] = { + translation: { + ...acc[lng].translation, + ...require(`../src/intl/${lng}/${n}.json`), + }, + } + }) + return acc +}, {}) + +i18n.use(initReactI18next).init({ + debug: true, + fallbackLng: "en", + interpolation: { escapeValue: false }, + react: { useSuspense: false }, + supportedLngs, + resources, +}) + +export default i18n diff --git a/.storybook/main.js b/.storybook/main.ts similarity index 58% rename from .storybook/main.js rename to .storybook/main.ts index 87f2ed7bd51..9f3bb88a066 100644 --- a/.storybook/main.js +++ b/.storybook/main.ts @@ -1,8 +1,8 @@ -const { propNames } = require("@chakra-ui/react") +import { StorybookConfig } from "@storybook/react-webpack5" +import { propNames } from "@chakra-ui/react" +import { babelConfig } from "./babel-storybook-config" -const { babelConfig } = require("./babel-storybook-config") - -module.exports = { +const config: StorybookConfig = { stories: ["../src/components/**/*.stories.tsx"], addons: [ "@storybook/addon-links", @@ -11,9 +11,10 @@ module.exports = { // https://storybook.js.org/addons/@storybook/addon-a11y/ "@storybook/addon-a11y", "@chakra-ui/storybook-addon", + "storybook-react-i18next", ], staticDirs: ["../static"], - babel: async (options) => ({ + babel: async () => ({ ...babelConfig, }), framework: { @@ -27,30 +28,30 @@ module.exports = { }, features: {}, webpackFinal: async (config) => { - const isRuleExist = - config.module && config.module.rules && config.module.rules.length - if (isRuleExist) { - // Transpile Gatsby module because Gatsby includes un-transpiled ES6 code. - config.module.rules[0].exclude = [ - /node_modules\/(?!(gatsby|gatsby-script)\/)/, - ] - - // Remove core-js to prevent issues with Storybook - config.module.rules[0].exclude = [/core-js/] - } - if ( - isRuleExist && - config.module.rules[0].use && - config.module.rules[0].use.length + config.module != undefined && + config.module.rules != undefined && + config.module.rules[0] !== "..." ) { - // Use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook - config.module.rules[0].use[0].options.plugins.push( - require.resolve("babel-plugin-remove-graphql-queries") - ) + config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/] + config.module.rules[0].use = [ + { + loader: require.resolve("babel-loader"), + options: { + presets: [ + // use @babel/preset-react for JSX and env (instead of staged presets) + require.resolve("@babel/preset-react"), + require.resolve("@babel/preset-env"), + ], + plugins: [ + // use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook + require.resolve("babel-plugin-remove-graphql-queries"), + ], + }, + }, + ] } - config.resolve.mainFields = ["browser", "module", "main"] return config }, typescript: { @@ -83,3 +84,5 @@ module.exports = { }, }, } + +export default config diff --git a/.storybook/preview.js b/.storybook/preview.ts similarity index 51% rename from .storybook/preview.js rename to .storybook/preview.ts index ef0c86d3545..6ff33e86b86 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.ts @@ -1,10 +1,9 @@ +import { Preview } from "@storybook/react" import { action } from "@storybook/addon-actions" +import i18n, { baseLocales } from "./i18next" import theme from "../src/@chakra-ui/gatsby-plugin/theme" -import "../static/fonts/inter-font-face.css" -import "../static/fonts/ibm-plex-font-face.css" - const chakraBreakpointArray = Object.entries(theme.breakpoints) // Gatsby's Link overrides: @@ -26,42 +25,48 @@ window.___navigate = (pathname) => { action("NavigateTo:")(pathname) } -export const parameters = { - actions: { argTypesRegex: "^on[A-Z].*" }, - backgrounds: { - disable: true, +const preview: Preview = { + globals: { + locale: "en", + locales: baseLocales, }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, + parameters: { + i18n, + actions: { argTypesRegex: "^on[A-Z].*" }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, }, - }, - backgrounds: { - disable: true, - }, - chakra: { - theme, - }, - layout: "centered", - // Modify viewport selection to match Chakra breakpoints (or custom breakpoints) - viewport: { - viewports: chakraBreakpointArray.reduce((prevVal, currVal) => { - const [token, key] = currVal + backgrounds: { + disable: true, + }, + chakra: { + theme, + }, + layout: "centered", + // Modify viewport selection to match Chakra breakpoints (or custom breakpoints) + viewport: { + viewports: chakraBreakpointArray.reduce((prevVal, currVal) => { + const [token, key] = currVal - // Unnecessary breakpoint - if (token === "base") return { ...prevVal } + // Unnecessary breakpoint + if (token === "base") return { ...prevVal } - return { - ...prevVal, - [token]: { - name: token, - styles: { - width: key, - height: "600px", + return { + ...prevVal, + [token]: { + name: token, + styles: { + width: key, + height: "600px", + }, }, - }, - } - }, {}), + } + }, {}), + }, }, } + +export default preview diff --git a/docs/best-practices.md b/docs/best-practices.md index 3d4c2d6d614..56012759d61 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -102,45 +102,58 @@ export default ComponentName ## Styling -- `src/theme.ts` - Declares site color themes, breakpoints and other constants (try to utilize these colors first) -- We use [emotion](https://emotion.sh/) +We use [Chakra UI](https://chakra-ui.com/). - - Tagged template literals are used to style custom components +`src/@chakra-ui/gatsby-plugin/theme.ts` - Holds all the theme configuration. This is where you can find the colors, fonts, component themes, variants, etc. - ```tsx - // Example of styling syntax using emotion - - import styled from "@emotion/styled" - - const GenericButton = styled.div` - width: 200px; - height: 50px; - ` - const PrimaryButton = styled(GenericButton)` - background: blue; - ` - const SecondaryButton = styled(GenericButton)` - background: red; - ` - - // These are each components, capitalized by convention, and can be used within JSX code - // ie: Text - ``` +- Wrappers or layout divs -- Values from `src/theme.ts` are automatically passed as a prop object to styled components +Use the [native layouts components](https://chakra-ui.com/docs/components/box) - ```tsx - // Example of theme.ts usage +```tsx + +``` - import styled from "@emotion/styled" +Center things using the `
` component - const Container = styled.div` - background: ${(props) => props.theme.colors.background}; - @media (max-width: ${(props) => props.theme.breakpoints.s}) { - font-size: #{(props) => props.theme.fontSized.s}; - } - ` - ``` +```tsx +
+``` + +- Group buttons using `` or `` + +```tsx + + + + + +// or + + + + +``` + +- Breakpoints + +Use [the Chakra default breakpoints](https://chakra-ui.com/docs/styled-system/theme#breakpoints). + +```tsx + +``` + +- Theme colors + +```tsx + +``` + +> Note the dotted notation. In Chakra, the values are referred to as "semantic tokens" and the new theme applies a nested structure of like tokens for better organization. See [semanticTokens.ts](../src/%40chakra-ui/gatsby-plugin/semanticTokens.ts) + +> Note 2: all the previous colors defined in the old theme `src/theme.ts` were +> ported into the new theme for compatibility reasons. Those colors will +> transition out of the codebase as we adopt the DS colors. - [Framer Motion](https://www.framer.com/motion/) - An open source and production-ready motion library for React on the web, used for our animated designs - **Emojis**: We use [Twemoji](https://twemoji.twitter.com/), an open-source emoji set created by Twitter. These are hosted by us, and used to provide a consistent experience across operating systems. @@ -154,29 +167,14 @@ import Emoji from "./Emoji" ``` - **Icons**: We use [React Icons](https://react-icons.github.io/react-icons/) - - `src/components/Icon.ts` is the component used to import icons to be used - - If an icon you want to use is not listed you will need to add it to this file - -`src/components/Icon.ts`: - -```tsx -// Example of how to add new icon not listed -import { ZzIconName } from "react-icons/zz" - -// Then add to IconContext.Provider children: -{ - name === "alias" && -} -``` - -From React component: + with [Chakra UI Icon component](https://chakra-ui.com/docs/components/icon/usage) ```tsx -// Example of icon use -import Icon from "./Icon" +import { Icon } from "@chakra-ui/react" +import { BsQuestionSquareFill } from "react-icons/bs" -// Within JSX: -; +// wrap your imported icon with the `Icon` component from Chakra UI +; ``` ## Image loading and API calls using GraphQL diff --git a/docs/chakra-migration-guide.md b/docs/chakra-migration-guide.md deleted file mode 100644 index d8b943119c7..00000000000 --- a/docs/chakra-migration-guide.md +++ /dev/null @@ -1,169 +0,0 @@ -# Chakra migration guide - -This is a reference for migrating our current `styled` components from `emotion` to [Chakra](https://chakra-ui.com/) components. - -This is part of our [UI library implementation epic](https://github.com/ethereum/ethereum-org-website/issues/6374). - -## Replace styled components with Chakra components - -All `styled` components need to be removed and replaced with the corresponded Chakra component. [See the list of components](https://chakra-ui.com/docs/components). - -Use as much native Chakra components as possible. - -### Wrappers or layout divs - -Use the [native layouts components](https://chakra-ui.com/docs/components/box) - -```tsx -// before -const Wrapper = styled.div` - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; -` - -// now - -``` - -Center things using the `
` component - -```tsx -// before -const Center = styled.div` - height: 100px; - display: flex; - justify-content: center; - align-items: center; -` - -// now -
-``` - -Group buttons using `` or `` - -```tsx -// before -const ButtonRow = styled.div` - display: flex; - align-items: center; - flex-wrap: wrap; -` - -// now - - - - - -// or - - - - -``` - -## Override styles using style props - -- You can see how to use the different style props here: [https://chakra-ui.com/docs/styled-system/style-props](https://chakra-ui.com/docs/styled-system/style-props#margin-and-padding) -- Chakra default values are documented here: [https://chakra-ui.com/docs/styled-system/theme](https://chakra-ui.com/docs/styled-system/theme) - -```tsx -// before -const Paragraph = styled.p` - font-size: 1rem; - margin: 1rem; -` - -// now - -``` - -## Breakpoints - -We will use [the Chakra default breakpoints](https://chakra-ui.com/docs/styled-system/theme#breakpoints) from now on. Check the following table to do the conversion: -| old breakpoints | new breakpoints | -|-----------------|-----------------| -| xs | - | -| s | sm | -| m | md | -| l | lg | -| xl | xl | -| - | 2xl | - -```tsx -// before -const Container = styled.div` - display: flex; - @media (max-width: ${(props) => props.theme.breakpoints.s}) { - display: block; - } -` - -// now - -``` - -## Theme colors - -All the previous colors defined in the old theme `src/theme.ts` were ported into the new theme as well. Use the same color variables. - -```tsx -// before -const Text = styled.p` - color: ${({ theme }) => theme.colors.primary}; - background-color: ${({ theme }) => theme.colors.background}; -` - -// now - -``` - -> Note the dotted notation. In Chakra, the values are referred to as "semantic tokens" and the new theme applies a nested structure of like tokens for better organization. See [semanticTokens.ts](../src/%40chakra-ui/gatsby-plugin/semanticTokens.ts) - - - -## Update dependencies - -- [Deprecated] `src/components/Icon` - use the [Chakra Icon](https://chakra-ui.com/docs/components/icon/usage) instead. - -```tsx -import { Icon } from "@chakra-ui/react" -import { BsQuestionSquareFill } from "react-icons/bs" -; -``` - -- [Deprecated]`src/components/SharedStyledComponents` - we are not using this anymore, replace everything with Chakra components. - -```tsx -// before -import { ButtonPrimary, ButtonSecondary } from "../SharedStyledComponents" - -// now -import Button from "../Button" // <-- use the new Button component built with Chakra - -// use our primary button (uses the default `solid` variant) - ) diff --git a/src/components/Card/Card.stories.tsx b/src/components/Card/Card.stories.tsx index e032e7e7bd2..51d9dd02be7 100644 --- a/src/components/Card/Card.stories.tsx +++ b/src/components/Card/Card.stories.tsx @@ -1,6 +1,7 @@ +import React from "react" import { Box } from "@chakra-ui/react" import { Meta, StoryFn } from "@storybook/react" -import React from "react" +import { useTranslation } from "react-i18next" import Card, { IProps } from "." import Button from "../Button" @@ -17,14 +18,18 @@ export default { ], } as Meta -const defaultProps: IProps = { - emoji: ":woman_student:", - title: "Learn Ethereum development", - description: "Read up on core concepts and the Ethereum stack with our docs", -} +export const Default: StoryFn = (args) => { + const { t } = useTranslation() -export const Default: StoryFn = (args) => ( - - - -) + const defaultProps: IProps = { + emoji: ":woman_student:", + title: t("page-developers-learn"), + description: t("page-developers-learn-desc"), + } + + return ( + + + + ) +} diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx deleted file mode 100644 index 20e695ff9b9..00000000000 --- a/src/components/Icon.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import React from "react" -import styled from "@emotion/styled" -import { IconContext } from "react-icons" -import { - FaGithub, - FaTwitter, - FaYoutube, - FaDiscord, - FaRedditAlien, - FaStackExchange, - FaGlobe, -} from "react-icons/fa" -import { - MdAdd, - MdBrightness2, - MdOutlineCancel, - MdCircle, - MdClose, - MdDone, - MdExpandLess, - MdExpandMore, - MdArrowForward, - MdInfoOutline, - MdLanguage, - MdMenu, - MdSearch, - MdWbSunny, - MdFlip, - MdLiveHelp, - MdKeyboardArrowLeft, - MdKeyboardArrowRight, -} from "react-icons/md" -import { - BsArrowCounterclockwise, - BsQuestionSquareFill, - BsToggleOff, - BsToggleOn, -} from "react-icons/bs" -import { IoCodeOutline, IoCodeDownload } from "react-icons/io5" - -const socialColors = { - reddit: "#ff4301", - twitter: "#1da1f2", - youtube: "#ff0000", - discord: "#7289da", - stackExchange: "#48a2da", -} - -export interface IProps { - name?: string - color?: string | boolean - size?: string - className?: string -} - -const Icon: React.FC = ({ - name = "", - color, - size = "24", - className, -}) => ( - - {name === "add" && } - {name === "chevronUp" && } - {name === "chevronDown" && } - {name === "circle" && } - {name === "arrowRight" && } - {name === "arrowRightIos" && } - {name === "arrowLeftIos" && } - {name === "cancel" && } - {name === "close" && } - {name === "darkTheme" && } - {name === "github" && } - {name === "info" && } - {name === "language" && } - {name === "lightTheme" && } - {name === "toggleOff" && } - {name === "toggleOn" && } - {name === "menu" && } - {name === "check" && } - {name === "twitter" && ( - - )} - {name === "search" && } - {name === "youtube" && ( - - )} - {name === "discord" && ( - - )} - {name === "glossary" && } - {name === "codeDownload" && } - {name === "code" && } - {name === "flip" && } - {name === "help" && } - {name === "reddit" && ( - - )} - {name === "stackExchange" && ( - - )} - {name === "webpage" && } - {name === "arrowCounterClockwise" && } - -) - -const StyledIcon = styled(Icon)` - fill: ${(props) => - props.color ? props.color : props.theme.colors.secondary}; - - &:hover svg { - fill: ${(props) => props.theme.colors.primary}; - } -` - -export default StyledIcon diff --git a/src/components/InfoBanner.tsx b/src/components/InfoBanner.tsx index 8014feebecb..d16edfd2b22 100644 --- a/src/components/InfoBanner.tsx +++ b/src/components/InfoBanner.tsx @@ -1,5 +1,5 @@ import React from "react" -import { Flex, FlexProps, Text } from "@chakra-ui/react" +import { Flex, FlexProps, LightMode, Text } from "@chakra-ui/react" import Emoji from "./Emoji" export interface IProps extends FlexProps { @@ -23,45 +23,47 @@ const InfoBanner: React.FC = ({ ...props }) => { const banner = ( - - {emoji && ( - - )} + - {title && ( - - {title} - + {emoji && ( + )} - {children} + + {title && ( + + {title} + + )} + {children} + - + ) return shouldCenter ? {banner} : banner } diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 6af89be19cd..12fe2537ef2 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,12 +1,7 @@ import React, { useState, useEffect } from "react" import { ApolloProvider } from "@apollo/client" -import { useColorModeValue, Text } from "@chakra-ui/react" -import { ThemeProvider } from "@emotion/react" - import { Flex } from "@chakra-ui/react" -import { lightTheme, darkTheme } from "../theme" - import Footer from "./Footer" import ZenMode from "./ZenMode" import Nav from "./Nav" @@ -18,6 +13,7 @@ import FeedbackWidget from "./FeedbackWidget" import { SkipLink } from "./SkipLink" import { ZenModeContext } from "../contexts/ZenModeContext" +import { lightTheme as oldTheme } from "../theme" import { useKeyPress } from "../hooks/useKeyPress" @@ -55,9 +51,6 @@ const Layout: React.FC = ({ pageContext, children, }) => { - // TODO: tmp - for backward compatibility with old theme - const theme = useColorModeValue(lightTheme, darkTheme) - const [isZenMode, setIsZenMode] = useState(false) const [shouldShowSideNav, setShouldShowSideNav] = useState(false) @@ -108,63 +101,61 @@ const Layout: React.FC = ({ return ( - - - - - - + + + + + + + +