Veamos una aplicación de carrito de compras que usa JSX como interfaz. Es una sintaxis similar a XML/HTML ampliamente utilizada por React que amplía ECMAScript y, por lo tanto, el texto similar a XML/HTML también se puede aplicar con código JavaScript/React.
Creación de la aplicación React e instalación de módulos:
Paso 1: Cree la aplicación de reacción usando
npx create-react-app <your foldername> Eg: npx create-react-app shoppingcart
Paso 2: Mover a la carpeta
cd shoppingcart
Paso 3: Instala las dependencias requeridas
npm install @babel/core npm i babel-runtime#Here i stands for install npm i @testing-library/jest-dom npm i @testing-library/react npm i @testing-library/user-event npm i autoprefixer npm i enzyme npm i enzyme-adapter-react-16 npm i react npm i react-dom npm i react-scripts
O, en lugar de hacerlo uno por uno, podemos especificar todo dentro del paquete.json como se indica a continuación y desde el símbolo del sistema podemos dar como:
npm install
Se encargará de instalar todos los paquetes que se mencionaron dentro de las dependencias. Los paquetes que se usaron se pueden verificar desde package.json:
paquete.json
{ "name": "shoppingcart", "version": "1.0.0", "description": "shoppingcart", "main": "app/main.jsx", "scripts": { "lint": "eslint 'app/**/*.@(js|jsx)'", "test": "react-scripts test", "start": "react-scripts start", "build": "react-scripts build", "eject": "react-scripts eject" }, "dependencies": { "babel-runtime": "~6.2.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "autoprefixer": "^9.8.0", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.5", "react": "^16.14.0", "react-dom": "^16.14.0", "react-scripts": "3.4.3" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "keywords": [ "react", "test", "enzyme" ], "pre-commit": [ "lint" ], "devDependencies": { "babel-eslint": "~4.1.6", "chai": "^3.4.1", "html-webpack-plugin": "^5.3.2", "react-addons-test-utils": "^15.4.1", "webpack": "^5.55.1", "webpack-cli": "^4.8.0", "webpack-dev-server": "^4.3.0", "jsdom": "^7.2.2" } }
Estructura de la carpeta del proyecto: El proyecto debería verse así:
Ejemplo: Comencemos el proyecto:
App.js
import React from 'react'; import './App.css'; // That means it is referring the jsx file // present under src/app/components folder import App1 from './app/components/App'; function App() { return ( <div className="App"> <App1 /> </div> ); } export default App;
App.css: para el embellecimiento del proyecto
App.css
.App { text-align: center; } .App-logo { height: 40vmin; pointer-events: none; } @media (prefers-reduced-motion: no-preference) { .App-logo { animation: App-logo-spin infinite 20s linear; } } .App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } .App-link { color: #006400; } @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .main-wrapper { display: flex; justify-content: center; } table { margin: 1rem; } table th td, table tr td { border: 1px solid black; border-collapse: collapse; } form { margin: 1rem; } input { line-height: 1.4rem; margin-left: 1rem; } h5 { display: block; } form button { line-height: 1.4rem; background-color: #ffffff; cursor: pointer; } .cities-wrapper { border: 1px solid black; margin: 1rem; padding: 1rem; align-items: flex-end; height: fit-content; } ul { float: center; margin-top: 0; } .center { margin-left: auto; margin-right: auto; } .shelf-wrapper { text-align: left; display: flex; justify-content: center; } .shelf-wrapper h4 { text-align: center; } .shelf-wrapper .shelf { width: 19%; border: 1px solid black; display: inline-block; min-height: 15rem; } .shelf span { /* width: 60%; */ } .shelf button { float: right; /* width: 40%; */ } .book-wrapper span { width: 100%; } .shelf table tr td { border: none; }
ItemJSON.js: como este proyecto no involucra ninguna base de datos, deje que los elementos se seleccionen de «ItemJSON.js». Está bajo src >> aplicación >> Elementos >> ItemJSON.js
ItemJSON.js
import { EventEmitter } from 'events'; import assign from 'object-assign'; // Initially specifying the constant items just as an sample const ProductStore = assign({}, EventEmitter.prototype, { items: { products: [ { productId: 0, productName: 'Samsung', productPrice: 10000, productQuantity: 2 }, { productId: 1, productName: 'Motorola', productPrice: 7000, productQuantity: 3 }, { productId: 2, productName: 'Redmi', productPrice: 8000, productQuantity: 4 }, ] }, nextproductId: 3, // To get all the items and display in the screen getAll: function getAll() { return this.items; }, emitChange: function emitChange() { this.emit('change'); }, // When an item is added addChangeListener: function addChangeListener(callback) { this.on('change', callback); }, // When an item is removed removeChangeListener: function removeChangeListener(callback) { this.removeListener('change', callback); }, addNewProducts: function addNewProducts(product) { const products = this.items.products; if (!products || typeof this.items.products.length !== 'number') { this.items.products = []; } product.productId = this.nextproductId++; product.done = false; this.items.products.push(product); }, deleteProducts: function deleteProducts(productId) { this.items.products = this.items.products.filter( (product) => product.productId !== productId); } }); export default ProductStore;
Inicie la aplicación: escriba el siguiente comando para iniciar la aplicación. El proyecto se inicia en el puerto 3000
npm start
Producción:
A continuación, el código requerido se puede encontrar en App.jsx
App.jsx
import React from 'react'; import AddItems from './AddItems'; import List from './List'; export default class App extends React.Component { render() { return ( <div> <h1>Available Products</h1> // List.jsx is enclosed <List /> <AddItems.jsx is enclosed <AddItems /> </div> ); } }
List.jsx: tenemos la opción de agregar los elementos y eliminarlos
List.jsx
import React from 'react'; import ItemJSON from '../Items/ItemJSON'; import ListItems from './ListItems'; export default class ProductList extends React.Component { constructor(props) { super(props); this.state = ItemJSON.getAll(); } componentDidMount() { ItemJSON.addChangeListener(this._onChange.bind(this)); } componentWillUnmount() { ItemJSON.removeChangeListener(this._onChange.bind(this)); } _onChange() { this.setState(ItemJSON.getAll()); } render() { const ListItemsList = this.state.products.map( product => { return ( <ListItems key={product.productId} product={product} /> ); }); return ( <center> // All the items present in // ItemJSON.js is displayed here <ul>{ListItemsList}</ul> </center> ); } }
Producción:
Al ingresar los detalles del producto y hacer clic en el botón «agregar», se produce la siguiente funcionalidad:
Javascript
import React from 'react'; import ItemJSON from '../Items/ItemJSON'; export default class AddItems extends React.Component { //will pick the added product and added addItems() { const newProductName = this.refs.product.value; const newPrice = this.refs.price.value; const newQuantity = this.refs.quantity.value; if (newProductName) { ItemJSON.addNewProducts({ productName: newProductName, productPrice: newPrice, productQuantity: newQuantity }); ItemJSON.emitChange(); this.refs.product.value = ''; this.refs.price.value = ''; this.refs.quantity.value = ''; } } render() { return ( <center> <div className="add-todo"> <table > <thead> <tr> <th>Product Name</th> <th>Price</th> <th>Quantity</th> <th>Action</th> </tr> </thead> <tbody> <tr> <td><input type="text" placeholder="Add Product Name" ref="product" /></td> <td><input type="text" placeholder="Add Price" ref="price" /></td> <td><input type="text" placeholder="Add Quantity" ref="quantity" /></td> <td><button className="add-button" onClick={this.addItems.bind(this)}> Add </button></td> </tr> </tbody> </table> </div> </center> ); } }
eliminación de un producto: Intentemos eliminar Redmi de la lista anterior. El código requerido para hacer la eliminación está en ListItems.jsx
ListItems.jsx
import React from 'react'; import ItemJSON from '../Items/ItemJSON'; export default class ListItems extends React.Component { // This code is meant for deletion deleteProduct(e) { e.preventDefault(); ItemJSON.deleteProducts(this.props.product.productId); ItemJSON.emitChange(); } render() { const product = this.props.product; return ( // displaying available products and it // is having delete action <li> <table border="3"> <tbody> <tr> <td width="100px"> <span className={`todo-text`} > {product.productName} </span> </td> <td width="100px"><span className={`todo-text`}> {product.productPrice}</span> </td> <td width="100px"><span className={`todo-text`}> {product.productQuantity}</span> </td> <td width="100px"><button className="delete" onClick={this.deleteProduct.bind(this)}> Delete </button> </td> </tr> </tbody> </table> </li> ); } }
Producción:
Podemos probar la funcionalidad del proyecto de la siguiente manera:
App.test.js
import React from 'react'; import Adapter from 'enzyme-adapter-react-16'; import { expect } from 'chai'; import { shallow, mount, configure } from 'enzyme'; import TestUtils from 'react-dom/test-utils'; import App from './app/components/App'; import jsdom from 'jsdom'; import { findDOMNode } from 'react-dom'; configure({ adapter: new Adapter() }); beforeAll(() => { global.fetch = jest.fn(); // window.fetch = jest.fn(); if running browser environment }); let wrapper; beforeEach(() => { wrapper = shallow(< App />, { disableLifecycleMethods: true }); }); afterEach(() => { wrapper.unmount(); }); if (typeof document === 'undefined') { global.document = jsdom.jsdom( '<!doctype html><html><body></body></html>'); global.window = document.defaultView; global.navigator = global.window.navigator; } describe('DOM Rendering', function () { it('Add functionality to add new products by clicking add', function () { const app = TestUtils.renderIntoDocument(<App />); const appDOM = findDOMNode(app); let productItemsLength = appDOM.querySelectorAll('.todo-text').length; let addInput = appDOM.querySelector('input'); addInput.value = 'Add item'; let addButton = appDOM.querySelector('.add-todo button'); TestUtils.Simulate.click(addButton); console.log(appDOM.querySelectorAll('.todo-text').length); expect(appDOM.querySelectorAll('.todo-text') .length).to.be.equal(productItemsLength + 3); // As after adding we will get additional value 3. }); }); describe('DOM Rendering', function () { it('On deleting, the item should get deleted', function () { const app = TestUtils.renderIntoDocument(<App />); let productItems = TestUtils .scryRenderedDOMComponentsWithTag(app, 'li'); let productLength = productItems.length; let deleteButton = productItems[0] .querySelector('button'); TestUtils.Simulate.click(deleteButton); let productItemsAfterClick = TestUtils .scryRenderedDOMComponentsWithTag(app, 'li'); expect(productItemsAfterClick.length) .to.equal(productLength - 1); }); }); describe('Enzyme Shallow', function () { it('App\'s title should be Available Products', function () { let app = shallow(<App />); expect(app.find('h1').text()) .to.equal('Available Products'); }); }); describe('Enzyme Mount', function () { it('Delete An Item', function () { let app = mount(<App />); let itemLength = app.find('li').length; app.find('button.delete').at(0).simulate('click'); expect(app.find('li').length).to.equal(itemLength - 1); }); });
El script de prueba se puede probar de la siguiente manera:
npm test
Producción:
Publicación traducida automáticamente
Artículo escrito por priyarajtt y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA