├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .prettierrc.json ├── .storybook ├── main.js ├── preview-head.html └── preview.jsx ├── .test └── setup.js ├── README.md ├── babel.config.json ├── package-lock.json ├── package.json ├── public ├── _redirects ├── assets │ └── images │ │ └── javascript.svg ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── api │ ├── dados.json │ ├── map-data.js │ ├── map-data.test.js │ ├── map-menu.js │ ├── map-menu.test.js │ ├── map-sections.js │ └── map-sections.test.js ├── components │ ├── Footer │ │ ├── Footer.spec.jsx │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js │ ├── GoTop │ │ ├── GoTop.spec.jsx │ │ ├── __snapshots__ │ │ │ └── GoTop.spec.jsx.snap │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js │ ├── GridContent │ │ ├── GridContent.spec.jsx │ │ ├── __snapshots__ │ │ │ └── GridContent.spec.jsx.snap │ │ ├── index.jsx │ │ ├── mock.js │ │ ├── stories.jsx │ │ └── styles.js │ ├── GridImage │ │ ├── GridImage.test.jsx │ │ ├── __snapshots__ │ │ │ └── GridImage.test.jsx.snap │ │ ├── index.jsx │ │ ├── mock.js │ │ ├── stories.jsx │ │ └── styles.js │ ├── GridText │ │ ├── GridText.test.jsx │ │ ├── __snapshots__ │ │ │ └── GridText.test.jsx.snap │ │ ├── index.jsx │ │ ├── mock.js │ │ ├── stories.jsx │ │ └── styles.js │ ├── GridTwoColumns │ │ ├── GridTwoColumns.spec.jsx │ │ ├── __snapshots__ │ │ │ └── GridTwoColumns.spec.jsx.snap │ │ ├── index.jsx │ │ ├── mock.js │ │ ├── stories.jsx │ │ └── styles.js │ ├── Heading │ │ ├── Heading.spec.jsx │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js │ ├── LogoLink │ │ ├── LogoLink.spec.jsx │ │ ├── __snapshots__ │ │ │ └── LogoLink.spec.jsx.snap │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js │ ├── Menu │ │ ├── Menu.spec.jsx │ │ ├── __snapshots__ │ │ │ └── Menu.spec.jsx.snap │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js │ ├── MenuLink │ │ ├── Menu.spec.jsx │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js │ ├── NavLinks │ │ ├── NavLinks.spec.jsx │ │ ├── __snapshots__ │ │ │ └── NavLinks.spec.jsx.snap │ │ ├── index.jsx │ │ ├── mock.js │ │ ├── stories.jsx │ │ └── styles.js │ ├── SectionBackground │ │ ├── SectionBackground.spec.jsx │ │ ├── __snapshots__ │ │ │ └── SectionBackground.spec.jsx.snap │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js │ ├── SectionContainer │ │ ├── SectionContainer.spec.jsx │ │ ├── __snapshots__ │ │ │ └── SectionContainer.spec.jsx.snap │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js │ └── TextComponent │ │ ├── TextComponent.spec.jsx │ │ ├── __snapshots__ │ │ └── TextComponent.spec.jsx.snap │ │ ├── index.jsx │ │ ├── stories.jsx │ │ └── styles.js ├── config │ └── index.js ├── fonts │ ├── montserrat-v24-latin │ │ ├── montserrat-v24-latin-900.woff │ │ ├── montserrat-v24-latin-900.woff2 │ │ ├── montserrat-v24-latin-900italic.woff │ │ └── montserrat-v24-latin-900italic.woff2 │ └── open-sans-v29-latin │ │ ├── open-sans-v29-latin-700.woff │ │ ├── open-sans-v29-latin-700.woff2 │ │ ├── open-sans-v29-latin-700italic.woff │ │ ├── open-sans-v29-latin-700italic.woff2 │ │ ├── open-sans-v29-latin-italic.woff │ │ ├── open-sans-v29-latin-italic.woff2 │ │ ├── open-sans-v29-latin-regular.woff │ │ └── open-sans-v29-latin-regular.woff2 ├── index.html ├── index.jsx ├── styles │ ├── global-styles.js │ ├── render-theme.jsx │ └── theme.js └── templates │ ├── Base │ ├── __snapshots__ │ │ └── test.jsx.snap │ ├── index.jsx │ ├── mock.jsx │ ├── stories.jsx │ ├── styles.js │ └── test.jsx │ ├── Home │ ├── Home.spec.jsx │ ├── index.jsx │ └── styles.js │ ├── Loading │ ├── index.jsx │ └── styles.js │ └── PageNotFound │ └── index.jsx └── vite.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "jest": true, 6 | "node": true 7 | }, 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:react/recommended", 11 | "plugin:react-hooks/recommended", 12 | "plugin:prettier/recommended", 13 | "plugin:storybook/recommended" 14 | ], 15 | "globals": { 16 | "Atomics": "readonly", 17 | "SharedArrayBuffer": "readonly" 18 | }, 19 | "parser": "@babel/eslint-parser", 20 | "parserOptions": { 21 | "ecmaFeatures": { 22 | "jsx": true 23 | }, 24 | "ecmaVersion": "latest", 25 | "sourceType": "module" 26 | }, 27 | "plugins": [ 28 | "react", 29 | "prettier", 30 | "react-hooks" 31 | ], 32 | "settings": { 33 | "react": { 34 | "version": "detect" 35 | } 36 | }, 37 | "rules": { 38 | "react/react-in-jsx-scope": "off" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | coverage/ 27 | venv 28 | __localcode 29 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "endOfLine": "lf", 5 | "htmlWhitespaceSensitivity": "ignore", 6 | "insertPragma": false, 7 | "jsxSingleQuote": false, 8 | "printWidth": 80, 9 | "proseWrap": "always", 10 | "quoteProps": "as-needed", 11 | "requirePragma": false, 12 | "semi": true, 13 | "singleQuote": true, 14 | "tabWidth": 2, 15 | "trailingComma": "all", 16 | "useTabs": false, 17 | "vueIndentScriptAndStyle": false, 18 | "embeddedLanguageFormatting": "off" 19 | } 20 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: [ 3 | '../src/**/*.stories.mdx', 4 | '../src/**/*.stories.@(js|jsx|ts|tsx)', 5 | '../src/**/stories.@(js|jsx|ts|tsx)', 6 | ], 7 | addons: [ 8 | '@storybook/addon-links', 9 | '@storybook/addon-essentials', 10 | '@storybook/addon-interactions', 11 | ], 12 | framework: '@storybook/react', 13 | core: { 14 | builder: '@storybook/builder-vite', 15 | }, 16 | features: { 17 | storyStoreV7: true, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.storybook/preview.jsx: -------------------------------------------------------------------------------- 1 | import { ThemeProvider } from 'styled-components'; 2 | import { theme } from '../src/styles/theme'; 3 | import { GlobalStyles } from '../src/styles/global-styles'; 4 | 5 | export const parameters = { 6 | actions: { argTypesRegex: '^on[A-Z].*' }, 7 | controls: { 8 | matchers: { 9 | color: /(background|color)$/i, 10 | date: /Date$/, 11 | }, 12 | }, 13 | }; 14 | 15 | export const decorators = [ 16 | (Story) => ( 17 | 18 | 19 | 20 | 21 | ), 22 | ]; 23 | -------------------------------------------------------------------------------- /.test/setup.js: -------------------------------------------------------------------------------- 1 | import { vi } from 'vitest'; 2 | import '@testing-library/jest-dom'; 3 | import 'jest-styled-components'; 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Creating a boilerplate for React 18 in ViteJS 2 | 3 | ## Editorconfig 4 | 5 | If your are using editorconfig, this is my config: 6 | 7 | ``` 8 | # EditorConfig is awesome: https://EditorConfig.org 9 | 10 | # top-most EditorConfig file 11 | root = true 12 | 13 | [*] 14 | indent_style = space 15 | indent_size = 2 16 | end_of_line = lf 17 | charset = utf-8 18 | trim_trailing_whitespace = true 19 | insert_final_newline = true 20 | ``` 21 | 22 | ## For ESLint and Prettier 23 | 24 | ``` 25 | npm i eslint @babel/eslint-parser @babel/preset-env @babel/preset-react prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-storybook -D 26 | ``` 27 | 28 | Create the files `.eslintrc.js`, `.prettierrc.js` and a file called 29 | `babel.config.js`. 30 | 31 | .eslintrc.js 32 | 33 | ```javascript 34 | module.exports = { 35 | env: { 36 | browser: true, 37 | es2021: true, 38 | jest: true, 39 | node: true, 40 | }, 41 | extends: [ 42 | 'eslint:recommended', 43 | 'plugin:react/recommended', 44 | 'plugin:react-hooks/recommended', 45 | 'plugin:prettier/recommended', 46 | 'plugin:storybook/recommended', 47 | ], 48 | globals: { 49 | Atomics: 'readonly', 50 | SharedArrayBuffer: 'readonly', 51 | }, 52 | parser: '@babel/eslint-parser', 53 | parserOptions: { 54 | ecmaFeatures: { 55 | jsx: true, 56 | }, 57 | ecmaVersion: 'latest', 58 | sourceType: 'module', 59 | }, 60 | plugins: ['react', 'prettier', 'react-hooks'], 61 | settings: { 62 | react: { 63 | version: 'detect', 64 | }, 65 | }, 66 | rules: { 67 | 'react/react-in-jsx-scope': 'off', 68 | }, 69 | }; 70 | ``` 71 | 72 | .prettierrc.js 73 | 74 | ```javascript 75 | module.exports = { 76 | arrowParens: 'always', 77 | bracketSpacing: true, 78 | endOfLine: 'lf', 79 | htmlWhitespaceSensitivity: 'ignore', 80 | insertPragma: false, 81 | jsxSingleQuote: false, 82 | printWidth: 80, 83 | proseWrap: 'always', 84 | quoteProps: 'as-needed', 85 | requirePragma: false, 86 | semi: true, 87 | singleQuote: true, 88 | tabWidth: 2, 89 | trailingComma: 'all', 90 | useTabs: false, 91 | vueIndentScriptAndStyle: false, 92 | embeddedLanguageFormatting: 'off', 93 | }; 94 | ``` 95 | 96 | babel.config.js 97 | 98 | ```javascript 99 | module.exports = { 100 | presets: [ 101 | '@babel/preset-env', 102 | ['@babel/preset-react', { runtime: 'automatic' }], 103 | ], 104 | }; 105 | ``` 106 | 107 | ## Prop-types? 108 | 109 | If your are using proptypes, install it: 110 | 111 | ``` 112 | npm i prop-types 113 | ``` 114 | 115 | And use: 116 | 117 | ## Testing 118 | 119 | For tests, we're going to use `vitest` and `testing-library`. 120 | 121 | ### For vitest 122 | 123 | ``` 124 | npm i -D vitest jsdom @testing-library/react @testing-library/jest-dom 125 | ``` 126 | 127 | Now, in your vite.config.js: 128 | 129 | ```javascript 130 | import { defineConfig } from 'vite'; 131 | import react from '@vitejs/plugin-react'; 132 | 133 | // https://vitejs.dev/config/ 134 | export default defineConfig({ 135 | plugins: [react()], 136 | root: 'src', 137 | build: { 138 | outDir: '../dist', 139 | }, 140 | test: { 141 | globals: true, 142 | environment: 'jsdom', 143 | setupFiles: ['../.test/setup.js'], 144 | include: ['**/*(*.)?{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], 145 | exclude: ['node_modules', 'dist', '.idea', '.git', '.cache'], 146 | }, 147 | }); 148 | ``` 149 | 150 | For the file in `/.test/setup.js`, use: 151 | 152 | ```javascript 153 | import '@testing-library/jest-dom'; 154 | import 'jest-styled-components'; 155 | ``` 156 | 157 | Ps.: I added `jest-styled-components` to make it easy for me. I am going to use 158 | Styled-Components. If your aren't, please comment that line out. 159 | 160 | ### For Styled Components 161 | 162 | ``` 163 | npm i styled-components 164 | npm i -D jest-styled-components @types/styled-components 165 | ``` 166 | 167 | Create a folder called styles in `src`. Add the files `render-theme.jsx`, 168 | `global-styles.jsx`, `theme.js` and a `styled-theme-provider.jsx`. 169 | 170 | The file `theme.js` is where you should add the theme for your application, for 171 | example (the most simple and ugly theme you would ever see): 172 | 173 | ```javascript 174 | export const theme = { 175 | colors: { 176 | primary: 'red', 177 | secondary: 'blue', 178 | }, 179 | }; 180 | ``` 181 | 182 | The `global-styles.jsx` is where we add the global theme for our application. 183 | Here we can add fonts, provide css reset and more. 184 | 185 | ```javascript 186 | import { createGlobalStyle } from 'styled-components'; 187 | 188 | export const GlobalStyles = createGlobalStyle` 189 | * { 190 | margin: 0; 191 | padding: 0; 192 | box-sizing: border-box; 193 | } 194 | 195 | html { 196 | font-size: 62.5%; 197 | } 198 | 199 | body { 200 | font-size: 1.6rem; 201 | } 202 | `; 203 | ``` 204 | 205 | The `styled-theme-provider.jsx` makes things easier by providing the theme to 206 | other components. 207 | 208 | ```javascript 209 | import { ThemeProvider } from 'styled-components'; 210 | import Proptypes from 'prop-types'; 211 | import { GlobalStyles } from './global-styles'; 212 | import { theme } from './theme'; 213 | 214 | export const StyledThemeProvider = ({ children }) => { 215 | return ( 216 | 217 | 218 | {children} 219 | 220 | ); 221 | }; 222 | 223 | StyledThemeProvider.propTypes = { 224 | children: Proptypes.node.isRequired, 225 | }; 226 | ``` 227 | 228 | The file `render-theme.jsx` is going to be used for tests. It will provide the 229 | `StyledThemeProvider` so we can use styled-components in our tests. 230 | 231 | This is the most basic version of the `renderTheme` function. 232 | 233 | ```javascript 234 | import { render } from '@testing-library/react'; 235 | import { StyledThemeProvider } from './styled-theme-provider'; 236 | 237 | export const renderTheme = (children) => { 238 | return render({children}); 239 | }; 240 | ``` 241 | 242 | And on the `main.jsx`, you may want to wrap everything using 243 | `StyledThemeProvider`. 244 | 245 | ```javascript 246 | import React from 'react'; 247 | import ReactDOM from 'react-dom/client'; 248 | import { StyledThemeProvider } from './styles/styled-theme-provider'; 249 | import App from './App'; 250 | import './index.css'; 251 | 252 | ReactDOM.createRoot(document.getElementById('root')).render( 253 | 254 | 255 | 256 | 257 | , 258 | ); 259 | ``` 260 | 261 | # Show markdown as HTML 262 | 263 | ``` 264 | npm install react-markdown 265 | ``` 266 | 267 | ```jsx 268 | import ReactMarkdown from 'react-markdown'; 269 | import { TextComponent } from '../TextComponent'; 270 | 271 | 272 | {html}, 273 | 274 | ``` 275 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | [ 5 | "@babel/preset-react", 6 | { 7 | "runtime": "automatic" 8 | } 9 | ] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vreact5", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build --emptyOutDir", 9 | "preview": "vite preview", 10 | "test": "vitest", 11 | "storybook": "start-storybook -p 6006", 12 | "build-storybook": "build-storybook" 13 | }, 14 | "dependencies": { 15 | "@styled-icons/material-outlined": "^10.34.0", 16 | "prop-types": "^15.8.1", 17 | "react": "^18.0.0", 18 | "react-dom": "^18.0.0", 19 | "react-markdown": "^8.0.3", 20 | "react-router-dom": "^6.3.0", 21 | "remark-gfm": "^3.0.1", 22 | "styled-components": "^5.3.5" 23 | }, 24 | "devDependencies": { 25 | "@babel/core": "^7.18.2", 26 | "@babel/eslint-parser": "^7.18.2", 27 | "@babel/preset-env": "^7.18.2", 28 | "@babel/preset-react": "^7.17.12", 29 | "@storybook/addon-actions": "^6.5.5", 30 | "@storybook/addon-essentials": "^6.5.5", 31 | "@storybook/addon-interactions": "^6.5.5", 32 | "@storybook/addon-links": "^6.5.5", 33 | "@storybook/builder-vite": "^0.1.35", 34 | "@storybook/react": "^6.5.5", 35 | "@storybook/testing-library": "^0.0.11", 36 | "@testing-library/jest-dom": "^5.16.4", 37 | "@testing-library/react": "^13.3.0", 38 | "@types/react": "^18.0.0", 39 | "@types/react-dom": "^18.0.0", 40 | "@types/styled-components": "^5.1.25", 41 | "@vitejs/plugin-react": "^1.3.0", 42 | "babel-loader": "^8.2.5", 43 | "eslint": "^8.16.0", 44 | "eslint-config-prettier": "^8.5.0", 45 | "eslint-plugin-prettier": "^4.0.0", 46 | "eslint-plugin-react": "^7.30.0", 47 | "eslint-plugin-react-hooks": "^4.5.0", 48 | "eslint-plugin-storybook": "^0.5.12", 49 | "jest-styled-components": "^7.0.8", 50 | "jsdom": "^19.0.0", 51 | "prettier": "^2.6.2", 52 | "vite": "^2.9.9", 53 | "vitest": "^0.12.9" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 2 | -------------------------------------------------------------------------------- /public/assets/images/javascript.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/api/dados.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "id": 2, 5 | "attributes": { 6 | "title": "Olha só a minha página", 7 | "slug": "olha-so-a-minha-pagina", 8 | "createdAt": "2022-05-07T12:25:04.694Z", 9 | "updatedAt": "2022-05-09T10:55:36.702Z", 10 | "publishedAt": "2022-05-07T12:31:53.293Z", 11 | "footer_text": "Feito com ❤ por Otávio Miranda", 12 | "sections": [ 13 | { 14 | "id": 1, 15 | "__component": "section.section-two-columns", 16 | "title": "JANUARY BRINGS US FIREFOX 85", 17 | "description": "To wrap up January, we are proud to bring you the release of Firefox 85. In this version we are bringing you support for the :focus-visible pseudo-class in CSS and associated devtools, and the complete removal of Flash support from Firefox.\n\n", 18 | "image": { 19 | "data": { 20 | "id": 4, 21 | "attributes": { 22 | "name": "javascript.svg", 23 | "alternativeText": "javascript.svg", 24 | "caption": "javascript.svg", 25 | "width": null, 26 | "height": null, 27 | "formats": null, 28 | "hash": "javascript_8c37407653", 29 | "ext": ".svg", 30 | "mime": "image/svg+xml", 31 | "size": 30.31, 32 | "url": "https://res.cloudinary.com/deosirvhi/image/upload/v1651926290/javascript_8c37407653.svg", 33 | "previewUrl": null, 34 | "provider": "cloudinary", 35 | "provider_metadata": { 36 | "public_id": "javascript_8c37407653", 37 | "resource_type": "image" 38 | }, 39 | "createdAt": "2022-05-07T12:24:52.179Z", 40 | "updatedAt": "2022-05-07T12:24:52.179Z" 41 | } 42 | } 43 | }, 44 | "metadata": { 45 | "id": 1, 46 | "name": "home", 47 | "section_id": "home", 48 | "background": true 49 | } 50 | }, 51 | { 52 | "id": 1, 53 | "__component": "section.section-content", 54 | "title": "NEWS COVERAGE AND SOME SURPRISES", 55 | "content": "The release of **Apple Silicon-based** Macs at the end of last year generated a flurry of news coverage and some surprises at the machine’s _performance_. This post details some background information on the experience of porting Firefox to run natively on these CPUs.\n\nWe’ll start with some background on the Mac transition and give an overview of Firefox internals that needed to know about the new architecture, before moving on to the concept of Universal Binaries.\n\nWe’ll then explain how DRM/EME works on the new platform, talk about our experience with macOS Big Sur, and discuss various updater problems we had to deal with. We’ll conclude with the release and an overview of various other improvements that are in the pipeline.\n\n", 56 | "metadata": { 57 | "id": 3, 58 | "name": "intro", 59 | "section_id": "intro", 60 | "background": false 61 | } 62 | }, 63 | { 64 | "id": 1, 65 | "__component": "section.section-grid", 66 | "title": "MY GRID", 67 | "description": "Uma breve descrição.\n\n", 68 | "text_grid": [ 69 | { 70 | "id": 1, 71 | "title": "Teste 1", 72 | "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores provident maiores.\n\n" 73 | }, 74 | { 75 | "id": 2, 76 | "title": "Teste 2", 77 | "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores provident maiores.\n\n" 78 | }, 79 | { 80 | "id": 3, 81 | "title": "Teste 3", 82 | "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores provident maiores.\n\n" 83 | } 84 | ], 85 | "image_grid": [], 86 | "metadata": { 87 | "id": 2, 88 | "name": "grid-one", 89 | "section_id": "grid-one", 90 | "background": true 91 | } 92 | } 93 | ], 94 | "menu": { 95 | "id": 1, 96 | "logo_link": "#home", 97 | "logo_text": "Logo", 98 | "logo": { 99 | "data": { 100 | "id": 5, 101 | "attributes": { 102 | "name": "landing-page-2.svg", 103 | "alternativeText": "Landing Page Logo", 104 | "caption": "Landing Page Logo", 105 | "width": null, 106 | "height": null, 107 | "formats": null, 108 | "hash": "landing_page_2_c6baa21725", 109 | "ext": ".svg", 110 | "mime": "image/svg+xml", 111 | "size": 2.51, 112 | "url": "https://res.cloudinary.com/deosirvhi/image/upload/v1652081175/landing_page_2_c6baa21725.svg", 113 | "previewUrl": null, 114 | "provider": "cloudinary", 115 | "provider_metadata": { 116 | "public_id": "landing_page_2_c6baa21725", 117 | "resource_type": "image" 118 | }, 119 | "createdAt": "2022-05-09T07:26:17.235Z", 120 | "updatedAt": "2022-05-09T07:26:17.235Z" 121 | } 122 | } 123 | }, 124 | "menu_links": [ 125 | { 126 | "id": 2, 127 | "link_text": "intro", 128 | "url": "#intro", 129 | "open_in_new_tab": false 130 | }, 131 | { 132 | "id": 1, 133 | "link_text": "grid-one", 134 | "url": "#grid-one", 135 | "open_in_new_tab": false 136 | } 137 | ] 138 | } 139 | } 140 | }, 141 | { 142 | "id": 4, 143 | "attributes": { 144 | "title": "Olha só a minha página 1", 145 | "slug": "olha-so-a-minha-pagina-1", 146 | "createdAt": "2022-05-09T11:10:30.466Z", 147 | "updatedAt": "2022-05-09T11:10:51.326Z", 148 | "publishedAt": "2022-05-09T11:10:51.177Z", 149 | "footer_text": "Feito com ❤ por Otávio Miranda", 150 | "sections": [ 151 | { 152 | "id": 3, 153 | "__component": "section.section-two-columns", 154 | "title": "JANUARY BRINGS US FIREFOX 85", 155 | "description": "To wrap up January, we are proud to bring you the release of Firefox 85. In this version we are bringing you support for the :focus-visible pseudo-class in CSS and associated devtools, and the complete removal of Flash support from Firefox.\n\n", 156 | "image": { 157 | "data": { 158 | "id": 4, 159 | "attributes": { 160 | "name": "javascript.svg", 161 | "alternativeText": "javascript.svg", 162 | "caption": "javascript.svg", 163 | "width": null, 164 | "height": null, 165 | "formats": null, 166 | "hash": "javascript_8c37407653", 167 | "ext": ".svg", 168 | "mime": "image/svg+xml", 169 | "size": 30.31, 170 | "url": "https://res.cloudinary.com/deosirvhi/image/upload/v1651926290/javascript_8c37407653.svg", 171 | "previewUrl": null, 172 | "provider": "cloudinary", 173 | "provider_metadata": { 174 | "public_id": "javascript_8c37407653", 175 | "resource_type": "image" 176 | }, 177 | "createdAt": "2022-05-07T12:24:52.179Z", 178 | "updatedAt": "2022-05-07T12:24:52.179Z" 179 | } 180 | } 181 | }, 182 | "metadata": { 183 | "id": 8, 184 | "name": "home", 185 | "section_id": "home", 186 | "background": true 187 | } 188 | }, 189 | { 190 | "id": 3, 191 | "__component": "section.section-content", 192 | "title": "NEWS COVERAGE AND SOME SURPRISES", 193 | "content": "The release of **Apple Silicon-based** Macs at the end of last year generated a flurry of news coverage and some surprises at the machine’s _performance_. This post details some background information on the experience of porting Firefox to run natively on these CPUs.\n\nWe’ll start with some background on the Mac transition and give an overview of Firefox internals that needed to know about the new architecture, before moving on to the concept of Universal Binaries.\n\nWe’ll then explain how DRM/EME works on the new platform, talk about our experience with macOS Big Sur, and discuss various updater problems we had to deal with. We’ll conclude with the release and an overview of various other improvements that are in the pipeline.\n\n", 194 | "metadata": { 195 | "id": 7, 196 | "name": "intro", 197 | "section_id": "intro", 198 | "background": false 199 | } 200 | }, 201 | { 202 | "id": 3, 203 | "__component": "section.section-grid", 204 | "title": "MY GRID", 205 | "description": "Uma breve descrição.\n\n", 206 | "text_grid": [ 207 | { 208 | "id": 8, 209 | "title": "Teste 1", 210 | "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores provident maiores.\n\n" 211 | }, 212 | { 213 | "id": 9, 214 | "title": "Teste 2", 215 | "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores provident maiores.\n\n" 216 | }, 217 | { 218 | "id": 7, 219 | "title": "Teste 3", 220 | "description": "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Debitis cum delectus molestias. Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde, reprehenderit minus ad dolores provident maiores.\n\n" 221 | } 222 | ], 223 | "image_grid": [], 224 | "metadata": { 225 | "id": 9, 226 | "name": "grid-one", 227 | "section_id": "grid-one", 228 | "background": true 229 | } 230 | } 231 | ], 232 | "menu": { 233 | "id": 2, 234 | "logo_link": "#home", 235 | "logo_text": "Logo", 236 | "logo": { 237 | "data": { 238 | "id": 5, 239 | "attributes": { 240 | "name": "landing-page-2.svg", 241 | "alternativeText": "Landing Page Logo", 242 | "caption": "Landing Page Logo", 243 | "width": null, 244 | "height": null, 245 | "formats": null, 246 | "hash": "landing_page_2_c6baa21725", 247 | "ext": ".svg", 248 | "mime": "image/svg+xml", 249 | "size": 2.51, 250 | "url": "https://res.cloudinary.com/deosirvhi/image/upload/v1652081175/landing_page_2_c6baa21725.svg", 251 | "previewUrl": null, 252 | "provider": "cloudinary", 253 | "provider_metadata": { 254 | "public_id": "landing_page_2_c6baa21725", 255 | "resource_type": "image" 256 | }, 257 | "createdAt": "2022-05-09T07:26:17.235Z", 258 | "updatedAt": "2022-05-09T07:26:17.235Z" 259 | } 260 | } 261 | }, 262 | "menu_links": [ 263 | { 264 | "id": 4, 265 | "link_text": "intro", 266 | "url": "#intro", 267 | "open_in_new_tab": false 268 | }, 269 | { 270 | "id": 3, 271 | "link_text": "grid-one", 272 | "url": "#grid-one", 273 | "open_in_new_tab": false 274 | } 275 | ] 276 | } 277 | } 278 | }, 279 | { 280 | "id": 5, 281 | "attributes": { 282 | "title": "Olha só a minha página 2", 283 | "slug": "olha-so-a-minha-pagina-2", 284 | "createdAt": "2022-05-29T14:29:50.208Z", 285 | "updatedAt": "2022-05-29T14:30:00.984Z", 286 | "publishedAt": "2022-05-29T14:30:00.638Z", 287 | "footer_text": "Feito com ❤ por Otávio Miranda", 288 | "sections": [ 289 | { 290 | "id": 4, 291 | "__component": "section.section-two-columns", 292 | "title": "JANUARY BRINGS US FIREFOX 85", 293 | "description": "To wrap up January, we are proud to bring you the release of Firefox 85. In this version we are bringing you support for the :focus-visible pseudo-class in CSS and associated devtools, and the complete removal of Flash support from Firefox.\n\n", 294 | "image": { 295 | "data": { 296 | "id": 4, 297 | "attributes": { 298 | "name": "javascript.svg", 299 | "alternativeText": "javascript.svg", 300 | "caption": "javascript.svg", 301 | "width": null, 302 | "height": null, 303 | "formats": null, 304 | "hash": "javascript_8c37407653", 305 | "ext": ".svg", 306 | "mime": "image/svg+xml", 307 | "size": 30.31, 308 | "url": "https://res.cloudinary.com/deosirvhi/image/upload/v1651926290/javascript_8c37407653.svg", 309 | "previewUrl": null, 310 | "provider": "cloudinary", 311 | "provider_metadata": { 312 | "public_id": "javascript_8c37407653", 313 | "resource_type": "image" 314 | }, 315 | "createdAt": "2022-05-07T12:24:52.179Z", 316 | "updatedAt": "2022-05-07T12:24:52.179Z" 317 | } 318 | } 319 | }, 320 | "metadata": { 321 | "id": 11, 322 | "name": "home", 323 | "section_id": "home", 324 | "background": true 325 | } 326 | }, 327 | { 328 | "id": 4, 329 | "__component": "section.section-content", 330 | "title": "NEWS COVERAGE AND SOME SURPRISES", 331 | "content": "The release of **Apple Silicon-based** Macs at the end of last year generated a flurry of news coverage and some surprises at the machine’s _performance_. This post details some background information on the experience of porting Firefox to run natively on these CPUs.\n\nWe’ll start with some background on the Mac transition and give an overview of Firefox internals that needed to know about the new architecture, before moving on to the concept of Universal Binaries.\n\nWe’ll then explain how DRM/EME works on the new platform, talk about our experience with macOS Big Sur, and discuss various updater problems we had to deal with. We’ll conclude with the release and an overview of various other improvements that are in the pipeline.\n\n", 332 | "metadata": { 333 | "id": 10, 334 | "name": "intro", 335 | "section_id": "intro", 336 | "background": false 337 | } 338 | }, 339 | { 340 | "id": 4, 341 | "__component": "section.section-grid", 342 | "title": "MY GRID", 343 | "description": "Uma breve descrição.\n\n", 344 | "text_grid": [], 345 | "image_grid": [ 346 | { 347 | "id": 1 348 | }, 349 | { 350 | "id": 2 351 | } 352 | ], 353 | "metadata": { 354 | "id": 12, 355 | "name": "grid-one", 356 | "section_id": "grid-one", 357 | "background": true 358 | } 359 | } 360 | ], 361 | "menu": { 362 | "id": 3, 363 | "logo_link": "#home", 364 | "logo_text": "Logo", 365 | "logo": { 366 | "data": { 367 | "id": 5, 368 | "attributes": { 369 | "name": "landing-page-2.svg", 370 | "alternativeText": "Landing Page Logo", 371 | "caption": "Landing Page Logo", 372 | "width": null, 373 | "height": null, 374 | "formats": null, 375 | "hash": "landing_page_2_c6baa21725", 376 | "ext": ".svg", 377 | "mime": "image/svg+xml", 378 | "size": 2.51, 379 | "url": "https://res.cloudinary.com/deosirvhi/image/upload/v1652081175/landing_page_2_c6baa21725.svg", 380 | "previewUrl": null, 381 | "provider": "cloudinary", 382 | "provider_metadata": { 383 | "public_id": "landing_page_2_c6baa21725", 384 | "resource_type": "image" 385 | }, 386 | "createdAt": "2022-05-09T07:26:17.235Z", 387 | "updatedAt": "2022-05-09T07:26:17.235Z" 388 | } 389 | } 390 | }, 391 | "menu_links": [ 392 | { 393 | "id": 5, 394 | "link_text": "intro", 395 | "url": "#intro", 396 | "open_in_new_tab": false 397 | }, 398 | { 399 | "id": 6, 400 | "link_text": "grid-one", 401 | "url": "#grid-one", 402 | "open_in_new_tab": false 403 | } 404 | ] 405 | } 406 | } 407 | } 408 | ], 409 | "meta": { 410 | "pagination": { 411 | "page": 1, 412 | "pageSize": 25, 413 | "pageCount": 1, 414 | "total": 3 415 | } 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /src/api/map-data.js: -------------------------------------------------------------------------------- 1 | import { mapMenu } from './map-menu'; 2 | import { mapSections } from './map-sections'; 3 | 4 | export const mapData = (pagesData = [{}]) => { 5 | return pagesData.map((data) => { 6 | const { 7 | footer_text: footerHtml = '', 8 | slug = '', 9 | title = '', 10 | sections = [], 11 | menu = {}, 12 | } = data; 13 | 14 | return { 15 | footerHtml, 16 | slug, 17 | title, 18 | sections: mapSections(sections), 19 | menu: mapMenu(menu), 20 | }; 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /src/api/map-data.test.js: -------------------------------------------------------------------------------- 1 | import { mapData } from './map-data'; 2 | 3 | describe('map-data', () => { 4 | it('should map data even if there is no data', () => { 5 | const pagesData = mapData()[0]; 6 | expect(pagesData.footerHtml).toBe(''); 7 | expect(pagesData.slug).toBe(''); 8 | expect(pagesData.title).toBe(''); 9 | }); 10 | 11 | it('should map data if there are data', () => { 12 | const pagesData = mapData([ 13 | { 14 | footer_text: '

Hey

', 15 | slug: 'slug', 16 | title: 'title', 17 | }, 18 | ])[0]; 19 | expect(pagesData.footerHtml).toBe('

Hey

'); 20 | expect(pagesData.slug).toBe('slug'); 21 | expect(pagesData.title).toBe('title'); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/api/map-menu.js: -------------------------------------------------------------------------------- 1 | export const mapMenu = (menu = {}) => { 2 | const { 3 | open_in_new_tab: newTab = false, 4 | logo_text: text = '', 5 | logo_link: link = '', 6 | } = menu; 7 | 8 | const links = menu.links || menu.menu_links || menu.menu || []; 9 | const srcImg = menu?.logo?.data?.attributes?.url || ''; 10 | 11 | return { 12 | newTab, 13 | text, 14 | link, 15 | srcImg, 16 | links: mapMenuLinks(links), 17 | }; 18 | }; 19 | 20 | export const mapMenuLinks = (links = []) => { 21 | return links.map((item) => { 22 | const { 23 | open_in_new_tab: newTab = false, 24 | link_text: children = '', 25 | url: link = '', 26 | } = item; 27 | 28 | return { 29 | newTab, 30 | children, 31 | link, 32 | }; 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /src/api/map-menu.test.js: -------------------------------------------------------------------------------- 1 | import { mapMenu, mapMenuLinks } from './map-menu'; 2 | 3 | describe('map-menu', () => { 4 | it('should return a predefined object if no data', () => { 5 | const menu = mapMenu(); 6 | expect(menu.newTab).toBe(false); 7 | expect(menu.text).toBe(''); 8 | expect(menu.srcImg).toBe(''); 9 | expect(menu.link).toBe(''); 10 | }); 11 | 12 | it('should map menu to match keys and values required', () => { 13 | const menu = mapMenu({ 14 | open_in_new_tab: false, 15 | logo_text: 'Landing Page', 16 | logo_link: '#home', 17 | menu: [ 18 | { 19 | open_in_new_tab: false, 20 | link_text: 'pricing', 21 | url: '#pricing', 22 | }, 23 | { 24 | open_in_new_tab: false, 25 | link_text: 'contact', 26 | url: '#contact', 27 | }, 28 | ], 29 | logo: { 30 | data: { 31 | attributes: { 32 | url: 'a.svg', 33 | }, 34 | }, 35 | }, 36 | }); 37 | expect(menu.newTab).toBe(false); 38 | expect(menu.text).toBe('Landing Page'); 39 | expect(menu.srcImg).toBe('a.svg'); 40 | expect(menu.link).toBe('#home'); 41 | expect(menu.links[0].newTab).toBe(false); 42 | expect(menu.links[0].children).toBe('pricing'); 43 | expect(menu.links[0].link).toBe('#pricing'); 44 | }); 45 | 46 | it('should return an empty array if no links', () => { 47 | const links = mapMenuLinks(); 48 | expect(links).toEqual([]); 49 | }); 50 | 51 | it('should map links if links passed', () => { 52 | const links = mapMenuLinks([ 53 | { 54 | open_in_new_tab: false, 55 | link_text: 'pricing', 56 | url: '#pricing', 57 | }, 58 | {}, 59 | ]); 60 | expect(links[0].newTab).toBe(false); 61 | expect(links[0].children).toBe('pricing'); 62 | expect(links[0].link).toBe('#pricing'); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /src/api/map-sections.js: -------------------------------------------------------------------------------- 1 | export const mapSections = (sections = []) => { 2 | return sections.map((section) => { 3 | if (section.__component === 'section.section-two-columns') { 4 | return mapSectionTwoColumns(section); 5 | } 6 | if (section.__component === 'section.section-content') { 7 | return mapSectionContent(section); 8 | } 9 | if (section.__component === 'section.section-grid') { 10 | const { text_grid = [], image_grid = [] } = section; 11 | 12 | if (text_grid.length > 0) { 13 | return mapTextGrid(section); 14 | } 15 | 16 | if (image_grid.length > 0) { 17 | return mapImageGrid(section); 18 | } 19 | } 20 | 21 | return section; 22 | }); 23 | }; 24 | 25 | export const mapSectionTwoColumns = (section = {}) => { 26 | const { 27 | __component: component = '', 28 | title = '', 29 | description: text = '', 30 | metadata: { background = false, section_id: sectionId = '' } = false, 31 | } = section; 32 | 33 | const srcImg = section?.image?.data?.attributes?.url || ''; 34 | 35 | return { 36 | component, 37 | title, 38 | text, 39 | srcImg, 40 | background, 41 | sectionId, 42 | }; 43 | }; 44 | 45 | export const mapSectionContent = (section = {}) => { 46 | const { 47 | __component: component = '', 48 | title = '', 49 | content: html = '', 50 | metadata: { background = false, section_id: sectionId = '' } = false, 51 | } = section; 52 | 53 | return { 54 | component, 55 | title, 56 | background, 57 | sectionId, 58 | html, 59 | }; 60 | }; 61 | 62 | export const mapTextGrid = (section = {}) => { 63 | const { 64 | title = '', 65 | description = '', 66 | metadata: { background = false, section_id: sectionId = '' } = false, 67 | text_grid: grid = [], 68 | } = section; 69 | 70 | return { 71 | component: 'section.section-grid-text', 72 | title, 73 | background, 74 | sectionId, 75 | description, 76 | grid: grid.map((text) => { 77 | const { title = '', description = '' } = text; 78 | return { 79 | title, 80 | description, 81 | }; 82 | }), 83 | }; 84 | }; 85 | 86 | export const mapImageGrid = (section = {}) => { 87 | const { 88 | title = '', 89 | description = '', 90 | metadata: { background = false, section_id: sectionId = '' } = false, 91 | image_grid: grid = [], 92 | } = section; 93 | 94 | return { 95 | component: 'section.section-grid-image', 96 | title, 97 | background, 98 | sectionId, 99 | description, 100 | grid: grid.map((img) => { 101 | const { 102 | image: { url: srcImg = '', alternativeText: altText = '' } = '', 103 | } = img; 104 | return { 105 | srcImg, 106 | altText, 107 | }; 108 | }), 109 | }; 110 | }; 111 | -------------------------------------------------------------------------------- /src/api/map-sections.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | mapImageGrid, 3 | mapSectionContent, 4 | mapSections, 5 | mapSectionTwoColumns, 6 | mapTextGrid, 7 | } from './map-sections'; 8 | 9 | import pagesFakeData from './dados.json'; 10 | 11 | describe('map-sections', () => { 12 | it('should render predefined section if no data', () => { 13 | const data = mapSections(); 14 | expect(data).toEqual([]); 15 | }); 16 | 17 | it('should render sections with correct data', () => { 18 | const data = mapSections(pagesFakeData.data[0].attributes.sections); 19 | expect(data[0].component).toBe('section.section-two-columns'); 20 | }); 21 | 22 | it('should test section with invalid data', () => { 23 | const withNoTextOrImageGrid = mapSections([ 24 | { 25 | __component: 'section.section-grid', 26 | }, 27 | ]); 28 | 29 | const WithNoComponent = mapSections([{}]); 30 | expect(withNoTextOrImageGrid).toEqual([ 31 | { __component: 'section.section-grid' }, 32 | ]); 33 | expect(WithNoComponent).toEqual([{}]); 34 | }); 35 | 36 | it('should test section.section-grid with no text_grid or image_grid', () => { 37 | const withNoTextOrImageGrid = mapSections([ 38 | { 39 | __component: 'section.section-grid', 40 | image_grid: [{}], 41 | }, 42 | { 43 | __component: 'section.section-grid', 44 | text_grid: [{}], 45 | }, 46 | ]); 47 | expect(withNoTextOrImageGrid.length).toBe(2); 48 | }); 49 | 50 | it('should map section two columns if data is empty', () => { 51 | const data = mapSectionTwoColumns(); 52 | expect(data.background).toBe(false); 53 | expect(data.component).toBe(''); 54 | expect(data.sectionId).toBe(''); 55 | expect(data.srcImg).toBe(''); 56 | expect(data.text).toBe(''); 57 | expect(data.title).toBe(''); 58 | }); 59 | 60 | it('should map section two columns with data', () => { 61 | const data = mapSectionTwoColumns({ 62 | __component: 'section.section-two-columns', 63 | title: 'title', 64 | description: 'abc', 65 | metadata: { 66 | background: true, 67 | section_id: 'contact', 68 | }, 69 | image: { 70 | data: { 71 | attributes: { 72 | url: 'a.svg', 73 | }, 74 | }, 75 | }, 76 | }); 77 | expect(data.background).toBe(true); 78 | expect(data.component).toBe('section.section-two-columns'); 79 | expect(data.sectionId).toBe('contact'); 80 | expect(data.srcImg).toBe('a.svg'); 81 | expect(data.text).toBe('abc'); 82 | expect(data.title).toBe('title'); 83 | }); 84 | 85 | it('should map section content with no data', () => { 86 | const data = mapSectionContent(); 87 | expect(data.background).toBe(false); 88 | expect(data.component).toBe(''); 89 | expect(data.sectionId).toBe(''); 90 | expect(data.title).toBe(''); 91 | expect(data.html).toBe(''); 92 | }); 93 | 94 | it('should map section content', () => { 95 | const data = mapSectionContent({ 96 | __component: 'section.section-content', 97 | title: 'Pricing', 98 | content: 'abc', 99 | metadata: { 100 | background: false, 101 | section_id: 'pricing', 102 | }, 103 | }); 104 | expect(data.background).toBe(false); 105 | expect(data.component).toBe('section.section-content'); 106 | expect(data.sectionId).toBe('pricing'); 107 | expect(data.title).toBe('Pricing'); 108 | expect(data.html).toBe('abc'); 109 | }); 110 | 111 | it('should map grid text with data', () => { 112 | const data = mapTextGrid({ 113 | __component: 'section.section-grid', 114 | description: 'abc', 115 | title: 'My Grid', 116 | text_grid: [ 117 | { 118 | title: 'Teste 1', 119 | description: 'Coisa', 120 | }, 121 | { 122 | title: 'Teste 2', 123 | description: 'abc', 124 | }, 125 | ], 126 | image_grid: [], 127 | metadata: { 128 | background: true, 129 | section_id: 'grid-one', 130 | }, 131 | }); 132 | expect(data.background).toBe(true); 133 | expect(data.component).toBe('section.section-grid-text'); 134 | expect(data.sectionId).toBe('grid-one'); 135 | expect(data.title).toBe('My Grid'); 136 | expect(data.description).toBe('abc'); 137 | expect(data.grid[0].title).toBe('Teste 1'); 138 | expect(data.grid[0].description).toBe('Coisa'); 139 | }); 140 | 141 | it('should map grid text without data', () => { 142 | const data = mapTextGrid(undefined); 143 | expect(data.background).toBe(false); 144 | expect(data.component).toBe('section.section-grid-text'); 145 | expect(data.sectionId).toBe(''); 146 | expect(data.title).toBe(''); 147 | expect(data.description).toBe(''); 148 | }); 149 | 150 | it('should map grid image without data', () => { 151 | const data = mapImageGrid(undefined); 152 | expect(data.background).toBe(false); 153 | expect(data.component).toBe('section.section-grid-image'); 154 | expect(data.sectionId).toBe(''); 155 | expect(data.title).toBe(''); 156 | expect(data.description).toBe(''); 157 | }); 158 | 159 | it('should map grid image with data', () => { 160 | const data = mapImageGrid({ 161 | __component: 'section.section-grid', 162 | description: 'abc', 163 | title: 'Gallery', 164 | text_grid: [], 165 | image_grid: [ 166 | { 167 | image: { 168 | alternativeText: 'abc', 169 | url: 'a.svg', 170 | }, 171 | }, 172 | ], 173 | metadata: { 174 | background: false, 175 | name: 'gallery', 176 | section_id: 'gallery', 177 | }, 178 | }); 179 | expect(data.background).toBe(false); 180 | expect(data.component).toBe('section.section-grid-image'); 181 | expect(data.sectionId).toBe('gallery'); 182 | expect(data.title).toBe('Gallery'); 183 | expect(data.description).toBe('abc'); 184 | expect(data.grid[0].srcImg).toBe('a.svg'); 185 | expect(data.grid[0].altText).toBe('abc'); 186 | }); 187 | }); 188 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.spec.jsx: -------------------------------------------------------------------------------- 1 | import { screen } from '@testing-library/react'; 2 | import { renderTheme } from '../../styles/render-theme'; 3 | import { Footer } from '.'; 4 | 5 | describe('