diff --git a/src/App.css b/src/App.css index 74b5e05..fe8e2a0 100644 --- a/src/App.css +++ b/src/App.css @@ -1,38 +1,38 @@ -.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: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} +.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: #61dafb; +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/src/App.js b/src/App.js index 3784575..c732f28 100644 --- a/src/App.js +++ b/src/App.js @@ -1,25 +1,13 @@ -import logo from './logo.svg'; -import './App.css'; - -function App() { - return ( -
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
-
- ); -} - -export default App; +import './App.css'; +import ProductsPage from "./components/ProductsPage"; + +function App() { + return ( +
+

Store

+ +
+ ); +} + +export default App; diff --git a/src/App.test.js b/src/App.test.js index 1f03afe..4de7037 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -1,8 +1,8 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); +import { render, screen } from '@testing-library/react'; +import App from './App'; + +test('renders learn react link', () => { + render(); + const linkElement = screen.getByText(/learn react/i); + expect(linkElement).toBeInTheDocument(); +}); diff --git a/src/components/ProductRow.js b/src/components/ProductRow.js new file mode 100644 index 0000000..3291f25 --- /dev/null +++ b/src/components/ProductRow.js @@ -0,0 +1,12 @@ +import React from "react"; + +function ProductRow({ product }) { + return ( + + {product.name} + {product.price} + + ); +} + +export default ProductRow; \ No newline at end of file diff --git a/src/components/ProductTable.js b/src/components/ProductTable.js new file mode 100644 index 0000000..07d0e40 --- /dev/null +++ b/src/components/ProductTable.js @@ -0,0 +1,27 @@ +import React from 'react' +import './css/style.css' + +function ProductTable({ products }) { + return ( + + + + + + + + + {products.map((product) => ( + + + + + ))} + +
NamePrice
{product.name}{product.price}
+ ); +} +export default ProductTable; \ No newline at end of file diff --git a/src/components/ProductsPage.js b/src/components/ProductsPage.js new file mode 100644 index 0000000..cb04acd --- /dev/null +++ b/src/components/ProductsPage.js @@ -0,0 +1,50 @@ +import React, { useState } from "react"; +import SearchBar from "./SearchBar"; +import ProductTable from "./ProductTable"; +import data from "../data.json"; +import "./css/style.css"; + +function ProductsPage() { + const [searchQuery, setSearchQuery] = useState(""); + const [onlyInStock, setOnlyInStock] = useState(false); + + const handleSearchChange = (query) => { + setSearchQuery(query); + }; + + const handleInStockChange = (event) => { + setOnlyInStock(event.target.checked); + }; + + const filteredProducts = data.filter((product) => { + const matchesSearchQuery = product.name + .toLowerCase() + .includes(searchQuery.toLowerCase()); + const matchesStockFilter = !onlyInStock || product.inStock; + return matchesSearchQuery && matchesStockFilter; + }); + + return ( +
+
Search
+ +
+ + +
+
+ +
+
+ ); +} + +export default ProductsPage; \ No newline at end of file diff --git a/src/components/SearchBar.js b/src/components/SearchBar.js new file mode 100644 index 0000000..f05dbbf --- /dev/null +++ b/src/components/SearchBar.js @@ -0,0 +1,16 @@ +import React from 'react' +import './css/style.css' + +function SearchBar({ searchQuery, onSearchChange }) { + return ( +
+ onSearchChange(e.target.value)} + /> +
+ ); +} + +export default SearchBar \ No newline at end of file diff --git a/src/components/style.css b/src/components/style.css new file mode 100644 index 0000000..d28c542 --- /dev/null +++ b/src/components/style.css @@ -0,0 +1,77 @@ +.table-wrapper { + display: flex; + justify-content: center; + align-items: center; + width: 1500px; + margin-left: 0px; + padding-left: 200px; + } + + table { + width: 100%; + max-width: 2000px; + border-collapse: collapse; + } + + table th:first-child + { + width: 100%; + padding-left: 0px; + padding-right: -300px; + } + + table th:nth-child(2) { + width: 300px; + margin-left: 500px; +} + + table th:first-child, + table th:nth-child(2) + { + background-color: rgb(209, 213, 222); + color: grey; + } + + table th, + table td { + padding-top: 20px; + padding-bottom: 20px; + padding-right: 300px; + text-align: center; + } + + td:first-child { + width: 300px; + padding-left: 0px; + } + + table th:nth-child(2) { + width: 150px; + padding-right: -200px; + } + + th:last-child, + .search-bar { + width: 100%; + margin-bottom: 20px; +} + +.search-bar input { + width: 80%; + padding: 8px; + box-sizing: border-box; +} + +div.search-title { + padding-bottom: 15px !important; +} + +.in-stock-filter { + padding-bottom: 50px; +} + + +.out-of-stock { + color: red; + +} diff --git a/src/data.json b/src/data.json index 3d95d98..2eb3dd0 100644 --- a/src/data.json +++ b/src/data.json @@ -1,11 +1,11 @@ -[ - {"category": "Sporting Goods", "price": "$49.99", "inStock": true, "name": "Football", "id": "295a4dab-74b2-4e60-b3c2-c1346aba7585"}, - {"category": "Sporting Goods", "price": "$9.99", "inStock": false, "name": "Basketball", "id": "6cf41052-7869-490f-9c2c-8f8efd2a4b5d"}, - {"category": "Electronics", "price": "$99.99", "inStock": true, "name": "iPod Touch", "id": "6fa4681a-61e1-4bf6-823a-24b2fe335543"}, - {"category": "Electronics", "price": "$199.99", "inStock": false, "name": "iPhone X", "id": "bbdabd03-0e02-4e7d-a7fc-ce52cc1164be"}, - {"category": "Sporting Goods", "price": "$9.99", "inStock": true, "name": "Tennis Balls", "id": "5358b8a4-fe62-4f7d-9a22-712be95a1f72"}, - {"category": "Electronics", "price": "$199.99", "inStock": true, "name": "Huawei P10", "id": "10858000-7894-4d77-bd0f-24639d111e74"}, - {"category": "Electronics", "price": "$199.99", "inStock": true, "name": "iPad Mini", "id": "a385a23f-07ed-4340-9ba7-937a0ce5f151"}, - {"category": "Sporting Goods", "price": "$9.99", "inStock": true, "name": "Baseball", "id": "4522f16e-3f55-4863-ae36-e935ec9cb4ef"} - ] +[ + {"category": "Sporting Goods", "price": "$49.99", "inStock": true, "name": "Football", "id": "295a4dab-74b2-4e60-b3c2-c1346aba7585"}, + {"category": "Sporting Goods", "price": "$9.99", "inStock": false, "name": "Basketball", "id": "6cf41052-7869-490f-9c2c-8f8efd2a4b5d"}, + {"category": "Electronics", "price": "$99.99", "inStock": true, "name": "iPod Touch", "id": "6fa4681a-61e1-4bf6-823a-24b2fe335543"}, + {"category": "Electronics", "price": "$199.99", "inStock": false, "name": "iPhone X", "id": "bbdabd03-0e02-4e7d-a7fc-ce52cc1164be"}, + {"category": "Sporting Goods", "price": "$9.99", "inStock": true, "name": "Tennis Balls", "id": "5358b8a4-fe62-4f7d-9a22-712be95a1f72"}, + {"category": "Electronics", "price": "$199.99", "inStock": true, "name": "Huawei P10", "id": "10858000-7894-4d77-bd0f-24639d111e74"}, + {"category": "Electronics", "price": "$199.99", "inStock": true, "name": "iPad Mini", "id": "a385a23f-07ed-4340-9ba7-937a0ce5f151"}, + {"category": "Sporting Goods", "price": "$9.99", "inStock": true, "name": "Baseball", "id": "4522f16e-3f55-4863-ae36-e935ec9cb4ef"} + ] \ No newline at end of file diff --git a/src/index.css b/src/index.css index ec2585e..bd5bd6d 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,13 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/src/index.js b/src/index.js index d563c0f..176fc7d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,17 +1,17 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -const root = ReactDOM.createRoot(document.getElementById('root')); -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/src/reportWebVitals.js b/src/reportWebVitals.js index 5253d3a..77957db 100644 --- a/src/reportWebVitals.js +++ b/src/reportWebVitals.js @@ -1,13 +1,13 @@ -const reportWebVitals = onPerfEntry => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; +const reportWebVitals = onPerfEntry => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/src/setupTests.js b/src/setupTests.js index 8f2609b..141e479 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -1,5 +1,5 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom';