├── .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('', () => {
6 | it('should render', () => {
7 | const { container } = renderTheme();
8 | expect(screen.getByRole('heading', { name: 'Olá' })).toBeInTheDocument();
9 | expect(container).toMatchInlineSnapshot(`
10 | .c0 {
11 | text-align: center;
12 | }
13 |
14 | .c0 a {
15 | color: inherit;
16 | -webkit-text-decoration: none;
17 | text-decoration: none;
18 | font-size: 1.6rem;
19 | }
20 |
21 | .c2 {
22 | font-size: 2.4rem;
23 | }
24 |
25 | .c1 {
26 | max-width: 120rem;
27 | margin: 0 auto;
28 | padding: 3.2rem;
29 | }
30 |
31 |
32 |
35 |
38 |
41 |
42 | Olá
43 |
44 |
45 |
46 |
47 |
48 | `);
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/src/components/Footer/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 | import { TextComponent } from '../TextComponent';
4 | import { SectionContainer } from '../SectionContainer';
5 |
6 | export const Footer = ({ footerHtml }) => {
7 | return (
8 |
9 |
10 | {footerHtml}
11 |
12 |
13 | );
14 | };
15 |
16 | Footer.propTypes = {
17 | footerHtml: P.string.isRequired,
18 | };
19 |
--------------------------------------------------------------------------------
/src/components/Footer/stories.jsx:
--------------------------------------------------------------------------------
1 | import { Footer } from '.';
2 |
3 | export default {
4 | title: 'Footer',
5 | component: Footer,
6 | args: {
7 | footerHtml: `Feito com ❤ por Otávio Miranda
`,
8 | },
9 | };
10 |
11 | export const Template = (args) => {
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/src/components/Footer/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.footer`
4 | ${({ theme }) => css`
5 | text-align: center;
6 |
7 | a {
8 | color: inherit;
9 | text-decoration: none;
10 | font-size: ${theme.font.sizes.small};
11 | }
12 | `}
13 | `;
14 |
--------------------------------------------------------------------------------
/src/components/GoTop/GoTop.spec.jsx:
--------------------------------------------------------------------------------
1 | import { fireEvent, screen } from '@testing-library/react';
2 | import { renderTheme } from '../../styles/render-theme';
3 | import { GoTop } from '.';
4 | import { expect, describe, it, vi } from 'vitest';
5 |
6 | // global.scrollTo = vi.fn();
7 |
8 | describe(' ', () => {
9 | it('should render a go to top button', () => {
10 | const { container } = renderTheme( );
11 | expect(screen.getByRole('link', { name: 'Go to top' })).toBeInTheDocument();
12 | expect(screen.getByRole('link', { name: 'Go to top' })).toHaveAttribute(
13 | 'href',
14 | '#',
15 | );
16 | expect(container).toMatchSnapshot();
17 | });
18 |
19 | it('should render a go to top button', () => {
20 | const spy = vi.fn();
21 | renderTheme( );
22 | const goTop = screen.getByRole('link', { name: 'Go to top' });
23 | fireEvent.click(goTop);
24 | expect(spy).toHaveBeenCalledTimes(1);
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/src/components/GoTop/__snapshots__/GoTop.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should render a go to top button 1`] = `
4 | .c0 {
5 | position: fixed;
6 | background: #0A1128;
7 | color: #FFFFFF;
8 | display: -webkit-box;
9 | display: -webkit-flex;
10 | display: -ms-flexbox;
11 | display: flex;
12 | -webkit-align-items: center;
13 | -webkit-box-align: center;
14 | -ms-flex-align: center;
15 | align-items: center;
16 | -webkit-box-pack: center;
17 | -webkit-justify-content: center;
18 | -ms-flex-pack: center;
19 | justify-content: center;
20 | width: 4rem;
21 | height: 4rem;
22 | bottom: 2rem;
23 | right: 2rem;
24 | z-index: 6;
25 | }
26 |
27 | .c1 {
28 | display: inline-block;
29 | vertical-align: middle;
30 | overflow: hidden;
31 | }
32 |
33 |
58 | `;
59 |
--------------------------------------------------------------------------------
/src/components/GoTop/index.jsx:
--------------------------------------------------------------------------------
1 | import * as Styled from './styles';
2 | import { KeyboardArrowUp } from '@styled-icons/material-outlined/KeyboardArrowUp';
3 | import PropTypes from 'prop-types';
4 |
5 | export const GoTop = ({ handleClick }) => {
6 | return (
7 |
13 |
14 |
15 | );
16 | };
17 |
18 | GoTop.propTypes = {
19 | handleClick: PropTypes.func,
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/GoTop/stories.jsx:
--------------------------------------------------------------------------------
1 | import { GoTop } from '.';
2 |
3 | export default {
4 | title: 'GoTop',
5 | component: GoTop,
6 | args: {
7 | children: 'GoTop',
8 | },
9 | argTypes: {
10 | children: { type: 'string' },
11 | },
12 | };
13 |
14 | export const Template = (args) => {
15 | return (
16 |
17 |
Lorem ipsum dolor sit, amet
18 |
19 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Animi
20 | doloremque qui eum maxime magni omnis sit, aliquam soluta distinctio nam
21 | dignissimos praesentium ut sunt porro incidunt molestias libero ab
22 | consectetur.
23 |
24 |
Lorem ipsum dolor sit, amet
25 |
26 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Animi
27 | doloremque qui eum maxime magni omnis sit, aliquam soluta distinctio nam
28 | dignissimos praesentium ut sunt porro incidunt molestias libero ab
29 | consectetur.
30 |
31 |
Lorem ipsum dolor sit, amet
32 |
33 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Animi
34 | doloremque qui eum maxime magni omnis sit, aliquam soluta distinctio nam
35 | dignissimos praesentium ut sunt porro incidunt molestias libero ab
36 | consectetur.
37 |
38 |
Lorem ipsum dolor sit, amet
39 |
40 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Animi
41 | doloremque qui eum maxime magni omnis sit, aliquam soluta distinctio nam
42 | dignissimos praesentium ut sunt porro incidunt molestias libero ab
43 | consectetur.
44 |
45 |
Lorem ipsum dolor sit, amet
46 |
47 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Animi
48 | doloremque qui eum maxime magni omnis sit, aliquam soluta distinctio nam
49 | dignissimos praesentium ut sunt porro incidunt molestias libero ab
50 | consectetur.
51 |
52 |
Lorem ipsum dolor sit, amet
53 |
54 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Animi
55 | doloremque qui eum maxime magni omnis sit, aliquam soluta distinctio nam
56 | dignissimos praesentium ut sunt porro incidunt molestias libero ab
57 | consectetur.
58 |
59 |
Lorem ipsum dolor sit, amet
60 |
61 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Animi
62 | doloremque qui eum maxime magni omnis sit, aliquam soluta distinctio nam
63 | dignissimos praesentium ut sunt porro incidunt molestias libero ab
64 | consectetur.
65 |
66 |
67 |
68 | );
69 | };
70 |
--------------------------------------------------------------------------------
/src/components/GoTop/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.a`
4 | ${({ theme }) => css`
5 | position: fixed;
6 | background: ${theme.colors.primaryColor};
7 | color: ${theme.colors.white};
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | width: 4rem;
12 | height: 4rem;
13 | bottom: 2rem;
14 | right: 2rem;
15 | z-index: 6;
16 | `}
17 | `;
18 |
--------------------------------------------------------------------------------
/src/components/GridContent/GridContent.spec.jsx:
--------------------------------------------------------------------------------
1 | import { renderTheme } from '../../styles/render-theme';
2 | import { GridContent } from '.';
3 |
4 | import mock from './mock';
5 |
6 | describe(' ', () => {
7 | it('should render grid content', () => {
8 | const { container } = renderTheme( );
9 | expect(container).toMatchSnapshot();
10 | });
11 |
12 | it('should render grid content', () => {
13 | const { container } = renderTheme(
14 | ,
15 | );
16 | expect(container).toMatchSnapshot();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/components/GridContent/__snapshots__/GridContent.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should render grid content 1`] = `
4 | .c3 {
5 | color: #0A1128;
6 | font-size: 6.4rem;
7 | text-transform: uppercase;
8 | }
9 |
10 | .c1 {
11 | max-width: 120rem;
12 | margin: 0 auto;
13 | padding: 3.2rem;
14 | }
15 |
16 | .c0 {
17 | background: #FFFFFF;
18 | color: #0A1128;
19 | min-height: 100vh;
20 | display: -webkit-box;
21 | display: -webkit-flex;
22 | display: -ms-flexbox;
23 | display: flex;
24 | -webkit-align-items: center;
25 | -webkit-box-align: center;
26 | -ms-flex-align: center;
27 | align-items: center;
28 | }
29 |
30 | .c5 {
31 | font-size: 2.4rem;
32 | }
33 |
34 | .c2 {
35 | text-align: center;
36 | max-width: 58rem;
37 | margin: 0 auto;
38 | }
39 |
40 | .c4 {
41 | margin: 6.4rem 0;
42 | }
43 |
44 | @media (max-width:768px) {
45 | .c3 {
46 | font-size: 4.0rem;
47 | }
48 | }
49 |
50 |
51 |
55 |
58 |
61 |
64 | O título que eu quero
65 |
66 |
69 |
72 | <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
73 |
74 |
75 | <h2>Header Level 2</h2>
76 |
77 |
78 | <ol>
79 | <li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
80 | <li>Aliquam tincidunt mauris eu risus.</li>
81 | </ol>
82 |
83 |
84 | <blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote>
85 |
86 |
87 | <h3>Header Level 3</h3>
88 |
89 |
90 | <ul>
91 | <li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
92 | <li>Aliquam tincidunt mauris eu risus.</li>
93 | </ul>
94 |
95 |
96 | <pre><code>
97 | #header h1 a {
98 | display: block;
99 | width: 300px;
100 | height: 80px;
101 | }
102 | </code></pre>
103 |
104 |
105 |
106 |
107 |
108 |
109 | `;
110 |
111 | exports[` > should render grid content 2`] = `
112 | .c3 {
113 | color: #0A1128;
114 | font-size: 6.4rem;
115 | text-transform: uppercase;
116 | }
117 |
118 | .c1 {
119 | max-width: 120rem;
120 | margin: 0 auto;
121 | padding: 3.2rem;
122 | }
123 |
124 | .c0 {
125 | background: #FFFFFF;
126 | color: #0A1128;
127 | min-height: 100vh;
128 | display: -webkit-box;
129 | display: -webkit-flex;
130 | display: -ms-flexbox;
131 | display: flex;
132 | -webkit-align-items: center;
133 | -webkit-box-align: center;
134 | -ms-flex-align: center;
135 | align-items: center;
136 | }
137 |
138 | .c5 {
139 | font-size: 2.4rem;
140 | }
141 |
142 | .c2 {
143 | text-align: center;
144 | max-width: 58rem;
145 | margin: 0 auto;
146 | }
147 |
148 | .c4 {
149 | margin: 6.4rem 0;
150 | }
151 |
152 | @media (max-width:768px) {
153 | .c3 {
154 | font-size: 4.0rem;
155 | }
156 | }
157 |
158 |
159 |
163 |
166 |
169 |
172 | O título que eu quero
173 |
174 |
177 |
180 | <p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href="#">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p>
181 |
182 |
183 | <h2>Header Level 2</h2>
184 |
185 |
186 | <ol>
187 | <li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
188 | <li>Aliquam tincidunt mauris eu risus.</li>
189 | </ol>
190 |
191 |
192 | <blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote>
193 |
194 |
195 | <h3>Header Level 3</h3>
196 |
197 |
198 | <ul>
199 | <li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li>
200 | <li>Aliquam tincidunt mauris eu risus.</li>
201 | </ul>
202 |
203 |
204 | <pre><code>
205 | #header h1 a {
206 | display: block;
207 | width: 300px;
208 | height: 80px;
209 | }
210 | </code></pre>
211 |
212 |
213 |
214 |
215 |
216 |
217 | `;
218 |
--------------------------------------------------------------------------------
/src/components/GridContent/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import { Heading } from '../Heading';
3 | import { SectionBackground } from '../SectionBackground';
4 | import { TextComponent } from '../TextComponent';
5 | import * as Styled from './styles';
6 |
7 | export const GridContent = ({
8 | title,
9 | html,
10 | background = false,
11 | sectionId = '',
12 | }) => {
13 | return (
14 |
15 |
16 |
17 | {title}
18 |
19 |
20 | {html}
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | GridContent.propTypes = {
28 | title: P.string.isRequired,
29 | html: P.string.isRequired,
30 | background: P.bool,
31 | sectionId: P.string,
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/GridContent/mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | title: 'O título que eu quero',
3 | html: `Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae
, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis.
4 |
5 | Header Level 2
6 |
7 |
8 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
9 | Aliquam tincidunt mauris eu risus.
10 |
11 |
12 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.
13 |
14 | Header Level 3
15 |
16 |
17 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
18 | Aliquam tincidunt mauris eu risus.
19 |
20 |
21 |
22 | #header h1 a {
23 | display: block;
24 | width: 300px;
25 | height: 80px;
26 | }
27 |
`,
28 | background: false,
29 | };
30 |
--------------------------------------------------------------------------------
/src/components/GridContent/stories.jsx:
--------------------------------------------------------------------------------
1 | import { GridContent } from '.';
2 |
3 | import mock from './mock';
4 |
5 | export default {
6 | title: 'GridContent',
7 | component: GridContent,
8 | args: mock,
9 | };
10 |
11 | export const Template = (args) => {
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/src/components/GridContent/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | ${() => css`
5 | text-align: center;
6 | max-width: 58rem;
7 | margin: 0 auto;
8 | `}
9 | `;
10 |
11 | export const Html = styled.div`
12 | ${({ theme }) => css`
13 | margin: ${theme.spacings.xhuge} 0;
14 | `}
15 | `;
16 |
--------------------------------------------------------------------------------
/src/components/GridImage/GridImage.test.jsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 | import { renderTheme } from '../../styles/render-theme';
3 | import { GridImage } from '.';
4 |
5 | import mock from './mock';
6 |
7 | describe(' ', () => {
8 | it('should render with background', () => {
9 | const { container } = renderTheme( );
10 | expect(container).toMatchSnapshot();
11 | });
12 |
13 | it('should render without background', () => {
14 | const { container } = renderTheme(
15 | ,
16 | );
17 | expect(container).toMatchSnapshot();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/src/components/GridImage/__snapshots__/GridImage.test.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should render with background 1`] = `
4 | .c3 {
5 | color: #0A1128;
6 | font-size: 6.4rem;
7 | text-transform: uppercase;
8 | }
9 |
10 | .c1 {
11 | max-width: 120rem;
12 | margin: 0 auto;
13 | padding: 3.2rem;
14 | }
15 |
16 | .c0 {
17 | background: #FFFFFF;
18 | color: #0A1128;
19 | min-height: 100vh;
20 | display: -webkit-box;
21 | display: -webkit-flex;
22 | display: -ms-flexbox;
23 | display: flex;
24 | -webkit-align-items: center;
25 | -webkit-box-align: center;
26 | -ms-flex-align: center;
27 | align-items: center;
28 | }
29 |
30 | .c5 {
31 | font-size: 2.4rem;
32 | }
33 |
34 | .c2 > .c4 {
35 | margin-bottom: 6.4rem;
36 | }
37 |
38 | .c6 {
39 | display: grid;
40 | grid-template-columns: repeat(auto-fill,minmax(280px,1fr));
41 | gap: 3.2rem;
42 | }
43 |
44 | .c7 {
45 | overflow: hidden;
46 | }
47 |
48 | .c8 {
49 | width: 100%;
50 | -webkit-transition: all 300ms ease-in-out;
51 | transition: all 300ms ease-in-out;
52 | }
53 |
54 | .c8:hover {
55 | -webkit-transform: scale(1.2) rotate(10deg);
56 | -ms-transform: scale(1.2) rotate(10deg);
57 | transform: scale(1.2) rotate(10deg);
58 | }
59 |
60 | @media (max-width:768px) {
61 | .c3 {
62 | font-size: 4.0rem;
63 | }
64 | }
65 |
66 | @media (max-width:768px) {
67 | .c6 {
68 | grid-template-columns: 1fr;
69 | }
70 | }
71 |
72 |
73 |
77 |
80 |
83 |
86 | My grid
87 |
88 |
91 |
92 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
93 |
94 |
95 |
98 |
101 |
106 |
107 |
110 |
115 |
116 |
119 |
124 |
125 |
128 |
133 |
134 |
137 |
142 |
143 |
146 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | `;
158 |
159 | exports[` > should render without background 1`] = `
160 | .c3 {
161 | color: #0A1128;
162 | font-size: 6.4rem;
163 | text-transform: uppercase;
164 | }
165 |
166 | .c1 {
167 | max-width: 120rem;
168 | margin: 0 auto;
169 | padding: 3.2rem;
170 | }
171 |
172 | .c0 {
173 | background: #FFFFFF;
174 | color: #0A1128;
175 | min-height: 100vh;
176 | display: -webkit-box;
177 | display: -webkit-flex;
178 | display: -ms-flexbox;
179 | display: flex;
180 | -webkit-align-items: center;
181 | -webkit-box-align: center;
182 | -ms-flex-align: center;
183 | align-items: center;
184 | }
185 |
186 | .c5 {
187 | font-size: 2.4rem;
188 | }
189 |
190 | .c2 > .c4 {
191 | margin-bottom: 6.4rem;
192 | }
193 |
194 | .c6 {
195 | display: grid;
196 | grid-template-columns: repeat(auto-fill,minmax(280px,1fr));
197 | gap: 3.2rem;
198 | }
199 |
200 | .c7 {
201 | overflow: hidden;
202 | }
203 |
204 | .c8 {
205 | width: 100%;
206 | -webkit-transition: all 300ms ease-in-out;
207 | transition: all 300ms ease-in-out;
208 | }
209 |
210 | .c8:hover {
211 | -webkit-transform: scale(1.2) rotate(10deg);
212 | -ms-transform: scale(1.2) rotate(10deg);
213 | transform: scale(1.2) rotate(10deg);
214 | }
215 |
216 | @media (max-width:768px) {
217 | .c3 {
218 | font-size: 4.0rem;
219 | }
220 | }
221 |
222 | @media (max-width:768px) {
223 | .c6 {
224 | grid-template-columns: 1fr;
225 | }
226 | }
227 |
228 |
229 |
233 |
236 |
239 |
242 | My grid
243 |
244 |
247 |
248 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
249 |
250 |
251 |
254 |
257 |
262 |
263 |
266 |
271 |
272 |
275 |
280 |
281 |
284 |
289 |
290 |
293 |
298 |
299 |
302 |
307 |
308 |
309 |
310 |
311 |
312 |
313 | `;
314 |
--------------------------------------------------------------------------------
/src/components/GridImage/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import { Heading } from '../Heading';
3 | import { SectionBackground } from '../SectionBackground';
4 | import { TextComponent } from '../TextComponent';
5 | import * as Styled from './styles';
6 |
7 | export const GridImage = ({
8 | title,
9 | description,
10 | grid,
11 | background = false,
12 | sectionId = '',
13 | }) => {
14 | return (
15 |
16 |
17 |
18 | {title}
19 |
20 | {description}
21 |
22 | {grid.map((el) => (
23 |
24 |
25 |
26 | ))}
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | GridImage.propTypes = {
34 | background: P.bool,
35 | title: P.string.isRequired,
36 | description: P.string.isRequired,
37 | grid: P.arrayOf(
38 | P.shape({
39 | altText: P.string.isRequired,
40 | srcImg: P.string.isRequired,
41 | }),
42 | ).isRequired,
43 | sectionId: P.string,
44 | };
45 |
--------------------------------------------------------------------------------
/src/components/GridImage/mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | background: false,
3 | title: 'My grid',
4 | description:
5 | 'Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde',
6 | grid: [
7 | {
8 | altText: 'Teste 1',
9 | srcImg: 'https://source.unsplash.com/random/800x800?r=1',
10 | },
11 | {
12 | altText: 'Teste 2',
13 | srcImg: 'https://source.unsplash.com/random/800x800?r=2',
14 | },
15 | {
16 | altText: 'Teste 3',
17 | srcImg: 'https://source.unsplash.com/random/800x800?r=3',
18 | },
19 | {
20 | altText: 'Teste 4',
21 | srcImg: 'https://source.unsplash.com/random/800x800?r=4',
22 | },
23 | {
24 | altText: 'Teste 5',
25 | srcImg: 'https://source.unsplash.com/random/800x800?r=5',
26 | },
27 | {
28 | altText: 'Teste 6',
29 | srcImg: 'https://source.unsplash.com/random/800x800?r=6',
30 | },
31 | ],
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/GridImage/stories.jsx:
--------------------------------------------------------------------------------
1 | import { GridImage } from '.';
2 |
3 | import mock from './mock';
4 |
5 | export default {
6 | title: 'GridImage',
7 | component: GridImage,
8 | args: mock,
9 | };
10 |
11 | export const Template = (args) => {
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/src/components/GridImage/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 | import { Title as HeadingContainer } from '../Heading/styles';
3 | import { Container as TextComponent } from '../TextComponent/styles';
4 |
5 | export const Container = styled.div`
6 | ${({ theme }) => css`
7 | > ${TextComponent} {
8 | margin-bottom: ${theme.spacings.xhuge};
9 | }
10 | `}
11 | `;
12 |
13 | export const Grid = styled.div`
14 | ${({ theme }) => css`
15 | display: grid;
16 | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
17 | gap: ${theme.spacings.large};
18 |
19 | @media ${theme.media.lteMedium} {
20 | grid-template-columns: 1fr;
21 | }
22 | `}
23 | `;
24 |
25 | export const GridElement = styled.div`
26 | ${({ theme }) => css`
27 | overflow: hidden;
28 | `}
29 | `;
30 |
31 | export const Image = styled.img`
32 | ${({ theme }) => css`
33 | width: 100%;
34 | transition: all 300ms ease-in-out;
35 |
36 | &:hover {
37 | transform: scale(1.2) rotate(10deg);
38 | }
39 | `}
40 | `;
41 |
--------------------------------------------------------------------------------
/src/components/GridText/GridText.test.jsx:
--------------------------------------------------------------------------------
1 | import { renderTheme } from '../../styles/render-theme';
2 | import { GridText } from '.';
3 |
4 | import mock from './mock';
5 |
6 | describe(' ', () => {
7 | it('should render with background', () => {
8 | const { container } = renderTheme( );
9 | expect(container).toMatchSnapshot();
10 | });
11 |
12 | it('should render without background', () => {
13 | const { container } = renderTheme(
14 | ,
15 | );
16 | expect(container).toMatchSnapshot();
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/src/components/GridText/__snapshots__/GridText.test.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should render with background 1`] = `
4 | .c4 {
5 | color: #0A1128;
6 | font-size: 6.4rem;
7 | text-transform: uppercase;
8 | }
9 |
10 | .c9 {
11 | color: #0A1128;
12 | font-size: 3.2rem;
13 | text-transform: none;
14 | }
15 |
16 | .c6 {
17 | font-size: 2.4rem;
18 | }
19 |
20 | .c2 .c5 {
21 | margin-bottom: 6.4rem;
22 | }
23 |
24 | .c7 {
25 | counter-reset: grid-counter;
26 | display: grid;
27 | grid-template-columns: repeat(auto-fill,minmax(280px,1fr));
28 | gap: 3.2rem;
29 | overflow: hidden;
30 | width: 100%;
31 | }
32 |
33 | .c8 .c3 {
34 | position: relative;
35 | left: 5rem;
36 | }
37 |
38 | .c8 .c3::before {
39 | counter-increment: grid-counter;
40 | content: counter(grid-counter);
41 | position: absolute;
42 | font-size: 7rem;
43 | top: -3rem;
44 | left: -5rem;
45 | -webkit-transform: rotate(5deg);
46 | -ms-transform: rotate(5deg);
47 | transform: rotate(5deg);
48 | }
49 |
50 | .c1 {
51 | max-width: 120rem;
52 | margin: 0 auto;
53 | padding: 3.2rem;
54 | }
55 |
56 | .c0 {
57 | background: #FFFFFF;
58 | color: #0A1128;
59 | min-height: 100vh;
60 | display: -webkit-box;
61 | display: -webkit-flex;
62 | display: -ms-flexbox;
63 | display: flex;
64 | -webkit-align-items: center;
65 | -webkit-box-align: center;
66 | -ms-flex-align: center;
67 | align-items: center;
68 | }
69 |
70 | @media (max-width:768px) {
71 | .c4 {
72 | font-size: 4.0rem;
73 | }
74 | }
75 |
76 | @media (max-width:768px) {
77 | .c7 {
78 | grid-template-columns: 1fr;
79 | }
80 | }
81 |
82 |
83 |
87 |
90 |
93 |
96 | My grid
97 |
98 |
101 |
102 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
103 |
104 |
105 |
108 |
111 |
114 | Teste 1
115 |
116 |
119 |
120 | 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.
121 |
122 |
123 |
124 |
127 |
130 | Teste 2
131 |
132 |
135 |
136 | 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.
137 |
138 |
139 |
140 |
143 |
146 | Teste 3
147 |
148 |
151 |
152 | 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.
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | `;
162 |
163 | exports[` > should render without background 1`] = `
164 | .c4 {
165 | color: #0A1128;
166 | font-size: 6.4rem;
167 | text-transform: uppercase;
168 | }
169 |
170 | .c9 {
171 | color: #0A1128;
172 | font-size: 3.2rem;
173 | text-transform: none;
174 | }
175 |
176 | .c6 {
177 | font-size: 2.4rem;
178 | }
179 |
180 | .c2 .c5 {
181 | margin-bottom: 6.4rem;
182 | }
183 |
184 | .c7 {
185 | counter-reset: grid-counter;
186 | display: grid;
187 | grid-template-columns: repeat(auto-fill,minmax(280px,1fr));
188 | gap: 3.2rem;
189 | overflow: hidden;
190 | width: 100%;
191 | }
192 |
193 | .c8 .c3 {
194 | position: relative;
195 | left: 5rem;
196 | }
197 |
198 | .c8 .c3::before {
199 | counter-increment: grid-counter;
200 | content: counter(grid-counter);
201 | position: absolute;
202 | font-size: 7rem;
203 | top: -3rem;
204 | left: -5rem;
205 | -webkit-transform: rotate(5deg);
206 | -ms-transform: rotate(5deg);
207 | transform: rotate(5deg);
208 | }
209 |
210 | .c1 {
211 | max-width: 120rem;
212 | margin: 0 auto;
213 | padding: 3.2rem;
214 | }
215 |
216 | .c0 {
217 | background: #FFFFFF;
218 | color: #0A1128;
219 | min-height: 100vh;
220 | display: -webkit-box;
221 | display: -webkit-flex;
222 | display: -ms-flexbox;
223 | display: flex;
224 | -webkit-align-items: center;
225 | -webkit-box-align: center;
226 | -ms-flex-align: center;
227 | align-items: center;
228 | }
229 |
230 | @media (max-width:768px) {
231 | .c4 {
232 | font-size: 4.0rem;
233 | }
234 | }
235 |
236 | @media (max-width:768px) {
237 | .c7 {
238 | grid-template-columns: 1fr;
239 | }
240 | }
241 |
242 |
243 |
247 |
250 |
253 |
256 | My grid
257 |
258 |
261 |
262 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
263 |
264 |
265 |
268 |
271 |
274 | Teste 1
275 |
276 |
279 |
280 | 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.
281 |
282 |
283 |
284 |
287 |
290 | Teste 2
291 |
292 |
295 |
296 | 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.
297 |
298 |
299 |
300 |
303 |
306 | Teste 3
307 |
308 |
311 |
312 | 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.
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 | `;
322 |
--------------------------------------------------------------------------------
/src/components/GridText/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 | import { SectionBackground } from '../SectionBackground';
4 | import { Heading } from '../Heading';
5 | import { TextComponent } from '../TextComponent';
6 |
7 | export const GridText = ({
8 | title,
9 | description,
10 | grid,
11 | background = false,
12 | sectionId = '',
13 | }) => {
14 | return (
15 |
16 |
17 |
18 | {title}
19 |
20 | {description}
21 |
22 | {grid.map((el) => (
23 |
24 |
25 | {el.title}
26 |
27 | {el.description}
28 |
29 | ))}
30 |
31 |
32 |
33 | );
34 | };
35 |
36 | GridText.propTypes = {
37 | background: P.bool,
38 | title: P.string.isRequired,
39 | description: P.string.isRequired,
40 | grid: P.arrayOf(
41 | P.shape({
42 | title: P.string.isRequired,
43 | description: P.string.isRequired,
44 | }),
45 | ).isRequired,
46 | sectionId: P.string,
47 | };
48 |
--------------------------------------------------------------------------------
/src/components/GridText/mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | background: false,
3 | title: 'My grid',
4 | description:
5 | 'Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde',
6 | grid: [
7 | {
8 | title: 'Teste 1',
9 | description:
10 | '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.',
11 | },
12 | {
13 | title: 'Teste 2',
14 | description:
15 | '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.',
16 | },
17 | {
18 | title: 'Teste 3',
19 | description:
20 | '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.',
21 | },
22 | ],
23 | };
24 |
--------------------------------------------------------------------------------
/src/components/GridText/stories.jsx:
--------------------------------------------------------------------------------
1 | import { GridText } from '.';
2 |
3 | import mock from './mock';
4 |
5 | export default {
6 | title: 'GridText',
7 | component: GridText,
8 | args: mock,
9 | };
10 |
11 | export const Template = (args) => {
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/src/components/GridText/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 | import { Title as HeadingContainer } from '../Heading/styles';
3 | import { Container as TextComponent } from '../TextComponent/styles';
4 |
5 | export const Container = styled.div`
6 | ${({ theme }) => css`
7 | ${TextComponent} {
8 | margin-bottom: ${theme.spacings.xhuge};
9 | }
10 | `}
11 | `;
12 |
13 | export const Grid = styled.div`
14 | ${({ theme }) => css`
15 | counter-reset: grid-counter;
16 | display: grid;
17 | grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
18 | gap: ${theme.spacings.large};
19 | overflow: hidden;
20 | width: 100%;
21 |
22 | @media ${theme.media.lteMedium} {
23 | grid-template-columns: 1fr;
24 | }
25 | `}
26 | `;
27 |
28 | export const GridElement = styled.div`
29 | ${() => css`
30 | ${HeadingContainer} {
31 | position: relative;
32 | left: 5rem;
33 | }
34 |
35 | ${HeadingContainer}::before {
36 | counter-increment: grid-counter;
37 | content: counter(grid-counter);
38 | position: absolute;
39 | font-size: 7rem;
40 | top: -3rem;
41 | left: -5rem;
42 | transform: rotate(5deg);
43 | }
44 | `}
45 | `;
46 |
--------------------------------------------------------------------------------
/src/components/GridTwoColumns/GridTwoColumns.spec.jsx:
--------------------------------------------------------------------------------
1 | import { renderTheme } from '../../styles/render-theme';
2 | import { GridTwoColumns } from '.';
3 |
4 | import mock from './mock';
5 |
6 | describe(' ', () => {
7 | it('should render two column grid', () => {
8 | const { container } = renderTheme( );
9 | expect(container).toMatchSnapshot();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/src/components/GridTwoColumns/__snapshots__/GridTwoColumns.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should render two column grid 1`] = `
4 | .c5 {
5 | color: #0A1128;
6 | font-size: 6.4rem;
7 | text-transform: uppercase;
8 | }
9 |
10 | .c2 {
11 | display: grid;
12 | grid-template-columns: 1fr 2fr;
13 | -webkit-align-items: center;
14 | -webkit-box-align: center;
15 | -ms-flex-align: center;
16 | align-items: center;
17 | gap: 3.2rem;
18 | }
19 |
20 | .c2 .c4 {
21 | margin-bottom: 4.0rem;
22 | }
23 |
24 | .c1 {
25 | max-width: 120rem;
26 | margin: 0 auto;
27 | padding: 3.2rem;
28 | }
29 |
30 | .c0 {
31 | background: #FFFFFF;
32 | color: #0A1128;
33 | min-height: 100vh;
34 | display: -webkit-box;
35 | display: -webkit-flex;
36 | display: -ms-flexbox;
37 | display: flex;
38 | -webkit-align-items: center;
39 | -webkit-box-align: center;
40 | -ms-flex-align: center;
41 | align-items: center;
42 | }
43 |
44 | .c6 {
45 | font-size: 2.4rem;
46 | }
47 |
48 | @media (max-width:768px) {
49 | .c5 {
50 | font-size: 4.0rem;
51 | }
52 | }
53 |
54 | @media (max-width:768px) {
55 | .c2 {
56 | grid-template-columns: 1fr;
57 | text-align: center;
58 | }
59 | }
60 |
61 | @media (max-width:768px) {
62 | .c3 {
63 | margin-bottom: 3.2rem;
64 | }
65 | }
66 |
67 |
68 |
72 |
75 |
78 |
81 |
84 | Grid two columns
85 |
86 |
89 |
90 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Distinctio magnam culpa eveniet doloribus harum? Ipsam, a necessitatibus? Sequi sunt accusantium quod, animi iure a, aliquid dolor ea vel magni dolore?
91 |
92 |
93 |
94 |
97 |
102 |
103 |
104 |
105 |
106 |
107 | `;
108 |
--------------------------------------------------------------------------------
/src/components/GridTwoColumns/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 | import { SectionBackground } from '../SectionBackground';
4 | import { Heading } from '../Heading';
5 | import { TextComponent } from '../TextComponent';
6 |
7 | export const GridTwoColumns = ({
8 | title,
9 | text,
10 | srcImg,
11 | background = false,
12 | sectionId = '',
13 | }) => {
14 | return (
15 |
16 |
17 |
18 |
19 | {title}
20 |
21 | {text}
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | GridTwoColumns.propTypes = {
32 | title: P.string.isRequired,
33 | text: P.string.isRequired,
34 | srcImg: P.string.isRequired,
35 | background: P.bool,
36 | sectionId: P.string,
37 | };
38 |
--------------------------------------------------------------------------------
/src/components/GridTwoColumns/mock.js:
--------------------------------------------------------------------------------
1 | export default {
2 | title: 'Grid two columns',
3 | text: `Lorem ipsum dolor sit amet consectetur adipisicing elit. Distinctio magnam culpa eveniet doloribus harum? Ipsam, a necessitatibus? Sequi sunt accusantium quod, animi iure a, aliquid dolor ea vel magni dolore?`,
4 | srcImg: 'assets/images/javascript.svg',
5 | };
6 |
--------------------------------------------------------------------------------
/src/components/GridTwoColumns/stories.jsx:
--------------------------------------------------------------------------------
1 | import { GridTwoColum } from '.';
2 |
3 | import mock from './mock';
4 |
5 | export default {
6 | title: 'GridTwoColum',
7 | component: GridTwoColum,
8 | args: mock,
9 | argTypes: {
10 | children: { type: 'string' },
11 | },
12 | };
13 |
14 | export const Template = (args) => {
15 | return (
16 |
17 |
18 |
19 | );
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/GridTwoColumns/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 | import { Title } from '../Heading/styles';
3 |
4 | export const Container = styled.div`
5 | ${({ theme }) => css`
6 | display: grid;
7 | grid-template-columns: 1fr 2fr;
8 | align-items: center;
9 | gap: ${theme.spacings.large};
10 |
11 | @media ${theme.media.lteMedium} {
12 | grid-template-columns: 1fr;
13 | text-align: center;
14 | }
15 |
16 | ${Title} {
17 | margin-bottom: ${theme.spacings.xlarge};
18 | }
19 | `}
20 | `;
21 |
22 | export const TextContainer = styled.div`
23 | ${({ theme }) => css`
24 | @media ${theme.media.lteMedium} {
25 | margin-bottom: ${theme.spacings.large};
26 | }
27 | `}
28 | `;
29 |
30 | export const ImageContainer = styled.div`
31 | ${() => css``}
32 | `;
33 |
34 | export const Image = styled.img`
35 | ${() => css``}
36 | `;
37 |
--------------------------------------------------------------------------------
/src/components/Heading/Heading.spec.jsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 | import { ThemeProvider } from 'styled-components';
3 | import { Heading } from '.';
4 | import { renderTheme } from '../../styles/render-theme';
5 | import { theme } from '../../styles/theme';
6 | import { expect, it, describe } from 'vitest';
7 |
8 | describe(' ', () => {
9 | it('should render with default values', () => {
10 | renderTheme(texto );
11 | const heading = screen.getByRole('heading', { name: 'texto' });
12 |
13 | expect(heading).toHaveStyle({
14 | color: theme.colors.primaryColor,
15 | 'font-size': theme.font.sizes.xhuge,
16 | 'text-transform': 'none',
17 | });
18 | });
19 |
20 | it('should render with white color', () => {
21 | renderTheme(texto );
22 | const heading = screen.getByRole('heading', { name: 'texto' });
23 |
24 | expect(heading).toHaveStyle({
25 | color: theme.colors.white,
26 | });
27 | });
28 |
29 | it('should render correct heading sizes', () => {
30 | const { rerender } = renderTheme(texto );
31 | const heading = screen.getByRole('heading', { name: 'texto' });
32 |
33 | expect(heading).toHaveStyle({
34 | 'font-size': theme.font.sizes.medium,
35 | });
36 |
37 | rerender(
38 |
39 | texto
40 | ,
41 | );
42 |
43 | expect(screen.getByRole('heading', { name: 'texto' })).toHaveStyle({
44 | 'font-size': theme.font.sizes.xlarge,
45 | });
46 |
47 | rerender(
48 |
49 | texto
50 | ,
51 | );
52 |
53 | expect(screen.getByRole('heading', { name: 'texto' })).toHaveStyle({
54 | 'font-size': theme.font.sizes.large,
55 | });
56 |
57 | rerender(
58 |
59 | texto
60 | ,
61 | );
62 |
63 | expect(screen.getByRole('heading', { name: 'texto' })).toHaveStyle({
64 | 'font-size': theme.font.sizes.xhuge,
65 | });
66 | });
67 |
68 | it('should render correct font-size when using mobile', () => {
69 | renderTheme(texto );
70 | screen.getByRole('heading', { name: 'texto' });
71 |
72 | expect(screen.getByRole('heading', { name: 'texto' })).toHaveStyleRule(
73 | 'font-size',
74 | theme.font.sizes.xlarge,
75 | {
76 | media: theme.media.lteMedium,
77 | },
78 | );
79 | });
80 |
81 | it('should render with uppercase letters', () => {
82 | renderTheme(texto );
83 | const heading = screen.getByRole('heading', { name: 'texto' });
84 |
85 | expect(heading).toHaveStyle({
86 | 'text-transform': 'uppercase',
87 | });
88 | });
89 |
90 | it('should render correct heading element', () => {
91 | const { container } = renderTheme(texto );
92 | screen.getByRole('heading', { name: 'texto' });
93 | const h6 = container.querySelector('h6');
94 |
95 | expect(h6.tagName.toLowerCase()).toBe('h6');
96 | });
97 | });
98 |
--------------------------------------------------------------------------------
/src/components/Heading/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 |
4 | export const Heading = ({
5 | children,
6 | colorDark = true,
7 | as = 'h1',
8 | size = 'huge',
9 | uppercase = false,
10 | }) => {
11 | return (
12 |
18 | {children}
19 |
20 | );
21 | };
22 |
23 | Heading.propTypes = {
24 | children: P.node.isRequired,
25 | colorDark: P.bool,
26 | as: P.oneOf(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']),
27 | size: P.oneOf(['small', 'medium', 'big', 'huge']),
28 | uppercase: P.bool,
29 | };
30 |
--------------------------------------------------------------------------------
/src/components/Heading/stories.jsx:
--------------------------------------------------------------------------------
1 | import { Heading } from '.';
2 |
3 | export default {
4 | title: 'Heading',
5 | component: Heading,
6 | args: {
7 | children: 'O texto está escuro',
8 | },
9 | argTypes: {
10 | children: { type: 'string' },
11 | },
12 | parameters: {
13 | backgrounds: {
14 | default: 'dark',
15 | },
16 | },
17 | };
18 |
19 | export const Light = (args) => ;
20 | export const Dark = (args) => ;
21 |
22 | Light.parameters = {
23 | backgrounds: {
24 | default: 'light',
25 | },
26 | };
27 |
28 | Dark.args = {
29 | children: 'O texto está claro',
30 | colorDark: false,
31 | };
32 |
--------------------------------------------------------------------------------
/src/components/Heading/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | const titleSize = {
4 | small: (theme) => css`
5 | font-size: ${theme.font.sizes.medium};
6 | `,
7 | medium: (theme) => css`
8 | font-size: ${theme.font.sizes.large};
9 | `,
10 | big: (theme) => css`
11 | font-size: ${theme.font.sizes.xlarge};
12 | `,
13 | huge: (theme) => css`
14 | font-size: ${theme.font.sizes.xhuge};
15 | ${mediaFont(theme)};
16 | `,
17 | };
18 |
19 | const mediaFont = (theme) => css`
20 | @media ${theme.media.lteMedium} {
21 | font-size: ${theme.font.sizes.xlarge};
22 | }
23 | `;
24 |
25 | const titleCase = (uppercase) => css`
26 | text-transform: ${uppercase ? 'uppercase' : 'none'};
27 | `;
28 |
29 | export const Title = styled.h1`
30 | ${({ theme, colorDark, size, uppercase }) => css`
31 | color: ${colorDark ? theme.colors.primaryColor : theme.colors.white};
32 | ${titleSize[size](theme)};
33 | ${titleCase(uppercase)};
34 | `}
35 | `;
36 |
--------------------------------------------------------------------------------
/src/components/LogoLink/LogoLink.spec.jsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 | import { renderTheme } from '../../styles/render-theme';
3 | import { LogoLink } from '.';
4 |
5 | describe(' ', () => {
6 | it('should render text logo', () => {
7 | renderTheme( );
8 | expect(screen.getByRole('link', { name: 'Olá mundo' })).toHaveAttribute(
9 | 'href',
10 | '#target',
11 | );
12 | });
13 |
14 | it('should render image logo', () => {
15 | renderTheme(
16 | ,
17 | );
18 | expect(screen.getByAltText('Olá mundo')).toHaveAttribute(
19 | 'src',
20 | 'image.jpg',
21 | );
22 | });
23 |
24 | it('should match snapshot', () => {
25 | const { container } = renderTheme(
26 | ,
27 | );
28 | expect(container).toMatchSnapshot();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/src/components/LogoLink/__snapshots__/LogoLink.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should match snapshot 1`] = `
4 | .c1 {
5 | display: -webkit-box;
6 | display: -webkit-flex;
7 | display: -ms-flexbox;
8 | display: flex;
9 | -webkit-align-items: center;
10 | -webkit-box-align: center;
11 | -ms-flex-align: center;
12 | align-items: center;
13 | -webkit-text-decoration: none;
14 | text-decoration: none;
15 | color: inherit;
16 | }
17 |
18 | .c1 > img {
19 | height: 3rem;
20 | }
21 |
22 | .c0 {
23 | color: #0A1128;
24 | font-size: 2.4rem;
25 | text-transform: uppercase;
26 | }
27 |
28 |
43 | `;
44 |
--------------------------------------------------------------------------------
/src/components/LogoLink/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 | import { Heading } from '../Heading';
4 |
5 | export const LogoLink = ({ text, srcImg = '', link }) => {
6 | return (
7 |
8 |
9 | {!!srcImg && }
10 | {!srcImg && text}
11 |
12 |
13 | );
14 | };
15 |
16 | LogoLink.propTypes = {
17 | text: P.string.isRequired,
18 | srcImg: P.string,
19 | link: P.string.isRequired,
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/LogoLink/stories.jsx:
--------------------------------------------------------------------------------
1 | import { LogoLink } from '.';
2 |
3 | export default {
4 | title: 'LogoLink',
5 | component: LogoLink,
6 | args: {
7 | text: 'Logo',
8 | srcImg: '',
9 | link: 'http://localhost',
10 | },
11 | argTypes: {
12 | text: { type: 'string' },
13 | srcImg: { type: 'string' },
14 | link: { type: 'string' },
15 | },
16 | };
17 |
18 | export const Template = (args) => {
19 | return (
20 |
21 |
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/src/components/LogoLink/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.a`
4 | ${() => css`
5 | display: flex;
6 | align-items: center;
7 | text-decoration: none;
8 | color: inherit;
9 |
10 | > img {
11 | height: 3rem;
12 | }
13 | `}
14 | `;
15 |
--------------------------------------------------------------------------------
/src/components/Menu/Menu.spec.jsx:
--------------------------------------------------------------------------------
1 | import { fireEvent, screen } from '@testing-library/react';
2 | import { renderTheme } from '../../styles/render-theme';
3 | import { Menu } from '.';
4 |
5 | import linksMock from '../NavLinks/mock';
6 | import { theme } from '../../styles/theme';
7 | const logoData = {
8 | text: 'Logo',
9 | link: '#target',
10 | };
11 |
12 | describe(' ', () => {
13 | it('should render Logo and Main Menu Nav', () => {
14 | const { container } = renderTheme(
15 | ,
16 | );
17 | expect(screen.getByRole('heading', { name: 'Logo' })).toBeInTheDocument();
18 | expect(
19 | screen.getByRole('navigation', { name: 'Main menu' }),
20 | ).toBeInTheDocument();
21 |
22 | expect(container).toMatchSnapshot();
23 | });
24 |
25 | it('should render menu mobile and button for open and close the menu', () => {
26 | renderTheme( );
27 |
28 | const button = screen.getByLabelText('Open/Close menu');
29 | const menuContainer = button.nextSibling;
30 |
31 | expect(button).toHaveStyleRule('display', 'none');
32 | expect(button).toHaveStyleRule('display', 'flex', {
33 | media: theme.media.lteMedium,
34 | });
35 |
36 | expect(menuContainer).toHaveStyleRule('opacity', '0', {
37 | media: theme.media.lteMedium,
38 | });
39 | expect(screen.getByLabelText('Open menu')).toBeInTheDocument();
40 |
41 | fireEvent.click(button);
42 | expect(menuContainer).toHaveStyleRule('opacity', '1', {
43 | media: theme.media.lteMedium,
44 | });
45 | expect(screen.getByLabelText('Close menu')).toBeInTheDocument();
46 |
47 | fireEvent.click(menuContainer);
48 | expect(menuContainer).toHaveStyleRule('opacity', '0', {
49 | media: theme.media.lteMedium,
50 | });
51 | expect(screen.getByLabelText('Open menu')).toBeInTheDocument();
52 | });
53 |
54 | it('should not render links', () => {
55 | const { container } = renderTheme( );
56 | expect(
57 | screen.queryByRole('navigation', { name: 'Main menu' }).firstChild,
58 | ).not.toBeInTheDocument();
59 | expect(container).toMatchSnapshot();
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/src/components/Menu/__snapshots__/Menu.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should not render links 1`] = `
4 | .c4 {
5 | max-width: 120rem;
6 | margin: 0 auto;
7 | padding: 3.2rem;
8 | }
9 |
10 | .c7 {
11 | color: #0A1128;
12 | font-size: 2.4rem;
13 | text-transform: uppercase;
14 | }
15 |
16 | .c2 {
17 | position: fixed;
18 | z-index: 5;
19 | top: 0;
20 | left: 0;
21 | right: 0;
22 | width: 100%;
23 | border-bottom: #DDDDDD;
24 | background: #FFFFFF;
25 | -webkit-transition: all 300ms ease-in-out;
26 | transition: all 300ms ease-in-out;
27 | }
28 |
29 | .c2 > .c3 {
30 | padding-top: 0;
31 | padding-bottom: 0;
32 | }
33 |
34 | .c2 .c6 {
35 | margin-top: 0;
36 | margin-bottom: 0;
37 | }
38 |
39 | .c5 {
40 | display: -webkit-box;
41 | display: -webkit-flex;
42 | display: -ms-flexbox;
43 | display: flex;
44 | -webkit-box-pack: justify;
45 | -webkit-justify-content: space-between;
46 | -ms-flex-pack: justify;
47 | justify-content: space-between;
48 | -webkit-align-items: center;
49 | -webkit-box-align: center;
50 | -ms-flex-align: center;
51 | align-items: center;
52 | }
53 |
54 | .c0 {
55 | z-index: 6;
56 | position: fixed;
57 | top: 2rem;
58 | right: 2rem;
59 | width: 4rem;
60 | height: 4rem;
61 | background: #0A1128;
62 | color: #FFFFFF;
63 | border: none;
64 | display: none;
65 | pointer-events: all;
66 | }
67 |
68 | .c0 > svg {
69 | width: 2.5rem;
70 | height: 2.5rem;
71 | }
72 |
73 | .c8 {
74 | display: -webkit-box;
75 | display: -webkit-flex;
76 | display: -ms-flexbox;
77 | display: flex;
78 | -webkit-align-items: center;
79 | -webkit-box-align: center;
80 | -ms-flex-align: center;
81 | align-items: center;
82 | -webkit-text-decoration: none;
83 | text-decoration: none;
84 | color: inherit;
85 | }
86 |
87 | .c8 > img {
88 | height: 3rem;
89 | }
90 |
91 | .c9 {
92 | display: -webkit-box;
93 | display: -webkit-flex;
94 | display: -ms-flexbox;
95 | display: flex;
96 | -webkit-flex-flow: row wrap;
97 | -ms-flex-flow: row wrap;
98 | flex-flow: row wrap;
99 | }
100 |
101 | .c1 {
102 | display: inline-block;
103 | vertical-align: middle;
104 | overflow: hidden;
105 | }
106 |
107 | @media (max-width:768px) {
108 | .c2 {
109 | height: 100vh;
110 | visibility: hidden;
111 | opacity: 0;
112 | }
113 |
114 | .c2 > .c3 {
115 | display: grid;
116 | grid-template-columns: 1fr;
117 | grid-template-rows: 1fr;
118 | height: 100vh;
119 | -webkit-align-items: center;
120 | -webkit-box-align: center;
121 | -ms-flex-align: center;
122 | align-items: center;
123 | overflow-y: auto;
124 | }
125 |
126 | .c2 .c6 {
127 | padding-bottom: 3.2rem;
128 | display: -webkit-box;
129 | display: -webkit-flex;
130 | display: -ms-flexbox;
131 | display: flex;
132 | -webkit-box-pack: center;
133 | -webkit-justify-content: center;
134 | -ms-flex-pack: center;
135 | justify-content: center;
136 | }
137 | }
138 |
139 | @media (max-width:768px) {
140 | .c5 {
141 | display: block;
142 | text-align: center;
143 | padding: 4.8rem 0;
144 | }
145 | }
146 |
147 | @media (max-width:768px) {
148 | .c0 {
149 | display: -webkit-box;
150 | display: -webkit-flex;
151 | display: -ms-flexbox;
152 | display: flex;
153 | -webkit-align-items: center;
154 | -webkit-box-align: center;
155 | -ms-flex-align: center;
156 | align-items: center;
157 | -webkit-box-pack: center;
158 | -webkit-justify-content: center;
159 | -ms-flex-pack: center;
160 | justify-content: center;
161 | }
162 | }
163 |
164 | @media (max-width:768px) {
165 | .c9 {
166 | -webkit-flex-flow: column wrap;
167 | -ms-flex-flow: column wrap;
168 | flex-flow: column wrap;
169 | -webkit-align-content: center;
170 | -ms-flex-line-pack: center;
171 | align-content: center;
172 | }
173 | }
174 |
175 |
176 |
180 |
189 |
193 |
196 |
197 |
198 |
224 |
225 | `;
226 |
227 | exports[` > should render Logo and Main Menu Nav 1`] = `
228 | .c4 {
229 | max-width: 120rem;
230 | margin: 0 auto;
231 | padding: 3.2rem;
232 | }
233 |
234 | .c7 {
235 | color: #0A1128;
236 | font-size: 2.4rem;
237 | text-transform: uppercase;
238 | }
239 |
240 | .c2 {
241 | position: fixed;
242 | z-index: 5;
243 | top: 0;
244 | left: 0;
245 | right: 0;
246 | width: 100%;
247 | border-bottom: #DDDDDD;
248 | background: #FFFFFF;
249 | -webkit-transition: all 300ms ease-in-out;
250 | transition: all 300ms ease-in-out;
251 | }
252 |
253 | .c2 > .c3 {
254 | padding-top: 0;
255 | padding-bottom: 0;
256 | }
257 |
258 | .c2 .c6 {
259 | margin-top: 0;
260 | margin-bottom: 0;
261 | }
262 |
263 | .c5 {
264 | display: -webkit-box;
265 | display: -webkit-flex;
266 | display: -ms-flexbox;
267 | display: flex;
268 | -webkit-box-pack: justify;
269 | -webkit-justify-content: space-between;
270 | -ms-flex-pack: justify;
271 | justify-content: space-between;
272 | -webkit-align-items: center;
273 | -webkit-box-align: center;
274 | -ms-flex-align: center;
275 | align-items: center;
276 | }
277 |
278 | .c0 {
279 | z-index: 6;
280 | position: fixed;
281 | top: 2rem;
282 | right: 2rem;
283 | width: 4rem;
284 | height: 4rem;
285 | background: #0A1128;
286 | color: #FFFFFF;
287 | border: none;
288 | display: none;
289 | pointer-events: all;
290 | }
291 |
292 | .c0 > svg {
293 | width: 2.5rem;
294 | height: 2.5rem;
295 | }
296 |
297 | .c8 {
298 | display: -webkit-box;
299 | display: -webkit-flex;
300 | display: -ms-flexbox;
301 | display: flex;
302 | -webkit-align-items: center;
303 | -webkit-box-align: center;
304 | -ms-flex-align: center;
305 | align-items: center;
306 | -webkit-text-decoration: none;
307 | text-decoration: none;
308 | color: inherit;
309 | }
310 |
311 | .c8 > img {
312 | height: 3rem;
313 | }
314 |
315 | .c9 {
316 | display: -webkit-box;
317 | display: -webkit-flex;
318 | display: -ms-flexbox;
319 | display: flex;
320 | -webkit-flex-flow: row wrap;
321 | -ms-flex-flow: row wrap;
322 | flex-flow: row wrap;
323 | }
324 |
325 | .c10 {
326 | display: block;
327 | -webkit-text-decoration: none;
328 | text-decoration: none;
329 | font-size: 1.6rem;
330 | padding: 1.6rem;
331 | color: #0A1128;
332 | position: relative;
333 | }
334 |
335 | .c10::after {
336 | content: '';
337 | position: absolute;
338 | bottom: 0.76rem;
339 | left: 50%;
340 | width: 0;
341 | height: 0.2rem;
342 | background: #dc143c;
343 | -webkit-transition: all 300ms ease-in-out;
344 | transition: all 300ms ease-in-out;
345 | }
346 |
347 | .c10:hover::after {
348 | left: 25%;
349 | width: 50%;
350 | }
351 |
352 | .c1 {
353 | display: inline-block;
354 | vertical-align: middle;
355 | overflow: hidden;
356 | }
357 |
358 | @media (max-width:768px) {
359 | .c2 {
360 | height: 100vh;
361 | visibility: hidden;
362 | opacity: 0;
363 | }
364 |
365 | .c2 > .c3 {
366 | display: grid;
367 | grid-template-columns: 1fr;
368 | grid-template-rows: 1fr;
369 | height: 100vh;
370 | -webkit-align-items: center;
371 | -webkit-box-align: center;
372 | -ms-flex-align: center;
373 | align-items: center;
374 | overflow-y: auto;
375 | }
376 |
377 | .c2 .c6 {
378 | padding-bottom: 3.2rem;
379 | display: -webkit-box;
380 | display: -webkit-flex;
381 | display: -ms-flexbox;
382 | display: flex;
383 | -webkit-box-pack: center;
384 | -webkit-justify-content: center;
385 | -ms-flex-pack: center;
386 | justify-content: center;
387 | }
388 | }
389 |
390 | @media (max-width:768px) {
391 | .c5 {
392 | display: block;
393 | text-align: center;
394 | padding: 4.8rem 0;
395 | }
396 | }
397 |
398 | @media (max-width:768px) {
399 | .c0 {
400 | display: -webkit-box;
401 | display: -webkit-flex;
402 | display: -ms-flexbox;
403 | display: flex;
404 | -webkit-align-items: center;
405 | -webkit-box-align: center;
406 | -ms-flex-align: center;
407 | align-items: center;
408 | -webkit-box-pack: center;
409 | -webkit-justify-content: center;
410 | -ms-flex-pack: center;
411 | justify-content: center;
412 | }
413 | }
414 |
415 | @media (max-width:768px) {
416 | .c9 {
417 | -webkit-flex-flow: column wrap;
418 | -ms-flex-flow: column wrap;
419 | flex-flow: column wrap;
420 | -webkit-align-content: center;
421 | -ms-flex-line-pack: center;
422 | align-content: center;
423 | }
424 | }
425 |
426 |
427 |
431 |
440 |
444 |
447 |
448 |
449 |
546 |
547 | `;
548 |
--------------------------------------------------------------------------------
/src/components/Menu/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 | import { SectionContainer } from '../SectionContainer';
4 | import { LogoLink } from '../LogoLink';
5 | import { NavLinks } from '../NavLinks';
6 | import { Menu as MenuIcon } from '@styled-icons/material-outlined/Menu';
7 | import { Close as CloseIcon } from '@styled-icons/material-outlined/Close';
8 | import { useState } from 'react';
9 |
10 | export const Menu = ({ links = [], logoData }) => {
11 | const [visible, setVisible] = useState(false);
12 |
13 | return (
14 | <>
15 | setVisible(true)}
18 | aria-label="Open/Close menu"
19 | >
20 | {visible ? (
21 |
22 | ) : (
23 |
24 | )}
25 |
26 | setVisible(false)}>
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | >
35 | );
36 | };
37 |
38 | Menu.propTypes = {
39 | ...NavLinks.propTypes,
40 | logoData: P.shape(LogoLink.propTypes).isRequired,
41 | };
42 |
--------------------------------------------------------------------------------
/src/components/Menu/stories.jsx:
--------------------------------------------------------------------------------
1 | import { Menu } from '.';
2 |
3 | import linksMock from '../NavLinks/mock';
4 |
5 | export default {
6 | title: 'Menu',
7 | component: Menu,
8 | args: {
9 | links: linksMock,
10 | logoData: {
11 | text: 'Logo',
12 | link: '#target',
13 | srcImg: '',
14 | },
15 | },
16 | };
17 |
18 | export const Template = (args) => {
19 | return (
20 |
21 |
22 |
23 | );
24 | };
25 |
--------------------------------------------------------------------------------
/src/components/Menu/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 | import { Container as SectionContainer } from '../SectionContainer/styles';
3 | import { Title as Heading } from '../Heading/styles';
4 |
5 | const menuVisible = () => css`
6 | visibility: visible;
7 | opacity: 1;
8 | `;
9 |
10 | export const Container = styled.div`
11 | ${({ theme, visible }) => css`
12 | position: fixed;
13 | z-index: 5;
14 | top: 0;
15 | left: 0;
16 | right: 0;
17 | width: 100%;
18 | border-bottom: ${theme.colors.mediumGray};
19 | background: ${theme.colors.white};
20 | transition: all 300ms ease-in-out;
21 |
22 | > ${SectionContainer} {
23 | padding-top: 0;
24 | padding-bottom: 0;
25 | }
26 |
27 | & ${Heading} {
28 | margin-top: 0;
29 | margin-bottom: 0;
30 | }
31 |
32 | @media ${theme.media.lteMedium} {
33 | height: 100vh;
34 | visibility: hidden;
35 | opacity: 0;
36 | ${visible && menuVisible(theme)}
37 |
38 | > ${SectionContainer} {
39 | display: grid;
40 | grid-template-columns: 1fr;
41 | grid-template-rows: 1fr;
42 | height: 100vh;
43 | align-items: center;
44 | overflow-y: auto;
45 | }
46 |
47 | & ${Heading} {
48 | padding-bottom: ${theme.spacings.large};
49 | display: flex;
50 | justify-content: center;
51 | }
52 | }
53 | `}
54 | `;
55 |
56 | export const MenuContainer = styled.div`
57 | ${({ theme }) => css`
58 | display: flex;
59 | justify-content: space-between;
60 | align-items: center;
61 |
62 | @media ${theme.media.lteMedium} {
63 | display: block;
64 | text-align: center;
65 | padding: ${theme.spacings.xxlarge} 0;
66 | }
67 | `}
68 | `;
69 |
70 | export const Button = styled.button`
71 | ${({ theme, visible }) => css`
72 | z-index: 6;
73 | position: fixed;
74 | top: 2rem;
75 | right: 2rem;
76 | width: 4rem;
77 | height: 4rem;
78 | background: ${theme.colors.primaryColor};
79 | color: ${theme.colors.white};
80 | border: none;
81 | display: none;
82 | pointer-events: ${visible ? 'none' : 'all'};
83 |
84 | @media ${theme.media.lteMedium} {
85 | display: flex;
86 | align-items: center;
87 | justify-content: center;
88 | }
89 |
90 | > svg {
91 | width: 2.5rem;
92 | height: 2.5rem;
93 | }
94 | `}
95 | `;
96 |
--------------------------------------------------------------------------------
/src/components/MenuLink/Menu.spec.jsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 | import { renderTheme } from '../../styles/render-theme';
3 | import { MenuLink } from '.';
4 |
5 | describe(' ', () => {
6 | it('should render a link', () => {
7 | renderTheme(Children );
8 | expect(screen.getByRole('link', { name: 'Children' })).toHaveAttribute(
9 | 'target',
10 | '_self',
11 | );
12 | });
13 |
14 | it('should render open in a new tab', () => {
15 | renderTheme(
16 |
17 | Children
18 | ,
19 | );
20 | expect(screen.getByRole('link', { name: 'Children' })).toHaveAttribute(
21 | 'target',
22 | '_blank',
23 | );
24 | });
25 |
26 | it('should render open in a new tab', () => {
27 | renderTheme(
28 |
29 | Children
30 | ,
31 | );
32 | expect(screen.getByRole('link', { name: 'Children' }))
33 | .toMatchInlineSnapshot(`
34 | .c0 {
35 | display: block;
36 | -webkit-text-decoration: none;
37 | text-decoration: none;
38 | font-size: 1.6rem;
39 | padding: 1.6rem;
40 | color: #0A1128;
41 | position: relative;
42 | }
43 |
44 | .c0::after {
45 | content: '';
46 | position: absolute;
47 | bottom: 0.76rem;
48 | left: 50%;
49 | width: 0;
50 | height: 0.2rem;
51 | background: #dc143c;
52 | -webkit-transition: all 300ms ease-in-out;
53 | transition: all 300ms ease-in-out;
54 | }
55 |
56 | .c0:hover::after {
57 | left: 25%;
58 | width: 50%;
59 | }
60 |
61 |
66 | Children
67 |
68 | `);
69 | });
70 | });
71 |
--------------------------------------------------------------------------------
/src/components/MenuLink/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 |
4 | export const MenuLink = ({ children, link, newTab = false }) => {
5 | const target = newTab ? '_blank' : '_self';
6 | return (
7 |
8 | {children}
9 |
10 | );
11 | };
12 |
13 | MenuLink.propTypes = {
14 | children: P.node.isRequired,
15 | link: P.string.isRequired,
16 | newTab: P.bool,
17 | };
18 |
--------------------------------------------------------------------------------
/src/components/MenuLink/stories.jsx:
--------------------------------------------------------------------------------
1 | import { MenuLink } from '.';
2 |
3 | export default {
4 | title: 'MenuLink',
5 | component: MenuLink,
6 | args: {
7 | children: 'MenuLink',
8 | link: 'https://www.google.com.br/',
9 | },
10 | argTypes: {
11 | children: { type: 'string' },
12 | },
13 | };
14 |
15 | export const Template = (args) => {
16 | return (
17 |
18 |
19 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/src/components/MenuLink/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.a`
4 | ${({ theme }) => css`
5 | display: block;
6 | text-decoration: none;
7 | font-size: ${theme.font.sizes.small};
8 | padding: ${theme.spacings.small};
9 | color: ${theme.colors.primaryColor};
10 | position: relative;
11 |
12 | &::after {
13 | content: '';
14 | position: absolute;
15 | bottom: 0.76rem;
16 | left: 50%;
17 | width: 0;
18 | height: 0.2rem;
19 | background: ${theme.colors.secondaryColor};
20 | transition: all 300ms ease-in-out;
21 | }
22 |
23 | &:hover::after {
24 | left: 25%;
25 | width: 50%;
26 | }
27 | `}
28 | `;
29 |
--------------------------------------------------------------------------------
/src/components/NavLinks/NavLinks.spec.jsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 | import { renderTheme } from '../../styles/render-theme';
3 | import { NavLinks } from '.';
4 |
5 | import mock from './mock';
6 | import { theme } from '../../styles/theme';
7 |
8 | describe(' ', () => {
9 | it('should render links', () => {
10 | renderTheme( );
11 | expect(screen.getAllByRole('link')).toHaveLength(mock.length);
12 | });
13 |
14 | it('should not render links', () => {
15 | renderTheme( );
16 | expect(screen.queryAllByText(/links/i)).toHaveLength(0);
17 | });
18 |
19 | it('should render links', () => {
20 | renderTheme( );
21 | expect(screen.getByText(/link 10/i).parentElement).toHaveStyleRule(
22 | 'flex-flow',
23 | 'column wrap',
24 | {
25 | media: theme.media.lteMedium,
26 | },
27 | );
28 | });
29 |
30 | it('should match snapshot', () => {
31 | const { container } = renderTheme( );
32 | expect(container.firstChild).toMatchSnapshot();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/src/components/NavLinks/__snapshots__/NavLinks.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should match snapshot 1`] = `
4 | .c0 {
5 | display: -webkit-box;
6 | display: -webkit-flex;
7 | display: -ms-flexbox;
8 | display: flex;
9 | -webkit-flex-flow: row wrap;
10 | -ms-flex-flow: row wrap;
11 | flex-flow: row wrap;
12 | }
13 |
14 | .c1 {
15 | display: block;
16 | -webkit-text-decoration: none;
17 | text-decoration: none;
18 | font-size: 1.6rem;
19 | padding: 1.6rem;
20 | color: #0A1128;
21 | position: relative;
22 | }
23 |
24 | .c1::after {
25 | content: '';
26 | position: absolute;
27 | bottom: 0.76rem;
28 | left: 50%;
29 | width: 0;
30 | height: 0.2rem;
31 | background: #dc143c;
32 | -webkit-transition: all 300ms ease-in-out;
33 | transition: all 300ms ease-in-out;
34 | }
35 |
36 | .c1:hover::after {
37 | left: 25%;
38 | width: 50%;
39 | }
40 |
41 | @media (max-width:768px) {
42 | .c0 {
43 | -webkit-flex-flow: column wrap;
44 | -ms-flex-flow: column wrap;
45 | flex-flow: column wrap;
46 | -webkit-align-content: center;
47 | -ms-flex-line-pack: center;
48 | align-content: center;
49 | }
50 | }
51 |
52 |
56 |
61 | Link 1
62 |
63 |
68 | Link 2
69 |
70 |
75 | Link 3
76 |
77 |
82 | Link 4
83 |
84 |
89 | Link 5
90 |
91 |
96 | Link 6
97 |
98 |
103 | Link 7
104 |
105 |
110 | Link 8
111 |
112 |
117 | Link 9
118 |
119 |
124 | Link 10
125 |
126 |
127 | `;
128 |
--------------------------------------------------------------------------------
/src/components/NavLinks/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 | import { MenuLink } from '../MenuLink';
4 |
5 | export const NavLinks = ({ links = [] }) => {
6 | return (
7 |
8 | {links.map((link) => (
9 |
10 | ))}
11 |
12 | );
13 | };
14 |
15 | NavLinks.propTypes = {
16 | links: P.arrayOf(
17 | P.shape({
18 | children: P.string.isRequired,
19 | link: P.string.isRequired,
20 | newTab: P.bool,
21 | }),
22 | ),
23 | };
24 |
--------------------------------------------------------------------------------
/src/components/NavLinks/mock.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | children: 'Link 1',
4 | link: '#target1',
5 | newTab: false,
6 | },
7 | {
8 | children: 'Link 2',
9 | link: '#target2',
10 | newTab: false,
11 | },
12 | {
13 | children: 'Link 3',
14 | link: '#target3',
15 | newTab: false,
16 | },
17 | {
18 | children: 'Link 4',
19 | link: '#target4',
20 | newTab: false,
21 | },
22 | {
23 | children: 'Link 5',
24 | link: '#target5',
25 | newTab: false,
26 | },
27 | {
28 | children: 'Link 6',
29 | link: '#target6',
30 | newTab: false,
31 | },
32 | {
33 | children: 'Link 7',
34 | link: '#target7',
35 | newTab: false,
36 | },
37 | {
38 | children: 'Link 8',
39 | link: '#target8',
40 | newTab: false,
41 | },
42 | {
43 | children: 'Link 9',
44 | link: '#target9',
45 | newTab: false,
46 | },
47 | {
48 | children: 'Link 10',
49 | link: '#target10',
50 | newTab: false,
51 | },
52 | ];
53 |
--------------------------------------------------------------------------------
/src/components/NavLinks/stories.jsx:
--------------------------------------------------------------------------------
1 | import { NavLinks } from '.';
2 | import links from './mock';
3 |
4 | export default {
5 | title: 'NavLinks',
6 | component: NavLinks,
7 | args: {
8 | links: links,
9 | },
10 | argTypes: {
11 | links: { type: '' },
12 | },
13 | };
14 |
15 | export const Template = (args) => {
16 | return (
17 |
18 |
19 |
20 | );
21 | };
22 |
--------------------------------------------------------------------------------
/src/components/NavLinks/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.nav`
4 | ${({ theme }) => css`
5 | display: flex;
6 | flex-flow: row wrap;
7 |
8 | @media ${theme.media.lteMedium} {
9 | flex-flow: column wrap;
10 | align-content: center;
11 | }
12 | `}
13 | `;
14 |
--------------------------------------------------------------------------------
/src/components/SectionBackground/SectionBackground.spec.jsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 | import { renderTheme } from '../../styles/render-theme';
3 | import { SectionBackground } from '.';
4 |
5 | describe(' ', () => {
6 | it('should render with background dark', () => {
7 | const { container } = renderTheme(
8 |
9 | Testing
10 | ,
11 | );
12 | expect(screen.getByRole('heading')).toBeInTheDocument();
13 | expect(container).toMatchSnapshot();
14 | });
15 |
16 | it('should render with background light', () => {
17 | const { container } = renderTheme(
18 |
19 | Testing
20 | ,
21 | );
22 | expect(screen.getByRole('heading')).toBeInTheDocument();
23 | expect(container).toMatchSnapshot();
24 | });
25 |
26 | it('should render with background light by default', () => {
27 | const { container } = renderTheme(
28 |
29 | Testing
30 | ,
31 | );
32 | expect(screen.getByRole('heading')).toBeInTheDocument();
33 | expect(container).toMatchSnapshot();
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/components/SectionBackground/__snapshots__/SectionBackground.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should render with background dark 1`] = `
4 | .c1 {
5 | max-width: 120rem;
6 | margin: 0 auto;
7 | padding: 3.2rem;
8 | }
9 |
10 | .c0 {
11 | background: #FFFFFF;
12 | color: #0A1128;
13 | background: #0A1128;
14 | color: #FFFFFF;
15 | min-height: 100vh;
16 | display: -webkit-box;
17 | display: -webkit-flex;
18 | display: -ms-flexbox;
19 | display: flex;
20 | -webkit-align-items: center;
21 | -webkit-box-align: center;
22 | -ms-flex-align: center;
23 | align-items: center;
24 | }
25 |
26 |
27 |
31 |
34 |
35 | Testing
36 |
37 |
38 |
39 |
40 | `;
41 |
42 | exports[` > should render with background light 1`] = `
43 | .c1 {
44 | max-width: 120rem;
45 | margin: 0 auto;
46 | padding: 3.2rem;
47 | }
48 |
49 | .c0 {
50 | background: #FFFFFF;
51 | color: #0A1128;
52 | min-height: 100vh;
53 | display: -webkit-box;
54 | display: -webkit-flex;
55 | display: -ms-flexbox;
56 | display: flex;
57 | -webkit-align-items: center;
58 | -webkit-box-align: center;
59 | -ms-flex-align: center;
60 | align-items: center;
61 | }
62 |
63 |
64 |
68 |
71 |
72 | Testing
73 |
74 |
75 |
76 |
77 | `;
78 |
79 | exports[` > should render with background light by default 1`] = `
80 | .c1 {
81 | max-width: 120rem;
82 | margin: 0 auto;
83 | padding: 3.2rem;
84 | }
85 |
86 | .c0 {
87 | background: #FFFFFF;
88 | color: #0A1128;
89 | min-height: 100vh;
90 | display: -webkit-box;
91 | display: -webkit-flex;
92 | display: -ms-flexbox;
93 | display: flex;
94 | -webkit-align-items: center;
95 | -webkit-box-align: center;
96 | -ms-flex-align: center;
97 | align-items: center;
98 | }
99 |
100 |
101 |
105 |
108 |
109 | Testing
110 |
111 |
112 |
113 |
114 | `;
115 |
--------------------------------------------------------------------------------
/src/components/SectionBackground/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import { SectionContainer } from '../SectionContainer';
3 | import * as Styled from './styles';
4 |
5 | export const SectionBackground = ({
6 | children,
7 | background = false,
8 | sectionId = '',
9 | }) => {
10 | return (
11 |
12 | {children}
13 |
14 | );
15 | };
16 |
17 | SectionBackground.propTypes = {
18 | children: P.node.isRequired,
19 | background: P.bool,
20 | sectionId: P.string,
21 | };
22 |
--------------------------------------------------------------------------------
/src/components/SectionBackground/stories.jsx:
--------------------------------------------------------------------------------
1 | import { SectionBackground } from '.';
2 |
3 | export default {
4 | title: 'SectionBackground',
5 | component: SectionBackground,
6 | args: {
7 | children: (
8 |
9 |
SectionBackground
10 |
11 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorem et
12 | deleniti laudantium mollitia aspernatur, ullam iure quasi! Incidunt
13 | culpa unde sapiente. Facilis corrupti quod ex voluptatem similique
14 | placeat nulla inventore!
15 |
16 |
17 | ),
18 | background: true,
19 | },
20 | argTypes: {
21 | children: {
22 | table: { disable: true },
23 | },
24 | background: { type: 'boolean' },
25 | },
26 | };
27 |
28 | export const Template = (args) => {
29 | return (
30 |
31 |
32 |
33 | );
34 | };
35 |
--------------------------------------------------------------------------------
/src/components/SectionBackground/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | const containerBackgroundActivate = (theme) => css`
4 | background: ${theme.colors.primaryColor};
5 | color: ${theme.colors.white};
6 | `;
7 |
8 | export const Container = styled.div`
9 | ${({ theme, background }) => css`
10 | background: ${theme.colors.white};
11 | color: ${theme.colors.primaryColor};
12 |
13 | ${!!background && containerBackgroundActivate(theme)};
14 |
15 | min-height: 100vh;
16 | display: flex;
17 | align-items: center;
18 | `}
19 | `;
20 |
--------------------------------------------------------------------------------
/src/components/SectionContainer/SectionContainer.spec.jsx:
--------------------------------------------------------------------------------
1 | import { screen } from '@testing-library/react';
2 | import { renderTheme } from '../../styles/render-theme';
3 | import { SectionContainer } from '.';
4 |
5 | describe(' ', () => {
6 | it('should render', () => {
7 | const { container } = renderTheme(
8 |
9 | Children
10 | ,
11 | );
12 | expect(screen.getByRole('heading')).toBeInTheDocument();
13 | expect(container).toMatchSnapshot();
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/components/SectionContainer/__snapshots__/SectionContainer.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should render 1`] = `
4 | .c0 {
5 | max-width: 120rem;
6 | margin: 0 auto;
7 | padding: 3.2rem;
8 | }
9 |
10 |
11 |
14 |
15 | Children
16 |
17 |
18 |
19 | `;
20 |
--------------------------------------------------------------------------------
/src/components/SectionContainer/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 |
4 | export const SectionContainer = ({ children }) => {
5 | return {children} ;
6 | };
7 |
8 | SectionContainer.propTypes = {
9 | children: P.node.isRequired,
10 | };
11 |
--------------------------------------------------------------------------------
/src/components/SectionContainer/stories.jsx:
--------------------------------------------------------------------------------
1 | import { SectionContainer } from '.';
2 |
3 | export default {
4 | title: 'SectionContainer',
5 | component: SectionContainer,
6 | args: {
7 | children: (
8 |
9 |
SectionContainer
10 |
11 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorem et
12 | deleniti laudantium mollitia aspernatur, ullam iure quasi! Incidunt
13 | culpa unde sapiente. Facilis corrupti quod ex voluptatem similique
14 | placeat nulla inventore!
15 |
16 |
17 | ),
18 | },
19 | argTypes: {
20 | children: { type: '' },
21 | },
22 | };
23 |
24 | export const Template = (args) => {
25 | return (
26 |
27 |
28 |
29 | );
30 | };
31 |
--------------------------------------------------------------------------------
/src/components/SectionContainer/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | ${({ theme }) => css`
5 | max-width: 120rem;
6 | margin: 0 auto;
7 | padding: ${theme.spacings.large};
8 | `}
9 | `;
10 |
--------------------------------------------------------------------------------
/src/components/TextComponent/TextComponent.spec.jsx:
--------------------------------------------------------------------------------
1 | import { renderTheme } from '../../styles/render-theme';
2 | import { TextComponent } from '.';
3 | import { screen } from '@testing-library/react';
4 |
5 | describe(' ', () => {
6 | it('should render a text', () => {
7 | renderTheme(The text );
8 | const element = screen.getByText(/The text/i);
9 | expect(element).toBeInTheDocument();
10 | });
11 |
12 | it('should match a snapshot', () => {
13 | const { container } = renderTheme(The text );
14 | expect(container).toMatchSnapshot();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/components/TextComponent/__snapshots__/TextComponent.spec.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should match a snapshot 1`] = `
4 | .c0 {
5 | font-size: 2.4rem;
6 | }
7 |
8 |
9 |
12 |
13 | The text
14 |
15 |
16 |
17 | `;
18 |
--------------------------------------------------------------------------------
/src/components/TextComponent/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import ReactMarkdown from 'react-markdown';
3 | import * as Styled from './styles';
4 |
5 | export const TextComponent = ({ children }) => {
6 | return (
7 |
8 | {children}
9 |
10 | );
11 | };
12 |
13 | TextComponent.propTypes = {
14 | children: P.node,
15 | };
16 |
--------------------------------------------------------------------------------
/src/components/TextComponent/stories.jsx:
--------------------------------------------------------------------------------
1 | import { TextComponent } from '.';
2 |
3 | export default {
4 | title: 'TextComponent',
5 | component: TextComponent,
6 | args: {
7 | children: `
8 | Lorem ipsum, dolor sit amet consectetur adipisicing
9 | elit. Nihil, iure quaerat! Voluptates ducimus exercitationem,
10 | reprehenderit laborum, libero cum porro necessitatibus sint incidunt,
11 | praesentium ipsa quaerat debitis delectus nemo aliquid! Facere.
12 | `,
13 | },
14 | argTypes: {
15 | children: { type: 'string' },
16 | },
17 | };
18 |
19 | export const Template = (args) => (
20 |
21 |
22 |
23 | );
24 |
--------------------------------------------------------------------------------
/src/components/TextComponent/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | ${({ theme }) => css`
5 | font-size: ${theme.font.sizes.medium};
6 | `}
7 | `;
8 |
--------------------------------------------------------------------------------
/src/config/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | url: 'http://localhost:1337/pages/?slug=',
3 | siteName: 'Otávio Miranda',
4 | defaultSlug: 'landing-page',
5 | };
6 |
--------------------------------------------------------------------------------
/src/fonts/montserrat-v24-latin/montserrat-v24-latin-900.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/montserrat-v24-latin/montserrat-v24-latin-900.woff
--------------------------------------------------------------------------------
/src/fonts/montserrat-v24-latin/montserrat-v24-latin-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/montserrat-v24-latin/montserrat-v24-latin-900.woff2
--------------------------------------------------------------------------------
/src/fonts/montserrat-v24-latin/montserrat-v24-latin-900italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/montserrat-v24-latin/montserrat-v24-latin-900italic.woff
--------------------------------------------------------------------------------
/src/fonts/montserrat-v24-latin/montserrat-v24-latin-900italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/montserrat-v24-latin/montserrat-v24-latin-900italic.woff2
--------------------------------------------------------------------------------
/src/fonts/open-sans-v29-latin/open-sans-v29-latin-700.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/open-sans-v29-latin/open-sans-v29-latin-700.woff
--------------------------------------------------------------------------------
/src/fonts/open-sans-v29-latin/open-sans-v29-latin-700.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/open-sans-v29-latin/open-sans-v29-latin-700.woff2
--------------------------------------------------------------------------------
/src/fonts/open-sans-v29-latin/open-sans-v29-latin-700italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/open-sans-v29-latin/open-sans-v29-latin-700italic.woff
--------------------------------------------------------------------------------
/src/fonts/open-sans-v29-latin/open-sans-v29-latin-700italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/open-sans-v29-latin/open-sans-v29-latin-700italic.woff2
--------------------------------------------------------------------------------
/src/fonts/open-sans-v29-latin/open-sans-v29-latin-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/open-sans-v29-latin/open-sans-v29-latin-italic.woff
--------------------------------------------------------------------------------
/src/fonts/open-sans-v29-latin/open-sans-v29-latin-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/open-sans-v29-latin/open-sans-v29-latin-italic.woff2
--------------------------------------------------------------------------------
/src/fonts/open-sans-v29-latin/open-sans-v29-latin-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/open-sans-v29-latin/open-sans-v29-latin-regular.woff
--------------------------------------------------------------------------------
/src/fonts/open-sans-v29-latin/open-sans-v29-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luizomf/curso-reactjs-nextjs-project-3-2022/e8b64f80d8a0ad72821bd1c883ec6e290e0470d5/src/fonts/open-sans-v29-latin/open-sans-v29-latin-regular.woff2
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite App
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/index.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import { ThemeProvider } from 'styled-components';
4 | import { GlobalStyles } from './styles/global-styles';
5 | import { theme } from './styles/theme';
6 | import Home from './templates/Home';
7 | import { Route, BrowserRouter, Routes } from 'react-router-dom';
8 |
9 | const root = ReactDOM.createRoot(document.getElementById('root'));
10 | root.render(
11 |
12 |
13 |
14 |
15 | } />
16 |
17 |
18 |
19 |
20 | ,
21 | );
22 |
--------------------------------------------------------------------------------
/src/styles/global-styles.js:
--------------------------------------------------------------------------------
1 | import { createGlobalStyle } from 'styled-components';
2 |
3 | import OpenSansV29LatinRegularWoff from '../fonts/open-sans-v29-latin/open-sans-v29-latin-regular.woff';
4 | import OpenSansV29LatinRegularWoff2 from '../fonts/open-sans-v29-latin/open-sans-v29-latin-regular.woff2';
5 | import OpenSansV29LatinItalicWoff from '../fonts/open-sans-v29-latin/open-sans-v29-latin-italic.woff';
6 | import OpenSansV29LatinItalicWoff2 from '../fonts/open-sans-v29-latin/open-sans-v29-latin-italic.woff2';
7 | import OpenSansV29Latin700Woff from '../fonts/open-sans-v29-latin/open-sans-v29-latin-700.woff';
8 | import OpenSansV29Latin700Woff2 from '../fonts/open-sans-v29-latin/open-sans-v29-latin-700.woff2';
9 | import OpenSansV29Latin700ItalicWoff from '../fonts/open-sans-v29-latin/open-sans-v29-latin-700italic.woff';
10 | import OpenSansV29Latin700ItalicWoff2 from '../fonts/open-sans-v29-latin/open-sans-v29-latin-700italic.woff2';
11 | import MontserratV24Latin900Woff from '../fonts/montserrat-v24-latin/montserrat-v24-latin-900.woff';
12 | import MontserratV24Latin900Woff2 from '../fonts/montserrat-v24-latin/montserrat-v24-latin-900.woff2';
13 | import MontserratV24Latin900ItalicWoff from '../fonts/montserrat-v24-latin/montserrat-v24-latin-900italic.woff';
14 | import MontserratV24Latin900ItalicWoff2 from '../fonts/montserrat-v24-latin/montserrat-v24-latin-900italic.woff2';
15 |
16 | export const GlobalStyles = createGlobalStyle`
17 | @font-face {
18 | font-family: 'Open Sans';
19 | font-style: normal;
20 | font-weight: 400;
21 | src: local(''),
22 | url(${OpenSansV29LatinRegularWoff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
23 | url(${OpenSansV29LatinRegularWoff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
24 | }
25 |
26 | @font-face {
27 | font-family: 'Open Sans';
28 | font-style: italic;
29 | font-weight: 400;
30 | src: local(''),
31 | url(${OpenSansV29LatinItalicWoff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
32 | url(${OpenSansV29LatinItalicWoff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
33 | }
34 |
35 | @font-face {
36 | font-family: 'Open Sans';
37 | font-style: normal;
38 | font-weight: 700;
39 | src: local(''),
40 | url(${OpenSansV29Latin700Woff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
41 | url(${OpenSansV29Latin700Woff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
42 | }
43 | @font-face {
44 | font-family: 'Open Sans';
45 | font-style: italic;
46 | font-weight: 700;
47 | src: local(''),
48 | url(${OpenSansV29Latin700ItalicWoff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
49 | url(${OpenSansV29Latin700ItalicWoff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
50 | }
51 |
52 | @font-face {
53 | font-family: 'Montserrat';
54 | font-style: normal;
55 | font-weight: 900;
56 | src: local(''),
57 | url(${MontserratV24Latin900Woff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
58 | url(${MontserratV24Latin900Woff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
59 | }
60 | /* montserrat-900italic - latin */
61 | @font-face {
62 | font-family: 'Montserrat';
63 | font-style: italic;
64 | font-weight: 900;
65 | src: local(''),
66 | url(${MontserratV24Latin900ItalicWoff2}) format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */
67 | url(${MontserratV24Latin900ItalicWoff}) format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
68 | }
69 |
70 | * {
71 | margin: 0;
72 | padding: 0;
73 | box-sizing: border-box;
74 | }
75 |
76 | html {
77 | font-size: 62.5%;
78 | scroll-behavior: smooth;
79 | }
80 |
81 | body {
82 | font-size: 1.6rem;
83 | font-family: ${({ theme }) => theme.font.family.default};
84 | }
85 |
86 | h1, h2, h3, h4, h5, h6 {
87 | font-family: ${({ theme }) => theme.font.family.secondary};
88 | margin: ${({ theme }) => theme.spacings.large} 0;
89 | }
90 |
91 | p {
92 | margin: ${({ theme }) => theme.spacings.medium} 0;
93 | }
94 |
95 | ul, ol {
96 | margin: ${({ theme }) => theme.spacings.medium};
97 | padding: ${({ theme }) => theme.spacings.medium};
98 | }
99 |
100 | a {
101 | color: ${({ theme }) => theme.colors.secondaryColor};
102 | }
103 | `;
104 |
--------------------------------------------------------------------------------
/src/styles/render-theme.jsx:
--------------------------------------------------------------------------------
1 | import { render } from '@testing-library/react';
2 | import { ThemeProvider } from 'styled-components';
3 | import { theme } from './theme';
4 | import PropTypes from 'prop-types';
5 |
6 | const Theme = ({ children }) => {
7 | return {children} ;
8 | };
9 |
10 | Theme.propTypes = {
11 | children: PropTypes.node.isRequired,
12 | };
13 |
14 | export const renderTheme = (children) => {
15 | const rendered = render({children} );
16 |
17 | const { rerender } = rendered;
18 | rendered.rerender = (children) => rerender({children} );
19 |
20 | return rendered;
21 | };
22 |
--------------------------------------------------------------------------------
/src/styles/theme.js:
--------------------------------------------------------------------------------
1 | export const theme = {
2 | colors: {
3 | primaryColor: '#0A1128',
4 | secondaryColor: '#dc143c',
5 | white: '#FFFFFF',
6 | mediumGray: '#DDDDDD',
7 | },
8 | font: {
9 | family: {
10 | default: "'Open Sans', sans-serif",
11 | secondary: "'Montserrat', sans-serif",
12 | },
13 | sizes: {
14 | xsmall: '8rem',
15 | small: '1.6rem',
16 | medium: '2.4rem',
17 | large: '3.2rem',
18 | xlarge: '4.0rem',
19 | xxlarge: '4.8rem',
20 | huge: '5.6rem',
21 | xhuge: '6.4rem',
22 | },
23 | },
24 | media: {
25 | lteMedium: '(max-width: 768px)',
26 | },
27 | spacings: {
28 | xsmall: '8rem',
29 | small: '1.6rem',
30 | medium: '2.4rem',
31 | large: '3.2rem',
32 | xlarge: '4.0rem',
33 | xxlarge: '4.8rem',
34 | huge: '5.6rem',
35 | xhuge: '6.4rem',
36 | },
37 | };
38 |
--------------------------------------------------------------------------------
/src/templates/Base/__snapshots__/test.jsx.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[` > should render 1`] = `
4 | .c11 {
5 | padding-top: 5.4rem;
6 | }
7 |
8 | .c4 {
9 | max-width: 120rem;
10 | margin: 0 auto;
11 | padding: 3.2rem;
12 | }
13 |
14 | .c7 {
15 | color: #0A1128;
16 | font-size: 2.4rem;
17 | text-transform: uppercase;
18 | }
19 |
20 | .c14 {
21 | color: #FFFFFF;
22 | font-size: 6.4rem;
23 | text-transform: uppercase;
24 | }
25 |
26 | .c19 {
27 | color: #FFFFFF;
28 | font-size: 3.2rem;
29 | text-transform: none;
30 | }
31 |
32 | .c21 {
33 | color: #0A1128;
34 | font-size: 6.4rem;
35 | text-transform: uppercase;
36 | }
37 |
38 | .c22 {
39 | color: #0A1128;
40 | font-size: 3.2rem;
41 | text-transform: none;
42 | }
43 |
44 | .c2 {
45 | position: fixed;
46 | z-index: 5;
47 | top: 0;
48 | left: 0;
49 | right: 0;
50 | width: 100%;
51 | border-bottom: #DDDDDD;
52 | background: #FFFFFF;
53 | -webkit-transition: all 300ms ease-in-out;
54 | transition: all 300ms ease-in-out;
55 | }
56 |
57 | .c2 > .c3 {
58 | padding-top: 0;
59 | padding-bottom: 0;
60 | }
61 |
62 | .c2 .c6 {
63 | margin-top: 0;
64 | margin-bottom: 0;
65 | }
66 |
67 | .c5 {
68 | display: -webkit-box;
69 | display: -webkit-flex;
70 | display: -ms-flexbox;
71 | display: flex;
72 | -webkit-box-pack: justify;
73 | -webkit-justify-content: space-between;
74 | -ms-flex-pack: justify;
75 | justify-content: space-between;
76 | -webkit-align-items: center;
77 | -webkit-box-align: center;
78 | -ms-flex-align: center;
79 | align-items: center;
80 | }
81 |
82 | .c0 {
83 | z-index: 6;
84 | position: fixed;
85 | top: 2rem;
86 | right: 2rem;
87 | width: 4rem;
88 | height: 4rem;
89 | background: #0A1128;
90 | color: #FFFFFF;
91 | border: none;
92 | display: none;
93 | pointer-events: all;
94 | }
95 |
96 | .c0 > svg {
97 | width: 2.5rem;
98 | height: 2.5rem;
99 | }
100 |
101 | .c8 {
102 | -webkit-text-decoration: none;
103 | text-decoration: none;
104 | color: inherit;
105 | display: -webkit-box;
106 | display: -webkit-flex;
107 | display: -ms-flexbox;
108 | display: flex;
109 | -webkit-align-items: center;
110 | -webkit-box-align: center;
111 | -ms-flex-align: center;
112 | align-items: center;
113 | }
114 |
115 | .c8 > img {
116 | max-height: 3rem;
117 | }
118 |
119 | .c9 {
120 | display: -webkit-box;
121 | display: -webkit-flex;
122 | display: -ms-flexbox;
123 | display: flex;
124 | -webkit-flex-flow: row wrap;
125 | -ms-flex-flow: row wrap;
126 | flex-flow: row wrap;
127 | }
128 |
129 | .c10 {
130 | display: block;
131 | -webkit-text-decoration: none;
132 | text-decoration: none;
133 | font-size: 1.6rem;
134 | padding: 1.6rem;
135 | color: #0A1128;
136 | position: relative;
137 | }
138 |
139 | .c10::after {
140 | content: '';
141 | position: absolute;
142 | bottom: 0.76rem;
143 | left: 50%;
144 | width: 0;
145 | height: 0.2rem;
146 | background: #dc143c;
147 | -webkit-transition: all 300ms ease-in-out;
148 | transition: all 300ms ease-in-out;
149 | }
150 |
151 | .c10:hover::after {
152 | left: 25%;
153 | width: 50%;
154 | }
155 |
156 | .c1 {
157 | display: inline-block;
158 | vertical-align: middle;
159 | overflow: hidden;
160 | }
161 |
162 | .c23 {
163 | text-align: center;
164 | }
165 |
166 | .c23 a {
167 | color: inherit;
168 | -webkit-text-decoration: none;
169 | text-decoration: none;
170 | font-size: 1.6rem;
171 | }
172 |
173 | .c16 {
174 | font-size: 2.4rem;
175 | }
176 |
177 | .c24 {
178 | position: fixed;
179 | background: #0A1128;
180 | color: #FFFFFF;
181 | display: -webkit-box;
182 | display: -webkit-flex;
183 | display: -ms-flexbox;
184 | display: flex;
185 | -webkit-align-items: center;
186 | -webkit-box-align: center;
187 | -ms-flex-align: center;
188 | align-items: center;
189 | -webkit-box-pack: center;
190 | -webkit-justify-content: center;
191 | -ms-flex-pack: center;
192 | justify-content: center;
193 | width: 4rem;
194 | height: 4rem;
195 | bottom: 2rem;
196 | right: 2rem;
197 | z-index: 6;
198 | }
199 |
200 | .c13 .c15 {
201 | margin-bottom: 6.4rem;
202 | }
203 |
204 | .c17 {
205 | counter-reset: grid-counter;
206 | display: grid;
207 | grid-template-columns: repeat(auto-fill,minmax(280px,1fr));
208 | gap: 3.2rem;
209 | overflow: hidden;
210 | width: 100%;
211 | }
212 |
213 | .c18 .c6 {
214 | position: relative;
215 | left: 5rem;
216 | }
217 |
218 | .c18 .c6::before {
219 | counter-increment: grid-counter;
220 | content: counter(grid-counter);
221 | position: absolute;
222 | font-size: 7rem;
223 | top: -3rem;
224 | left: -5rem;
225 | -webkit-transform: rotate(5deg);
226 | -ms-transform: rotate(5deg);
227 | transform: rotate(5deg);
228 | }
229 |
230 | .c12 {
231 | background: #FFFFFF;
232 | color: #0A1128;
233 | background: #0A1128;
234 | color: #FFFFFF;
235 | min-height: 100vh;
236 | display: -webkit-box;
237 | display: -webkit-flex;
238 | display: -ms-flexbox;
239 | display: flex;
240 | -webkit-align-items: center;
241 | -webkit-box-align: center;
242 | -ms-flex-align: center;
243 | align-items: center;
244 | }
245 |
246 | .c20 {
247 | background: #FFFFFF;
248 | color: #0A1128;
249 | min-height: 100vh;
250 | display: -webkit-box;
251 | display: -webkit-flex;
252 | display: -ms-flexbox;
253 | display: flex;
254 | -webkit-align-items: center;
255 | -webkit-box-align: center;
256 | -ms-flex-align: center;
257 | align-items: center;
258 | }
259 |
260 | @media (max-width:768px) {
261 | .c11 {
262 | padding-top: 0;
263 | }
264 | }
265 |
266 | @media (max-width:768px) {
267 | .c14 {
268 | font-size: 4.0rem;
269 | }
270 | }
271 |
272 | @media (max-width:768px) {
273 | .c21 {
274 | font-size: 4.0rem;
275 | }
276 | }
277 |
278 | @media (max-width:768px) {
279 | .c2 {
280 | height: 100vh;
281 | visibility: hidden;
282 | opacity: 0;
283 | }
284 |
285 | .c2 > .c3 {
286 | display: grid;
287 | grid-template-columns: 1fr;
288 | grid-template-rows: 1fr;
289 | height: 100vh;
290 | -webkit-align-items: center;
291 | -webkit-box-align: center;
292 | -ms-flex-align: center;
293 | align-items: center;
294 | overflow-y: auto;
295 | }
296 |
297 | .c2 .c6 {
298 | padding-bottom: 3.2rem;
299 | display: -webkit-box;
300 | display: -webkit-flex;
301 | display: -ms-flexbox;
302 | display: flex;
303 | -webkit-box-pack: center;
304 | -webkit-justify-content: center;
305 | -ms-flex-pack: center;
306 | justify-content: center;
307 | }
308 | }
309 |
310 | @media (max-width:768px) {
311 | .c5 {
312 | display: block;
313 | text-align: center;
314 | padding: 4.8rem 0;
315 | }
316 | }
317 |
318 | @media (max-width:768px) {
319 | .c0 {
320 | display: -webkit-box;
321 | display: -webkit-flex;
322 | display: -ms-flexbox;
323 | display: flex;
324 | -webkit-align-items: center;
325 | -webkit-box-align: center;
326 | -ms-flex-align: center;
327 | align-items: center;
328 | -webkit-box-pack: center;
329 | -webkit-justify-content: center;
330 | -ms-flex-pack: center;
331 | justify-content: center;
332 | }
333 | }
334 |
335 | @media (max-width:768px) {
336 | .c9 {
337 | -webkit-flex-flow: column wrap;
338 | -ms-flex-flow: column wrap;
339 | flex-flow: column wrap;
340 | -webkit-align-content: center;
341 | -ms-flex-line-pack: center;
342 | align-content: center;
343 | }
344 | }
345 |
346 | @media (max-width:768px) {
347 | .c17 {
348 | grid-template-columns: 1fr;
349 | }
350 | }
351 |
352 |
353 |
357 |
366 |
370 |
373 |
374 |
375 |
472 |
475 |
478 |
481 |
484 |
487 | My grid
488 |
489 |
492 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
493 |
494 |
497 |
500 |
503 | Teste 1
504 |
505 |
508 | 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.
509 |
510 |
511 |
514 |
517 | Teste 2
518 |
519 |
522 | 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.
523 |
524 |
525 |
528 |
531 | Teste 3
532 |
533 |
536 | 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.
537 |
538 |
539 |
540 |
541 |
542 |
543 |
546 |
549 |
552 |
555 | My grid
556 |
557 |
560 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
561 |
562 |
565 |
568 |
571 | Teste 1
572 |
573 |
576 | 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.
577 |
578 |
579 |
582 |
585 | Teste 2
586 |
587 |
590 | 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.
591 |
592 |
593 |
596 |
599 | Teste 3
600 |
601 |
604 | 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.
605 |
606 |
607 |
608 |
609 |
610 |
611 |
614 |
617 |
620 |
623 | My grid
624 |
625 |
628 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
629 |
630 |
633 |
636 |
639 | Teste 1
640 |
641 |
644 | 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.
645 |
646 |
647 |
650 |
653 | Teste 2
654 |
655 |
658 | 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.
659 |
660 |
661 |
664 |
667 | Teste 3
668 |
669 |
672 | 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.
673 |
674 |
675 |
676 |
677 |
678 |
679 |
682 |
685 |
688 |
691 | My grid
692 |
693 |
696 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
697 |
698 |
701 |
704 |
707 | Teste 1
708 |
709 |
712 | 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.
713 |
714 |
715 |
718 |
721 | Teste 2
722 |
723 |
726 | 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.
727 |
728 |
729 |
732 |
735 | Teste 3
736 |
737 |
740 | 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.
741 |
742 |
743 |
744 |
745 |
746 |
747 |
750 |
753 |
756 |
759 | My grid
760 |
761 |
764 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
765 |
766 |
769 |
772 |
775 | Teste 1
776 |
777 |
780 | 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.
781 |
782 |
783 |
786 |
789 | Teste 2
790 |
791 |
794 | 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.
795 |
796 |
797 |
800 |
803 | Teste 3
804 |
805 |
808 | 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.
809 |
810 |
811 |
812 |
813 |
814 |
815 |
818 |
821 |
824 |
827 | My grid
828 |
829 |
832 | Atque doloribus nobis laudantium esse ut, non commodi maxime distinctio veritatis unde
833 |
834 |
837 |
840 |
843 | Teste 1
844 |
845 |
848 | 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.
849 |
850 |
851 |
854 |
857 | Teste 2
858 |
859 |
862 | 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.
863 |
864 |
865 |
868 |
871 | Teste 3
872 |
873 |
876 | 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.
877 |
878 |
879 |
880 |
881 |
882 |
883 |
886 |
889 |
892 |
893 | Teste de footer
894 |
895 |
896 |
897 |
898 |
899 |
905 |
913 |
917 |
920 |
921 |
922 |
923 | `;
924 |
--------------------------------------------------------------------------------
/src/templates/Base/index.jsx:
--------------------------------------------------------------------------------
1 | import P from 'prop-types';
2 | import * as Styled from './styles';
3 | import { Menu } from '../../components/Menu';
4 | import { Footer } from '../../components/Footer';
5 | import { GoTop } from '../../components/GoTop';
6 |
7 | export const Base = ({ links, logoData, footerHtml, children }) => {
8 | const handleClick = (e) => {
9 | e.preventDefault();
10 | window && window.scrollTo(0, 0);
11 | };
12 |
13 | return (
14 | <>
15 |
16 |
17 | {children}
18 |
19 |
20 |
21 | >
22 | );
23 | };
24 |
25 | Base.propTypes = {
26 | children: P.node.isRequired,
27 | ...Menu.propTypes,
28 | footerHtml: P.string.isRequired,
29 | };
30 |
--------------------------------------------------------------------------------
/src/templates/Base/mock.jsx:
--------------------------------------------------------------------------------
1 | import linksMock from '../../components/NavLinks/mock';
2 |
3 | import gridMock from '../../components/GridText/mock';
4 | import { GridText } from '../../components/GridText';
5 |
6 | export const mockBase = {
7 | children: (
8 | <>
9 |
10 |
11 |
12 |
13 |
14 |
15 | >
16 | ),
17 | links: linksMock,
18 | logoData: {
19 | text: 'Logo',
20 | link: '#',
21 | },
22 | footerHtml: 'Teste de footer
',
23 | };
24 |
--------------------------------------------------------------------------------
/src/templates/Base/stories.jsx:
--------------------------------------------------------------------------------
1 | import { Base } from '.';
2 |
3 | import { mockBase } from './mock';
4 |
5 | export default {
6 | title: 'Templates/Base',
7 | component: Base,
8 | args: mockBase,
9 | };
10 |
11 | export const Template = (args) => {
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
--------------------------------------------------------------------------------
/src/templates/Base/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css } from 'styled-components';
2 |
3 | export const Container = styled.div`
4 | ${({ theme }) => css`
5 | padding-top: 5.4rem;
6 | @media ${theme.media.lteMedium} {
7 | padding-top: 0;
8 | }
9 | `}
10 | `;
11 |
--------------------------------------------------------------------------------
/src/templates/Base/test.jsx:
--------------------------------------------------------------------------------
1 | import { renderTheme } from '../../styles/render-theme';
2 | import { Base } from '.';
3 | import { mockBase } from './mock';
4 | import { fireEvent, screen } from '@testing-library/react';
5 | import { describe, it, expect, vi } from 'vitest';
6 |
7 | let e = {};
8 | global.scrollTo = vi.fn(() => {
9 | e.preventDefault();
10 | });
11 |
12 | describe(' ', () => {
13 | it('should render', () => {
14 | const { container } = renderTheme( );
15 | expect(container).toMatchSnapshot();
16 | });
17 |
18 | it('should scrollTo top', () => {
19 | e['preventDefault'] = vi.fn();
20 | renderTheme( );
21 | const goTop = screen.getByLabelText(/Go to top/i);
22 | fireEvent.click(goTop);
23 | expect(e.preventDefault).toHaveBeenCalled();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/src/templates/Home/Home.spec.jsx:
--------------------------------------------------------------------------------
1 | import { renderTheme } from '../../styles/render-theme';
2 | import { Home } from '.';
3 |
4 | describe(' ', () => {
5 | it('should render home', () => {
6 | renderTheme( );
7 | });
8 |
9 | it('should do something', () => {
10 | expect(1).toBe(1);
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/src/templates/Home/index.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState } from 'react';
2 |
3 | import { mapData } from '../../api/map-data';
4 |
5 | import { GridTwoColumns } from '../../components/GridTwoColumns';
6 | import { GridContent } from '../../components/GridContent';
7 | import { GridText } from '../../components/GridText';
8 | import { GridImage } from '../../components/GridImage';
9 |
10 | import { Base } from '../Base';
11 | import { PageNotFound } from '../PageNotFound';
12 | import { Loading } from '../Loading';
13 | import { useLocation } from 'react-router-dom';
14 |
15 | import config from '../../config';
16 |
17 | function Home() {
18 | const [data, setData] = useState([]);
19 | const isMounted = useRef(true);
20 | const location = useLocation();
21 |
22 | useEffect(() => {
23 | const load = async () => {
24 | const pathName = location.pathname.replace(/[^a-z0-9-_]/gi, '');
25 | const slug = pathName ? pathName : 'landing-page';
26 |
27 | try {
28 | const data = await fetch(
29 | `https://strapi-v4-test.herokuapp.com/api/pages/?filters[slug]=${slug}&populate=deep`,
30 | );
31 | const json = await data.json();
32 | const { attributes } = json.data[0];
33 | const pageData = mapData([attributes]);
34 | setData(() => pageData[0]);
35 | } catch {
36 | setData(undefined);
37 | }
38 | };
39 |
40 | if (isMounted.current === true) {
41 | load();
42 | }
43 |
44 | return () => {
45 | isMounted.current = false;
46 | };
47 | }, [location]);
48 |
49 | useEffect(() => {
50 | if (data === undefined) {
51 | document.title = `Página não encontrada | ${config.siteName}`;
52 | }
53 |
54 | if (data && !data.slug) {
55 | document.title = `Carregando... | ${config.siteName}`;
56 | }
57 |
58 | if (data && data.title) {
59 | document.title = `${data.title} | ${config.siteName}`;
60 | }
61 | }, [data]);
62 |
63 | if (data === undefined) {
64 | return ;
65 | }
66 |
67 | if (data && !data.slug) {
68 | return ;
69 | }
70 |
71 | const { menu, sections, footerHtml, slug } = data;
72 | const { links, text, link, srcImg } = menu;
73 |
74 | return (
75 |
80 | {sections.map((section, index) => {
81 | const { component } = section;
82 | const key = `${slug}-${index}`;
83 |
84 | if (component === 'section.section-two-columns') {
85 | return ;
86 | }
87 |
88 | if (component === 'section.section-content') {
89 | return ;
90 | }
91 |
92 | if (component === 'section.section-grid-text') {
93 | return ;
94 | }
95 |
96 | if (component === 'section.section-grid-image') {
97 | return ;
98 | }
99 | })}
100 |
101 | );
102 | }
103 |
104 | export default Home;
105 |
--------------------------------------------------------------------------------
/src/templates/Home/styles.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components';
2 |
3 | export const Wrapper = styled.div``;
4 |
--------------------------------------------------------------------------------
/src/templates/Loading/index.jsx:
--------------------------------------------------------------------------------
1 | import * as Styled from './styles';
2 |
3 | export const Loading = () => {
4 | return ;
5 | };
6 |
--------------------------------------------------------------------------------
/src/templates/Loading/styles.js:
--------------------------------------------------------------------------------
1 | import styled, { css, keyframes } from 'styled-components';
2 |
3 | const rotate = () => keyframes`
4 | 0% {
5 | transform: translate(-50%, -50%) rotate(0deg);
6 | }
7 |
8 | 100% {
9 | transform: translate(-50%, -50%) rotate(360deg);
10 | }
11 | `;
12 |
13 | export const Container = styled.div`
14 | ${({ theme }) => css`
15 | position: absolute;
16 | top: 0;
17 | left: 0;
18 | bottom: 0;
19 | right: 0;
20 | z-index: 10;
21 | background-color: ${theme.colors.primaryColor};
22 |
23 | &:before,
24 | &:after {
25 | content: '';
26 | position: absolute;
27 | top: 50%;
28 | left: 50%;
29 | transform: translate(-50%, -50%);
30 | border-width: 0.5rem;
31 | border-color: transparent;
32 | border-style: solid;
33 | border-radius: 50%;
34 | }
35 |
36 | &:after {
37 | width: 6rem;
38 | height: 6rem;
39 | border-left: 0.5rem solid ${theme.colors.secondaryColor};
40 | border-top: 0.5rem solid ${theme.colors.secondaryColor};
41 | animation: ${rotate()} 600ms linear infinite;
42 | }
43 |
44 | &:before {
45 | width: 2rem;
46 | height: 2rem;
47 | border-left: 0.5rem solid ${theme.colors.secondaryColor};
48 | border-top: 0.5rem solid ${theme.colors.secondaryColor};
49 | animation: ${rotate()} 1s linear reverse infinite;
50 | }
51 | `}
52 | `;
53 |
--------------------------------------------------------------------------------
/src/templates/PageNotFound/index.jsx:
--------------------------------------------------------------------------------
1 | import { GridContent } from '../../components/GridContent';
2 |
3 | export const PageNotFound = () => {
4 | return (
5 |
9 | );
10 | };
11 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | root: 'src',
8 | build: {
9 | outDir: '../dist',
10 | },
11 | publicDir: '../public',
12 | test: {
13 | globals: true,
14 | environment: 'jsdom',
15 | setupFiles: ['../.test/setup.js'],
16 | include: ['**/*(*.)?{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
17 | exclude: ['node_modules', 'dist', '.idea', '.git', '.cache', 'templates'],
18 | // coverage: {
19 | // reporter: ['clover', 'json', 'lcov'],
20 | // },
21 | },
22 | });
23 |
--------------------------------------------------------------------------------