├── cypress ├── fixtures │ └── example.json ├── support │ ├── e2e.js │ └── commands.js └── e2e │ └── test.cy.js ├── cypress.config.js ├── PULL_REQUEST_TEMPLATE.md ├── .github └── workflows │ └── cypress.yml ├── public ├── index.html └── styles.css ├── package.json ├── src └── index.js ├── LICENSE ├── .gitignore └── README.md /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /cypress.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require("cypress"); 2 | 3 | module.exports = defineConfig({ 4 | e2e: { 5 | setupNodeEvents(on, config) { 6 | // implement node event listeners here 7 | }, 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## DESCRIPTION 2 | 3 | Solución al reto: 4 | 5 | Nombre: 6 | Usuario Platzi: 7 | Correo Electronico: 8 | 9 | ## Reto: 10 | 11 | - [ ] Primer problema 12 | - [ ] Segundo problema 13 | - [ ] Tercer problema 14 | - [ ] Cuarto Problema 15 | - [ ] Quinto Problema 16 | -------------------------------------------------------------------------------- /.github/workflows/cypress.yml: -------------------------------------------------------------------------------- 1 | name: PlatziStore Tests 2 | on: [pull_request] 3 | jobs: 4 | cypress-run: 5 | runs-on: ubuntu-20.04 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: cypress-io/github-action@v4 9 | with: 10 | start: npm start 11 | browser: chrome 12 | headless: true 13 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

PlatziStore

15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-challenge", 3 | "version": "1.0.0", 4 | "description": "PlatziStore Fix Bugs", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "live-server --open=public --entry-file=index.html", 8 | "e2e": "cypress open" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/platzi/js-challenge.git" 13 | }, 14 | "keywords": [], 15 | "author": "Oscar Barajas ", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/platzi/js-challenge/issues" 19 | }, 20 | "homepage": "https://github.com/platzi/js-challenge#readme", 21 | "devDependencies": { 22 | "live-server": "1.2.2" 23 | }, 24 | "dependencies": { 25 | "cypress": "10.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cypress/e2e/test.cy.js: -------------------------------------------------------------------------------- 1 | describe("PlatziStore Tests", () => { 2 | beforeEach(() => { 3 | cy.visit('http://localhost:8080/public/'); 4 | cy.wait(2000); 5 | }) 6 | 7 | it('Existe el titulo PlatziStore', () => { 8 | cy.title().should('contain', 'PlatziStore'); 9 | }); 10 | 11 | it("Obtener initialState del LocalStorage", () => { 12 | expect(localStorage.getItem('pagination')).to.eq('5'); 13 | }); 14 | 15 | it("Obtener los primeros 10 Productos", () => { 16 | cy.get('*[class^="Items"]').find('article').should('have.length', 10) 17 | }); 18 | 19 | it('Desplácese hacia abajo y renderice nuevos productos', () => { 20 | cy.scrollTo('bottom') 21 | cy.get('*[class^="Items"]').should('have.length', 2); 22 | }); 23 | 24 | it('Comprobar el nombre del Producto', () => { 25 | cy.scrollTo('bottom'); 26 | cy.get('*[class^="Items"]').find('article').find('h2').eq(0).should('exist'); 27 | }); 28 | 29 | }); -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const $app = document.getElementById('app'); 2 | const $observe = document.getElementById('observe'); 3 | const API = 'https://api.escuelajs.co/api/v1/products'; 4 | 5 | const getData = api => { 6 | fetch(api) 7 | .then(response => response.json()) 8 | .then(response => { 9 | let products = response; 10 | let output = products.map(product => { 11 | // template 12 | }); 13 | let newItem = document.createElement('section'); 14 | newItem.classList.add('Item'); 15 | newItem.innerHTML = output; 16 | $app.appendChild(newItem); 17 | }) 18 | .catch(error => console.log(error)); 19 | } 20 | 21 | const loadData = () => { 22 | getData(API); 23 | } 24 | 25 | const intersectionObserver = new IntersectionObserver(entries => { 26 | // logic... 27 | }, { 28 | rootMargin: '0px 0px 100% 0px', 29 | }); 30 | 31 | intersectionObserver.observe($observe); 32 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Comunidad Platzi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /public/styles.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400&display=swap"); 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | color: #3c484e; 7 | font-family: "Open Sans", sans-serif; 8 | } 9 | 10 | .Main { 11 | padding: 10px; 12 | grid-template-columns: minmax(auto, 768px); 13 | display: grid; 14 | justify-content: center; 15 | } 16 | 17 | .Items { 18 | grid-template-columns: repeat(2, 1fr); 19 | grid-gap: 1.5rem; 20 | grid-row-gap: 1.5em; 21 | display: grid; 22 | } 23 | 24 | .Card { 25 | text-decoration: none; 26 | box-shadow: 8px 14px 38px rgba(39, 44, 49, 0.06), 27 | 1px 3px 8px rgba(39, 44, 49, 0.03); 28 | border-radius: 5px; 29 | margin: 0 0 20px 0; 30 | display: block; 31 | animation-duration: 4s; 32 | animation-name: fade; 33 | } 34 | 35 | .Card img { 36 | width: 100%; 37 | height: auto; 38 | border-radius: 5px 5px 0 0; 39 | } 40 | 41 | .Card h2 { 42 | font-size: 18px; 43 | font-weight: 300; 44 | padding: 5px 10px; 45 | display: flex; 46 | justify-content: space-between; 47 | } 48 | 49 | @keyframes fade { 50 | from { 51 | opacity: 0; 52 | } 53 | to { 54 | opacity: 1; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PlatziStore: Tu Comercio en Línea 2 | 3 | ¡Bienvenido a PlatziStore! Somos una plataforma de comercio en línea con una amplia variedad de productos esperando ser explorados. A medida que nos preparamos para nuestro lanzamiento oficial, hemos identificado varios desafíos en nuestra aplicación que necesitan ser abordados. 4 | 5 | Desde pequeños bugs hasta tareas incompletas y nuevos recursos por implementar, estamos en busca de soluciones que garanticen una experiencia fluida para nuestros usuarios. 6 | 7 | ## Configuración Inicial 8 | 9 | ### Instalación 10 | 11 | Para comenzar, necesitas instalar las dependencias del proyecto con el siguiente comando: 12 | 13 | ``` 14 | npm install 15 | ``` 16 | 17 | ### Ejecución 18 | 19 | Una vez que hayas instalado todo, puedes ejecutar la aplicación con: 20 | 21 | ``` 22 | npm run start 23 | ``` 24 | 25 | ### Depuración 26 | 27 | Para acceder a la interfaz de depuración, visita: 28 | 29 | http://localhost:8080/public/ 30 | 31 | ## Entendiendo la Estructura 32 | 33 | ### Documentación Básica 34 | 35 | Aquí hay una breve descripción de algunos de los elementos clave: 36 | 37 | - `$app`: Es la variable donde renderizaremos nuestra aplicación. 38 | - `$observe`: Representa un elemento del DOM que será observado. 39 | - `API`: Es una constante que utiliza la FakeAPI de Platzi para obtener datos. 40 | 41 | ```javascript 42 | const $app = document.getElementById("app"); 43 | const $observe = document.getElementById("observe"); 44 | const API = "https://api.escuelajs.co/api/v1/products"; 45 | ``` 46 | 47 | Tenemos una función llamada `getData` encargada de hacer solicitudes Fetch a una API y de construir un nuevo elemento en el DOM: 48 | 49 | ```javascript 50 | const getData = (api) => { 51 | // ... 52 | }; 53 | ``` 54 | 55 | Además, contamos con una función `loadData` que obtiene información de los productos: 56 | 57 | ```javascript 58 | const loadData = () => { 59 | // ... 60 | }; 61 | ``` 62 | 63 | Y, por último, se encuentra la lógica para el Intersection Observer: 64 | 65 | ```javascript 66 | const intersectionObserver = new IntersectionObserver( 67 | (entries) => { 68 | // ... 69 | }, 70 | { 71 | rootMargin: "0px 0px 100% 0px", 72 | } 73 | ); 74 | 75 | intersectionObserver.observe($observe); 76 | ``` 77 | 78 | ## Desafíos Técnicos 79 | 80 | ### Desafío 1: Integración con la API 81 | 82 | - Estudia y analiza la estructura de la API: fakeapi.platzi.com. 83 | - Implementa la API de productos, iniciando desde el producto 5 y obteniendo los siguientes 10 productos. 84 | - Usa el `localStorage` para guardar la posición inicial ("pagination"). Asegúrate de actualizar esta posición con cada nueva petición para traer los siguientes productos. 85 | - Diseña la lógica para implementar un scroll infinito utilizando el Intersection Observer. 86 | 87 | ### Desafío 2: Presentación de Productos 88 | 89 | - Diseña una estructura HTML adecuada para mostrar cada producto. 90 | - Crea un artículo con la clase "Card" que muestre la imagen, título y precio de un producto: 91 | 92 | ```html 93 |
94 | 95 |

96 | Producto 97 | $ Precio 98 |

99 |
100 | ``` 101 | 102 | - Asegúrate de que el archivo `index.html` tenga los elementos mínimos de SEO (por ejemplo, `title = "PlatziStore"` y una descripción adecuada). 103 | 104 | ### Desafío 3: Experiencia al Recargar 105 | 106 | Al cerrar o recargar la pestaña, es esencial que los usuarios vean los primeros 10 productos: 107 | 108 | - Muestra los primeros 10 productos. 109 | - Limpia el `localStorage`. 110 | - Refactoriza la función `loadData()` para usar Async/Await. 111 | 112 | ### Desafío 4: Finalización de la Paginación 113 | 114 | Dado que la API "fakeAPI" proporciona 200 productos, utilizaremos la paginación tal como se describe en su documentación. Al llegar al final de la lista de productos: 115 | 116 | - Muestra el mensaje: "Todos los productos han sido obtenidos". 117 | - Termina la observación del elemento "observe" con el Intersection Observer. 118 | 119 | ### Desafío Bono: Despliegue 120 | 121 | ¡Pon tu solución en vivo! Despliega la aplicación en uno de los siguientes servicios: GitHub Pages, Netlify o Vercel. 122 | 123 | ## Colabora con Nosotros 124 | 125 | Si sientes que puedes aportar o mejorar algún aspecto del proyecto, ¡te animamos a hacerlo! Haz un "Fork" de este proyecto, resuelve los desafíos y envía un "Pull Request" a [js-challenge](https://github.com/platzi/js-challenge/). 126 | 127 | ## Licencia 128 | 129 | El proyecto `js-challenge` está bajo la licencia [MIT](https://opensource.org/licenses/MIT). ¡Úsalo con confianza! 130 | --------------------------------------------------------------------------------