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 |
--------------------------------------------------------------------------------