From 856b8733bdf15ab8335e573fe50387ed12da2a07 Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Fri, 16 Dec 2022 19:37:38 -0500 Subject: [PATCH 01/19] Refactored redundant css and abstracted logic into variables --- src/components/BusinessCard/BusinessCard.jsx | 125 ++++++++++--------- src/components/Pagination/Pagination.jsx | 4 +- 2 files changed, 68 insertions(+), 61 deletions(-) diff --git a/src/components/BusinessCard/BusinessCard.jsx b/src/components/BusinessCard/BusinessCard.jsx index 5ab25ec9..25c8b982 100644 --- a/src/components/BusinessCard/BusinessCard.jsx +++ b/src/components/BusinessCard/BusinessCard.jsx @@ -6,14 +6,12 @@ import { Box, Typography, } from '@material-ui/core'; - import StarIcon from '@material-ui/icons/Star'; import LocationOnIcon from '@material-ui/icons/LocationOn'; import RateReviewIcon from '@material-ui/icons/RateReview'; import MoreVertIcon from '@material-ui/icons/MoreVert'; import PhoneIcon from '@material-ui/icons/Phone'; import LanguageIcon from '@material-ui/icons/Language'; - import useMediaQuery from '@material-ui/core/useMediaQuery'; import { withStyles } from '@material-ui/core/styles'; @@ -29,16 +27,13 @@ const styles = (theme) => ({ height: '100%', backgroundColor: '#FFFFFF', }, - searchContentContainer: { + cardParentContainer: { display: 'flex', padding: 24, }, imageContainer: { marginRight: 24, position: 'relative', - maxWidth: 254, - maxHeight: 184, - minHeight: 184, flexGrow: 1, overflow: 'hidden', }, @@ -48,6 +43,16 @@ const styles = (theme) => ({ objectFit: 'cover', borderRadius: '4px', }, + largeImageDimensions: { + maxWidth: 254, + maxHeight: 184, + minHeight: 184, + }, + smallImageDimensions: { + maxWidth: 127, + maxHeight: 92, + minHeight: 92, + }, contentContainer: { display: 'flex', flexDirection: 'column', @@ -56,7 +61,14 @@ const styles = (theme) => ({ businessTitle: { marginBottom: 0, letterSpacing: '-0.4px', - lineHeight: '28px', + height: '100%', + color: '#1E1131', + margin: 0, + flex: 1, + }, + mobileBusinessTitle: { + marginBottom: 0, + letterSpacing: '-0.4px', height: '100%', color: '#1E1131', margin: 0, @@ -83,6 +95,11 @@ const styles = (theme) => ({ fontWeight: 700, display: 'flex', }, + mobileTitleContainer: { + fontSize: '16px', + fontWeight: 700, + display: 'flex', + }, CTAcontainer: { borderTop: '1px solid #E5E5E5', paddingTop: '12px', @@ -104,6 +121,11 @@ const styles = (theme) => ({ display: 'flex', flexDirection: 'column', }, + mobileBottomContent: { + marginBottom: 10, + marginTop: 10, + marginLeft: 10, + }, ratingContainer: { display: 'flex', textDecoration: 'none', @@ -118,43 +140,39 @@ const styles = (theme) => ({ addressString: { textDecoration: 'underline', }, - mobileImageContainer: { - marginRight: 24, - position: 'relative', - maxWidth: 127, - maxHeight: 92, - minHeight: 92, - flexGrow: 1, - overflow: 'hidden', - }, - mobileTitleContainer: { - fontSize: '16px', - fontWeight: 700, - display: 'flex', - }, - mobileBusinessTitle: { - marginBottom: 0, - letterSpacing: '-0.4px', - height: '100%', - color: '#1E1131', - margin: 0, - flex: 1, - }, + hideElement: { display: 'none', }, - mobileBottomContent: { - marginBottom: 10, - marginTop: 10, - marginLeft: 10, - }, + mobileContainer: { display: 'flex', marginLeft: '5px', marginTop: '5px', }, + ctaSpacing: { + marginRight: 2, + }, + indexNumberMargins: { + marginRight: 5, + }, }); +const convertAddressToGoogleMapsLink = (businessAddress) => { + const googleAPIQuery = 'https://www.google.com/maps/dir/?api=1&destination='; + return googleAPIQuery + encodeURIComponent(businessAddress.address); +}; + +const formatTenDigitPhoneNumber = (phoneNumberString) => { + const cleaned = (`'' + ${phoneNumberString}`).replace(/\D/g, ''); + const match = cleaned.match(/^(1|)?(\d{3})(\d{3})(\d{4})$/); + if (match) { + const intlCode = (match[1] ? '+1 ' : ''); + return [intlCode, '(', match[2], ') ', match[3], '-', match[4]].join(''); + } + return 'No number found'; +}; + const BusinessCard = ({ business: { id, @@ -174,32 +192,21 @@ const BusinessCard = ({ }) => { const useDesktop = useMediaQuery('(min-width:838px)'); - const convertAddressToGoogleMapsLink = (businessAddress) => { - const googleAPIQuery = 'https://www.google.com/maps/dir/?api=1&destination='; - return googleAPIQuery + encodeURIComponent(businessAddress.address); - }; - - const formatTenDigitPhoneNumber = (phoneNumberString) => { - const cleaned = (`'' + ${phoneNumberString}`).replace(/\D/g, ''); - const match = cleaned.match(/^(1|)?(\d{3})(\d{3})(\d{4})$/); - if (match) { - const intlCode = (match[1] ? '+1 ' : ''); - return [intlCode, '(', match[2], ') ', match[3], '-', match[4]].join(''); - } - return 'No number found'; + const Image = () => { + const CardImage = imageUrl || 'https://as2.ftcdn.net/v2/jpg/04/70/29/97/1000_F_470299797_UD0eoVMMSUbHCcNJCdv2t8B2g1GVqYgs.jpg'; + const ImageDimension = useDesktop ? classes.largeImageDimensions : classes.smallImageDimensions; + return ( + + + business + + + ); }; - const Image = () => ( - - - alt-text - - - ); - return ( - + {useDesktop ? : null} @@ -207,7 +214,7 @@ const BusinessCard = ({

- + {count} . @@ -237,11 +244,11 @@ const BusinessCard = ({ - + Add Review - Rating + Rating {averageRating} diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 3a9b05d7..ee7faa2e 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -150,7 +150,7 @@ const Pagination = ({ const calculatePageRange = () => { const startingRange = (page - 1) * perPage + 1; const endingRange = (page) * perPage < totalCount ? (page) * perPage : totalCount; - return [startingRange, endingRange]; + return `Showing ${startingRange} - ${endingRange} of ${totalCount} results`; }; const calculatePaginationMenu = () => { @@ -181,7 +181,7 @@ const Pagination = ({ {(totalCount > 0) && ( - {`Showing ${calculatePageRange()[0]} - ${calculatePageRange()[1]} of ${totalCount} results`} + {calculatePageRange()} )} {(backExists || nextExists) From cd5f4c4f37bdec9fd683ce507c225f437a767c7b Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Fri, 16 Dec 2022 21:32:17 -0500 Subject: [PATCH 02/19] Refactored out the next and previous buttons for pagination --- src/components/Pagination/Pagination.jsx | 59 +++++++++++------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index ee7faa2e..841b1a25 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -85,6 +85,24 @@ const styles = () => ({ }, }); +const NextButton = ({ pageLink, classes }) => { + const color = pageLink === '' ? classes.inactiveColor : classes.activeColor; + return ( + + {'>'} + + ); +}; + +const BackButton = ({ pageLink, classes }) => { + const color = pageLink === '' ? classes.inactiveColor : classes.activeColor; + return ( + + {'<'} + + ); +}; + const Pagination = ({ totalCount, page, @@ -103,37 +121,22 @@ const Pagination = ({ backExists = true; query.set('page', page - 1); query.set('perPage', perPage); - backButton = ( - - {'<'} - - ); - } else { - backButton = ( -

- {'<'} -
- ); + backButton = `${backLink}?${query.toString()}`; } - if (totalPages > 1 && page < totalPages) { const nextLink = history.location.pathname; nextExists = true; query.set('page', page + 1); query.set('perPage', perPage); - nextButton = ( - - {'>'} - - ); - } else { - nextButton = ( -
- {'>'} -
- ); + nextButton = `${nextLink}?${query.toString()}`; } + const calculatePageRange = () => { + const startingRange = (page - 1) * perPage + 1; + const endingRange = (page) * perPage < totalCount ? (page) * perPage : totalCount; + return `Showing ${startingRange} - ${endingRange} of ${totalCount} results`; + }; + const paginationButton = (link, pageNumber, isActive) => (
@@ -147,12 +150,6 @@ const Pagination = ({ ); - const calculatePageRange = () => { - const startingRange = (page - 1) * perPage + 1; - const endingRange = (page) * perPage < totalCount ? (page) * perPage : totalCount; - return `Showing ${startingRange} - ${endingRange} of ${totalCount} results`; - }; - const calculatePaginationMenu = () => { const menu = [page]; const nextPage = page + 1; @@ -189,7 +186,7 @@ const Pagination = ({
- {backButton} +
{calculatePaginationMenu().map((pages) => ( @@ -197,7 +194,7 @@ const Pagination = ({ ))}
- {nextButton} +
From 3d66ceed3a7b1887b2e788d8b25eaa2a9c263b0d Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Fri, 16 Dec 2022 21:48:09 -0500 Subject: [PATCH 03/19] Refactoring more redundant classes --- src/components/Pagination/Pagination.jsx | 41 ++++++------------------ 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 841b1a25..a0d3dd16 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -38,18 +38,7 @@ const styles = () => ({ justifyContent: 'flex-start', whiteSpace: 'no wrap', }, - paginationLinkContainers: { - display: 'flex', - alignItems: 'center', - height: '30px', - justifyContent: 'center', - minWidth: '20px', - }, - paginationLink: { - padding: '0.6px', - }, paginationButton: { - backgroundColor: '#633AA3', height: '28px', width: '28px', display: 'flex', @@ -57,13 +46,12 @@ const styles = () => ({ alignItems: 'center', margin: '0 2px', }, - activeButton: { - backgroundColor: '#633AA3', - color: '#FFFFFF', - }, - inactiveButton: { + activeColor: { color: '#666666', }, + inactiveColor: { + color: '#E5E5E5', + }, pageInputNavigation: { border: '1px solid #666666', width: '40px', @@ -137,16 +125,11 @@ const Pagination = ({ return `Showing ${startingRange} - ${endingRange} of ${totalCount} results`; }; - const paginationButton = (link, pageNumber, isActive) => ( - -
- - {pageNumber} - -
+ const paginationButton = (link, pageNumber) => ( +
+ + {pageNumber} + ); @@ -185,15 +168,11 @@ const Pagination = ({ && (
-
+
-
-
{calculatePaginationMenu().map((pages) => ( paginationButton('/', pages, true) ))} -
-
From 486fbee09759f75c7955575d76b9c870f7e4148d Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Fri, 16 Dec 2022 22:03:35 -0500 Subject: [PATCH 04/19] Abstracted out the showing page range logic --- src/components/Pagination/Pagination.jsx | 69 +++++++++++++----------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index a0d3dd16..9265ed2e 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -32,12 +32,6 @@ const styles = () => ({ display: 'flex', flexDirection: 'column', }, - paginationContainer: { - display: 'flex', - alignItems: 'center', - justifyContent: 'flex-start', - whiteSpace: 'no wrap', - }, paginationButton: { height: '28px', width: '28px', @@ -60,7 +54,7 @@ const styles = () => ({ marginLeft: '8px', backgroundColor: '#FFFFFF', }, - pageInputContainer: { + goToPageContainer: { backgroundColor: '#F2F2F2', boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)', borderRadius: '4px', @@ -83,7 +77,7 @@ const NextButton = ({ pageLink, classes }) => { }; const BackButton = ({ pageLink, classes }) => { - const color = pageLink === '' ? classes.inactiveColor : classes.activeColor; + const color = pageLink === '' ? [classes.inactiveColor] : [classes.activeColor]; return ( {'<'} @@ -91,6 +85,29 @@ const BackButton = ({ pageLink, classes }) => { ); }; +const GoToPage = ({ classes, totalPages }) => { + const label = 'Go to page'; + return ( +
+ {label} + +
+ ); +}; + +const RangeOfResults = ({ classes, totalCount, calculateRange }) => ( + (totalCount > 0) + && ( + + {calculateRange} + + ) +); + const Pagination = ({ totalCount, page, @@ -119,7 +136,7 @@ const Pagination = ({ nextButton = `${nextLink}?${query.toString()}`; } - const calculatePageRange = () => { + const CalculatePageRange = () => { const startingRange = (page - 1) * perPage + 1; const endingRange = (page) * perPage < totalCount ? (page) * perPage : totalCount; return `Showing ${startingRange} - ${endingRange} of ${totalCount} results`; @@ -158,32 +175,22 @@ const Pagination = ({ return (
- {(totalCount > 0) - && ( - - {calculatePageRange()} - - )} + {(backExists || nextExists) && (
-
-
- - {calculatePaginationMenu().map((pages) => ( - paginationButton('/', pages, true) - ))} - -
-
-
- Go to page - +
+ + {calculatePaginationMenu().map((pages) => ( + paginationButton('/', pages, true) + ))} +
+
)}
From 6817d7998ba4cc182ecc76fb0e99d326826a4464 Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Fri, 16 Dec 2022 22:43:48 -0500 Subject: [PATCH 05/19] Abstracted pagination buttons into a parent function --- src/components/Pagination/Pagination.jsx | 54 +++++++++++++++--------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 9265ed2e..5cdaa3f4 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -65,6 +65,9 @@ const styles = () => ({ alignItems: 'center', color: '#000000', }, + paginationListContainer: { + display: 'flex', + }, }); const NextButton = ({ pageLink, classes }) => { @@ -108,6 +111,25 @@ const RangeOfResults = ({ classes, totalCount, calculateRange }) => ( ) ); +const PaginationButton = ({ pageNumber, classes }) => { + const calculateLink = '/'; + return ( + <> +
+
+ {pageNumber} +
+
+ + ); +}; + +const RenderPaginationButtons = ({ pagesToRender, classes }) => ( + pagesToRender.map((page) => ( + + )) +); + const Pagination = ({ totalCount, page, @@ -142,14 +164,6 @@ const Pagination = ({ return `Showing ${startingRange} - ${endingRange} of ${totalCount} results`; }; - const paginationButton = (link, pageNumber) => ( - - - {pageNumber} - - - ); - const calculatePaginationMenu = () => { const menu = [page]; const nextPage = page + 1; @@ -180,19 +194,19 @@ const Pagination = ({ classes={classes} calculateRange={CalculatePageRange()} /> - {(backExists || nextExists) - && ( -
-
- - {calculatePaginationMenu().map((pages) => ( - paginationButton('/', pages, true) - ))} - -
- + {(backExists || nextExists) && ( +
+
+ + +
- )} + +
+ )}
); From c7c5238a043f3b2f55c4d0d55cd986dd0f1d2c1e Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Fri, 16 Dec 2022 23:16:12 -0500 Subject: [PATCH 06/19] Refactored paginationMenu logic to be more readable --- src/components/Pagination/Pagination.jsx | 40 ++++++++++-------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 5cdaa3f4..f93d8c5f 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -111,7 +111,10 @@ const RangeOfResults = ({ classes, totalCount, calculateRange }) => ( ) ); -const PaginationButton = ({ pageNumber, classes }) => { +const PaginationButton = ({ pageNumber, classes, goToPageLabel }) => { + if (pageNumber === goToPageLabel) { + return
...
; + } const calculateLink = '/'; return ( <> @@ -124,9 +127,9 @@ const PaginationButton = ({ pageNumber, classes }) => { ); }; -const RenderPaginationButtons = ({ pagesToRender, classes }) => ( - pagesToRender.map((page) => ( - +const RenderPaginationButtons = ({ pagesToRender, classes, goToPageLabel }) => ( + pagesToRender.filter((page) => page !== null).map((page) => ( + )) ); @@ -143,6 +146,7 @@ const Pagination = ({ let backExists; let nextExists; let nextButton; + const labelForGoToPage = '...'; if (totalPages > 1 && page > 1) { const backLink = history.location.pathname; backExists = true; @@ -165,25 +169,14 @@ const Pagination = ({ }; const calculatePaginationMenu = () => { - const menu = [page]; - const nextPage = page + 1; - const prevPage = page - 1; - - if ((totalPages > 1 && page > 1)) { - menu.unshift(prevPage); - } - - if ((totalPages > 1 && page < totalPages) === false) { - return menu; - } - - if (page + 3 <= totalPages) { - return menu.concat([nextPage, '...', totalPages]); - } - if (page + 2 > totalPages) { - return menu.concat([nextPage]); - } - return menu.concat([nextPage, nextPage + 1]); + const pagesToRender = { + prevPage: ((totalPages > 1 && page > 1) ? page - 1 : null), + nextPage: ((page + 1 >= totalPages) ? page + 1 : null), + nextNextPage: ((page + 2 === totalPages) ? page + 2 : null), + ellipsis: ((page + 3 <= totalPages) ? labelForGoToPage : null), + lastPage: ((totalPages !== page) ? totalPages : null), + }; + return Object.values(pagesToRender); }; return ( @@ -201,6 +194,7 @@ const Pagination = ({
From 6e5ab0d1f0cc6202c89401e07786c6e3d6bf8fff Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Fri, 16 Dec 2022 23:21:41 -0500 Subject: [PATCH 07/19] Fixed a bug regarding which numbers were rendered in pagination menu --- src/components/Pagination/Pagination.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index f93d8c5f..4aace5b4 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -171,8 +171,9 @@ const Pagination = ({ const calculatePaginationMenu = () => { const pagesToRender = { prevPage: ((totalPages > 1 && page > 1) ? page - 1 : null), - nextPage: ((page + 1 >= totalPages) ? page + 1 : null), - nextNextPage: ((page + 2 === totalPages) ? page + 2 : null), + currPage: page, + nextPage: ((page + 1 < totalPages) ? page + 1 : null), + nextNextPage: ((page + 2 < totalPages) ? page + 2 : null), ellipsis: ((page + 3 <= totalPages) ? labelForGoToPage : null), lastPage: ((totalPages !== page) ? totalPages : null), }; From 1bd557624271ed9cfdafc264835db21f43e80cc7 Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 00:24:20 -0500 Subject: [PATCH 08/19] Pagination will now center during mobile sized screens --- src/components/Pagination/Pagination.jsx | 36 +++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 4aace5b4..71d3a7cc 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -3,9 +3,10 @@ import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; import Input from '@material-ui/core/Input'; - +import useMediaQuery from '@material-ui/core/useMediaQuery'; import { withStyles } from '@material-ui/core/styles'; import { Typography } from '@material-ui/core'; + import useQuery from '../../hooks/useQuery'; const styles = () => ({ @@ -23,6 +24,15 @@ const styles = () => ({ color: '#666666', fontSize: '16px', }, + mobilePrompt: { + display: 'flex', + flexGrow: 2, + width: '100%', + marginTop: '40px', + color: '#666666', + fontSize: '16px', + flexDirection: 'column', + }, showingText: { flexGrow: 1, justifyContent: 'center', @@ -32,6 +42,11 @@ const styles = () => ({ display: 'flex', flexDirection: 'column', }, + mobileNavigationContainer: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }, paginationButton: { height: '28px', width: '28px', @@ -68,6 +83,10 @@ const styles = () => ({ paginationListContainer: { display: 'flex', }, + mobilePaginationListContainer: { + display: 'flex', + marginTop: 44, + }, }); const NextButton = ({ pageLink, classes }) => { @@ -139,6 +158,7 @@ const Pagination = ({ perPage, classes, }) => { + const useDesktop = useMediaQuery('(min-width:838px)'); const query = useQuery(); const history = useHistory(); const totalPages = Math.ceil(totalCount / perPage); @@ -146,6 +166,13 @@ const Pagination = ({ let backExists; let nextExists; let nextButton; + + const paginationListClasses = useDesktop + ? classes.paginationListContainer : classes.mobilePaginationListContainer; + const navigationContainerClasses = useDesktop + ? classes.navigationContainer : classes.mobileNavigationContainer; + const promptClasses = useDesktop ? classes.prompt : classes.mobilePrompt; + const labelForGoToPage = '...'; if (totalPages > 1 && page > 1) { const backLink = history.location.pathname; @@ -182,15 +209,15 @@ const Pagination = ({ return (
-
+
{(backExists || nextExists) && ( -
-
+
+
Date: Sat, 17 Dec 2022 13:27:15 -0500 Subject: [PATCH 09/19] Align showing x of y results label absolute center relative to container --- src/components/Pagination/Pagination.jsx | 61 +++++++++++++++--------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 71d3a7cc..f39bf49a 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -1,3 +1,5 @@ +/* eslint-disable no-unused-vars */ + import React from 'react'; import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; @@ -41,18 +43,20 @@ const styles = () => ({ navigationContainer: { display: 'flex', flexDirection: 'column', + alignItems: 'flex-end', + flex: 1, }, mobileNavigationContainer: { display: 'flex', flexDirection: 'column', alignItems: 'center', + flex: 1, }, paginationButton: { height: '28px', width: '28px', display: 'flex', justifyContent: 'center', - alignItems: 'center', margin: '0 2px', }, activeColor: { @@ -86,6 +90,13 @@ const styles = () => ({ mobilePaginationListContainer: { display: 'flex', marginTop: 44, + justifyContent: 'center', + }, + paginationLabel: { + textDecoration: 'none', + }, + alignLabelAbsoluteCenter: { + flex: 1, }, }); @@ -137,7 +148,7 @@ const PaginationButton = ({ pageNumber, classes, goToPageLabel }) => { const calculateLink = '/'; return ( <> - +
{pageNumber}
@@ -192,7 +203,8 @@ const Pagination = ({ const CalculatePageRange = () => { const startingRange = (page - 1) * perPage + 1; const endingRange = (page) * perPage < totalCount ? (page) * perPage : totalCount; - return `Showing ${startingRange} - ${endingRange} of ${totalCount} results`; + const resultString = endingRange <= 1 ? 'Result' : 'Results'; + return `Showing ${startingRange} - ${endingRange} of ${totalCount} ${resultString}`; }; const calculatePaginationMenu = () => { @@ -210,25 +222,30 @@ const Pagination = ({ return (
- - {(backExists || nextExists) && ( -
-
- - - -
- -
- )} +
+
+ +
+
+ {(backExists || nextExists) && ( + <> +
+ + + +
+ + + )} +
); From 8d423dce11352258f19d15f2c8f8e1d19cd0a877 Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 14:01:44 -0500 Subject: [PATCH 10/19] Removed underline and fixed a bug regarding colors for pagination buttons --- src/components/Pagination/Pagination.jsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index f39bf49a..a9905509 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -101,18 +101,20 @@ const styles = () => ({ }); const NextButton = ({ pageLink, classes }) => { - const color = pageLink === '' ? classes.inactiveColor : classes.activeColor; + const color = pageLink === undefined ? classes.inactiveColor : classes.activeColor; + const removeUnderline = classes.paginationLabel; return ( -
- {'>'} + + {'>'} ); }; const BackButton = ({ pageLink, classes }) => { - const color = pageLink === '' ? [classes.inactiveColor] : [classes.activeColor]; + const color = pageLink === undefined ? classes.inactiveColor : classes.activeColor; + const removeUnderline = classes.paginationLabel; return ( - + {'<'} ); From 8801a159e72b13e01278b9f6b7323c66a591011c Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 14:16:16 -0500 Subject: [PATCH 11/19] Expanded the margins between each button and extended width of next and back buttons --- src/components/Pagination/Pagination.jsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index a9905509..e0cbd781 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -94,18 +94,24 @@ const styles = () => ({ }, paginationLabel: { textDecoration: 'none', + margin: '0 2px', }, alignLabelAbsoluteCenter: { flex: 1, }, + expandNavigationButtons: { + width: '32px', + textAlign: 'center', + }, }); const NextButton = ({ pageLink, classes }) => { const color = pageLink === undefined ? classes.inactiveColor : classes.activeColor; const removeUnderline = classes.paginationLabel; + const nextClasses = `${color} ${removeUnderline} ${classes.expandNavigationButtons}`; return ( - - {'>'} + + {'>'} ); }; @@ -113,8 +119,9 @@ const NextButton = ({ pageLink, classes }) => { const BackButton = ({ pageLink, classes }) => { const color = pageLink === undefined ? classes.inactiveColor : classes.activeColor; const removeUnderline = classes.paginationLabel; + const backClasses = `${color} ${removeUnderline} ${classes.expandNavigationButtons}`; return ( - + {'<'} ); From 218736ea739cb40d4366c2b4de0595cc014d6e36 Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 14:33:00 -0500 Subject: [PATCH 12/19] Current page now displays with a purple highlight and white font --- src/components/Pagination/Pagination.jsx | 42 +++++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index e0cbd781..02deb0cf 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -103,6 +103,11 @@ const styles = () => ({ width: '32px', textAlign: 'center', }, + currentPageButton: { + color: '#FFFFFF', + backgroundColor: '#633AA3', + borderRadius: '2px', + }, }); const NextButton = ({ pageLink, classes }) => { @@ -150,14 +155,28 @@ const RangeOfResults = ({ classes, totalCount, calculateRange }) => ( ) ); -const PaginationButton = ({ pageNumber, classes, goToPageLabel }) => { +const GoToPageButton = ({ goToPageLabel }) => { + const onClick = null; + return ( +
{goToPageLabel}
+ ); +}; + +const PaginationButton = ({ + pageNumber, + classes, + goToPageLabel, + currPage, +}) => { if (pageNumber === goToPageLabel) { - return
...
; + return ; } const calculateLink = '/'; + const isCurrentPage = currPage === pageNumber ? classes.currentPageButton : ''; + const PaginationButtonClasses = `${classes.paginationLabel} ${isCurrentPage}`; return ( <> - +
{pageNumber}
@@ -166,9 +185,21 @@ const PaginationButton = ({ pageNumber, classes, goToPageLabel }) => { ); }; -const RenderPaginationButtons = ({ pagesToRender, classes, goToPageLabel }) => ( +const RenderPaginationButtons = ( + { + pagesToRender, + classes, + goToPageLabel, + currPage, + }, +) => ( pagesToRender.filter((page) => page !== null).map((page) => ( - + )) ); @@ -248,6 +279,7 @@ const Pagination = ({ pagesToRender={calculatePaginationMenu()} classes={classes} goToPageLabel={labelForGoToPage} + currPage={page} />
From 99da80fc465ef31dca01c2d91715b4b022f0de1f Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 14:47:09 -0500 Subject: [PATCH 13/19] Added spacing between Go to page and pagination --- src/components/Pagination/Pagination.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 02deb0cf..db3fc39d 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -83,6 +83,7 @@ const styles = () => ({ justifyContent: 'center', alignItems: 'center', color: '#000000', + marginTop: 8, }, paginationListContainer: { display: 'flex', @@ -100,7 +101,7 @@ const styles = () => ({ flex: 1, }, expandNavigationButtons: { - width: '32px', + width: '28px', textAlign: 'center', }, currentPageButton: { From 0f85e0f8b890cf313342218f73474257b50ffb74 Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 14:58:49 -0500 Subject: [PATCH 14/19] GoToPage now has a variable that determines if it's hidden or not --- src/components/Pagination/Pagination.jsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index db3fc39d..a365204f 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -109,6 +109,9 @@ const styles = () => ({ backgroundColor: '#633AA3', borderRadius: '2px', }, + hideDisplay: { + display: 'none', + }, }); const NextButton = ({ pageLink, classes }) => { @@ -135,8 +138,10 @@ const BackButton = ({ pageLink, classes }) => { const GoToPage = ({ classes, totalPages }) => { const label = 'Go to page'; + const ShowContent = false ? undefined : classes.hideDisplay; + const GoToPageClasses = `${classes.goToPageContainer} ${ShowContent}`; return ( -
+
{label} ( ) ); -const GoToPageButton = ({ goToPageLabel }) => { +const OpenGoToPageButton = ({ goToPageLabel }) => { const onClick = null; return (
{goToPageLabel}
@@ -170,7 +175,7 @@ const PaginationButton = ({ currPage, }) => { if (pageNumber === goToPageLabel) { - return ; + return ; } const calculateLink = '/'; const isCurrentPage = currPage === pageNumber ? classes.currentPageButton : ''; From 5fba8fa4909e93a2652f1487b33dc960533d46aa Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 16:50:46 -0500 Subject: [PATCH 15/19] Switching anchor tags to MUI links --- src/components/Pagination/Pagination.jsx | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index a365204f..29974273 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -7,7 +7,7 @@ import { useHistory } from 'react-router-dom'; import Input from '@material-ui/core/Input'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import { withStyles } from '@material-ui/core/styles'; -import { Typography } from '@material-ui/core'; +import { Typography, Link } from '@material-ui/core'; import useQuery from '../../hooks/useQuery'; @@ -119,9 +119,9 @@ const NextButton = ({ pageLink, classes }) => { const removeUnderline = classes.paginationLabel; const nextClasses = `${color} ${removeUnderline} ${classes.expandNavigationButtons}`; return ( -
+ {'>'} - + ); }; @@ -130,15 +130,15 @@ const BackButton = ({ pageLink, classes }) => { const removeUnderline = classes.paginationLabel; const backClasses = `${color} ${removeUnderline} ${classes.expandNavigationButtons}`; return ( - + {'<'} - + ); }; -const GoToPage = ({ classes, totalPages }) => { +const GoToPage = ({ classes, totalPages, showGoToPage }) => { const label = 'Go to page'; - const ShowContent = false ? undefined : classes.hideDisplay; + const ShowContent = showGoToPage ? undefined : classes.hideDisplay; const GoToPageClasses = `${classes.goToPageContainer} ${ShowContent}`; return (
@@ -168,6 +168,11 @@ const OpenGoToPageButton = ({ goToPageLabel }) => { ); }; +const getLinkFromPageNumber = ({ pageNumber }) => { + const link = null; + return link; +}; + const PaginationButton = ({ pageNumber, classes, @@ -182,11 +187,11 @@ const PaginationButton = ({ const PaginationButtonClasses = `${classes.paginationLabel} ${isCurrentPage}`; return ( <> - +
{pageNumber}
-
+ ); }; From 762ab0343a195acea5360a5b634d29d3e61d92d7 Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 17:37:48 -0500 Subject: [PATCH 16/19] 1, 2, 3 pagination.. etc buttons now redirect to the correct link --- src/components/Pagination/Pagination.jsx | 50 +++++++++++++----------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 29974273..912fb597 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -168,26 +168,21 @@ const OpenGoToPageButton = ({ goToPageLabel }) => { ); }; -const getLinkFromPageNumber = ({ pageNumber }) => { - const link = null; - return link; -}; - const PaginationButton = ({ pageNumber, classes, goToPageLabel, currPage, + link, }) => { if (pageNumber === goToPageLabel) { return ; } - const calculateLink = '/'; const isCurrentPage = currPage === pageNumber ? classes.currentPageButton : ''; const PaginationButtonClasses = `${classes.paginationLabel} ${isCurrentPage}`; return ( <> - +
{pageNumber}
@@ -204,16 +199,36 @@ const RenderPaginationButtons = ( currPage, }, ) => ( - pagesToRender.filter((page) => page !== null).map((page) => ( + pagesToRender.map((page) => ( )) ); +const calculatePaginationMenu = (totalPages, page, labelForGoToPage, history, query, perPage) => { + let pagesToRender = { + prevPage: ((totalPages > 1 && page > 1) ? page - 1 : null), + currPage: page, + nextPage: ((page + 1 < totalPages) ? page + 1 : null), + nextNextPage: ((page + 2 < totalPages) ? page + 2 : null), + ellipsis: ((page + 3 <= totalPages) ? labelForGoToPage : null), + lastPage: ((totalPages !== page) ? totalPages : null), + }; + pagesToRender = Object.values(pagesToRender).filter((pgNum) => pgNum !== null); + const pagesWithLinks = pagesToRender.map((pg) => { + const base = history.location.pathname; + query.set('page', pg); + query.set('perPage', perPage); + return { pageNumber: pg, link: `${base}?${query.toString()}` }; + }); + return pagesWithLinks; +}; + const Pagination = ({ totalCount, page, @@ -258,18 +273,6 @@ const Pagination = ({ return `Showing ${startingRange} - ${endingRange} of ${totalCount} ${resultString}`; }; - const calculatePaginationMenu = () => { - const pagesToRender = { - prevPage: ((totalPages > 1 && page > 1) ? page - 1 : null), - currPage: page, - nextPage: ((page + 1 < totalPages) ? page + 1 : null), - nextNextPage: ((page + 2 < totalPages) ? page + 2 : null), - ellipsis: ((page + 3 <= totalPages) ? labelForGoToPage : null), - lastPage: ((totalPages !== page) ? totalPages : null), - }; - return Object.values(pagesToRender); - }; - return (
@@ -287,7 +290,10 @@ const Pagination = ({
Date: Sat, 17 Dec 2022 19:27:21 -0500 Subject: [PATCH 17/19] The ... button now opens up the Go To Page prompt --- src/components/Pagination/Pagination.jsx | 53 ++++++++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 912fb597..9945fca9 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -1,13 +1,13 @@ /* eslint-disable no-unused-vars */ -import React from 'react'; +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; import Input from '@material-ui/core/Input'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import { withStyles } from '@material-ui/core/styles'; -import { Typography, Link } from '@material-ui/core'; +import { Button, Typography, Link } from '@material-ui/core'; import useQuery from '../../hooks/useQuery'; @@ -112,6 +112,19 @@ const styles = () => ({ hideDisplay: { display: 'none', }, + buttonOpenGoToPage: { + maxWidth: 28, + maxHeight: 28, + minWidth: 28, + minHeight: 28, + display: 'flex', + justifyContent: 'center', + margin: '0 2px', + }, + goToPageLabel: { + fontSize: 30, + fontWeight: 700, + }, }); const NextButton = ({ pageLink, classes }) => { @@ -136,9 +149,9 @@ const BackButton = ({ pageLink, classes }) => { ); }; -const GoToPage = ({ classes, totalPages, showGoToPage }) => { +const GoToPage = ({ classes, totalPages, showButton }) => { const label = 'Go to page'; - const ShowContent = showGoToPage ? undefined : classes.hideDisplay; + const ShowContent = showButton ? undefined : classes.hideDisplay; const GoToPageClasses = `${classes.goToPageContainer} ${ShowContent}`; return (
@@ -161,10 +174,21 @@ const RangeOfResults = ({ classes, totalCount, calculateRange }) => ( ) ); -const OpenGoToPageButton = ({ goToPageLabel }) => { - const onClick = null; +const OpenGoToPageButton = ({ goToPageLabel, setShowButton, classes }) => { + const [toggle, setToggle] = useState(true); + const GoToClasses = `${classes.activeColor} ${classes.buttonOpenGoToPage}`; return ( -
{goToPageLabel}
+ ); }; @@ -174,9 +198,16 @@ const PaginationButton = ({ goToPageLabel, currPage, link, + setShowButton, }) => { if (pageNumber === goToPageLabel) { - return ; + return ( + + ); } const isCurrentPage = currPage === pageNumber ? classes.currentPageButton : ''; const PaginationButtonClasses = `${classes.paginationLabel} ${isCurrentPage}`; @@ -197,6 +228,7 @@ const RenderPaginationButtons = ( classes, goToPageLabel, currPage, + setShowButton, }, ) => ( pagesToRender.map((page) => ( @@ -206,6 +238,7 @@ const RenderPaginationButtons = ( goToPageLabel={goToPageLabel} currPage={currPage} link={page.link} + setShowButton={setShowButton} /> )) ); @@ -236,6 +269,7 @@ const Pagination = ({ classes, }) => { const useDesktop = useMediaQuery('(min-width:838px)'); + const [showButton, setShowButton] = useState(false); const query = useQuery(); const history = useHistory(); const totalPages = Math.ceil(totalCount / perPage); @@ -297,10 +331,11 @@ const Pagination = ({ classes={classes} goToPageLabel={labelForGoToPage} currPage={page} + setShowButton={setShowButton} />
- + )}
From c9ec73caefba560425f5a8ea2881b36531f59279 Mon Sep 17 00:00:00 2001 From: ApplePieAngel <86702974+ApplePieAngel@users.noreply.github.com> Date: Sat, 17 Dec 2022 20:18:05 -0500 Subject: [PATCH 18/19] Implemented Go To Page functionality --- src/components/Pagination/Pagination.jsx | 54 ++++++++++++++++++------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index 9945fca9..b89b15a6 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -127,6 +127,13 @@ const styles = () => ({ }, }); +const getPageLink = (history, query, perPage, page) => { + const base = history.location.pathname; + query.set('page', page); + query.set('perPage', perPage); + return `${base}?${query.toString()}`; +}; + const NextButton = ({ pageLink, classes }) => { const color = pageLink === undefined ? classes.inactiveColor : classes.activeColor; const removeUnderline = classes.paginationLabel; @@ -149,18 +156,31 @@ const BackButton = ({ pageLink, classes }) => { ); }; -const GoToPage = ({ classes, totalPages, showButton }) => { +const GoToPage = ({ + classes, + totalPages, + showButton, + navigationObject, +}) => { + const [input, setInput] = useState(''); const label = 'Go to page'; const ShowContent = showButton ? undefined : classes.hideDisplay; const GoToPageClasses = `${classes.goToPageContainer} ${ShowContent}`; + const handleSubmit = (event) => { + event.preventDefault(); + navigationObject.history.push(getPageLink(...Object.values(navigationObject), input)); + }; return (
- {label} - +
+ {label} + setInput(event.target.value)} + /> +
); }; @@ -253,12 +273,9 @@ const calculatePaginationMenu = (totalPages, page, labelForGoToPage, history, qu lastPage: ((totalPages !== page) ? totalPages : null), }; pagesToRender = Object.values(pagesToRender).filter((pgNum) => pgNum !== null); - const pagesWithLinks = pagesToRender.map((pg) => { - const base = history.location.pathname; - query.set('page', pg); - query.set('perPage', perPage); - return { pageNumber: pg, link: `${base}?${query.toString()}` }; - }); + const pagesWithLinks = pagesToRender.map((pg) => ( + { pageNumber: pg, link: getPageLink(history, query, perPage, pg) } + )); return pagesWithLinks; }; @@ -307,6 +324,10 @@ const Pagination = ({ return `Showing ${startingRange} - ${endingRange} of ${totalCount} ${resultString}`; }; + const navigationObject = { + history, query, perPage, + }; + return (
@@ -335,7 +356,12 @@ const Pagination = ({ />
- + )}
From b2cdb8aa3c04be869312aed73e7686da9b068d28 Mon Sep 17 00:00:00 2001 From: Amy <86702974+ApplePieAngel@users.noreply.github.com> Date: Sun, 5 Mar 2023 16:08:18 -0600 Subject: [PATCH 19/19] DEV-97.2: Pagination, Card, Refactoring and Improvements (#81) * Fixed a bug where Rating wasn't inheriting the right color * Updated pagination with larger icons for the next, back buttons * Fixed a bug with Pagination where the buttons now render as gray * Fixed a bug with ToGoPageX, deleted unnecessary override * Realigned the buttons to be in the center of the purple box * Fixed bug with text overflowing the card component * Refactored out some of the CTA logic * Refactored some rendundant classes, address now links in a new tab * Added a lot of refactoring for redundant css * Added min height for CTA bar * Added additional margins to the Chips * Cleanly render images without the use of booleans * Added 16px margin around the card * DEV-97.3: Search Page Menus and Mobile Menus (#82) * Added components for search options, aligned text and icon * Renamed class names for readability, created parent component for Sort * Sort by bars now extend the length of the page on smaller dimensions * Refactoring component tree into smaller components * Header containing num of search results shrinks in smaller dimensions * Reduced the margin between cards and pagination * Sort bars now collapse at a smaller width * Added extra font weight to the rating CTA * Refactored some redudancy with props drilling * Added dropdown for share button * Fixed a bug regarding Google Maps and locations * Replaced hrefs with react router links prevent re-rendering * Added clear all button and refactored redundant component * Changed Clear All button to be the same size as filter buutton * Deleted unused css, added coloring and borders to the Clear All button * Refactored props composition with Menus * Fixed cards not extending the max length, changed some menu text * Added back the new menu labels for all the menus * Refactored components into one object to make it easier to edit * Phone number buttons no longer appear if there's no input * Align the filter and sort menus for the desktop view * Users can no longer go to the current page with pagination. * Menu now renders under the button rather than off to the side * Changed the Buttons to ListItems, it now changes and saves the current value. * Scrapped list items and implemented changing values with buttons * The selected menu item label has a bolding effect * Changed dropdown menu to gray and selected option is grayer with box shadow * Added additional line height to mobile menus * Adding initial test scaffolding * Adding initial test scaffolding * Added scaffolding for tests * Added scaffolding to business card tests. * Fixed no key in list error, fixed typo in className and fixed unused MUI element * Refactoring all class names to be camelCase * Added box shadow to apply box and removed bottom margin for mobile view * Removed unnecessary prop causing errors. * Removed empty tests, abstracted out pagination function * Added test cases for next button. * Added tests for check valid page number * Testing range of results * Added test cases for reviewCTA, addressRow, titleBar, ShareMenu, OrdinalNumber, formatPhone#, convertAdd.. * Added tests for business cards regarding CTA components * Added tests for SortByMenu * DEV-97.4: Add Report Problem Button in Results Page (#84) * Added SupportButton component from PR 74 * Opening Go To Page no longer adjusts the page height, added support button --- src/components/BusinessCard/BusinessCard.jsx | 462 +++++++++++------ .../BusinessCard/BusinessCard.test.jsx | 207 +++++++- src/components/FilterPanel/FilterPanel.jsx | 8 +- src/components/Pagination/Pagination.jsx | 140 +++-- src/components/Pagination/Pagination.test.jsx | 117 ++++- .../SupportButton/SupportButton.jsx | 87 ++++ src/components/SupportButton/SupportButton.md | 5 + .../SupportButton/SupportButton.test.jsx | 12 + src/components/SupportButton/index.js | 3 + src/routes/Search/Search.jsx | 482 +++++++++++++----- src/routes/Search/Search.test.jsx | 48 ++ 11 files changed, 1256 insertions(+), 315 deletions(-) create mode 100644 src/components/SupportButton/SupportButton.jsx create mode 100644 src/components/SupportButton/SupportButton.md create mode 100644 src/components/SupportButton/SupportButton.test.jsx create mode 100644 src/components/SupportButton/index.js create mode 100644 src/routes/Search/Search.test.jsx diff --git a/src/components/BusinessCard/BusinessCard.jsx b/src/components/BusinessCard/BusinessCard.jsx index 25c8b982..62657b79 100644 --- a/src/components/BusinessCard/BusinessCard.jsx +++ b/src/components/BusinessCard/BusinessCard.jsx @@ -1,12 +1,17 @@ /* eslint-disable no-unused-vars */ -import React, { useEffect, useState } from 'react'; +import React from 'react'; +import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import { Box, + Button, + Menu, + MenuItem, Typography, } from '@material-ui/core'; import StarIcon from '@material-ui/icons/Star'; +import ShareIcon from '@material-ui/icons/Share'; import LocationOnIcon from '@material-ui/icons/LocationOn'; import RateReviewIcon from '@material-ui/icons/RateReview'; import MoreVertIcon from '@material-ui/icons/MoreVert'; @@ -17,64 +22,42 @@ import { withStyles } from '@material-ui/core/styles'; import { spaceProps } from '../../types'; import ChipList from '../ChipList'; -import useQuery from '../../hooks/useQuery'; -const styles = (theme) => ({ +const styles = () => ({ root: { borderRadius: '4px', border: '1px solid #E5E5E5', - width: 'auto', + width: '100%', height: '100%', backgroundColor: '#FFFFFF', - }, - cardParentContainer: { - display: 'flex', - padding: 24, + overflow: 'hidden', }, imageContainer: { marginRight: 24, position: 'relative', flexGrow: 1, overflow: 'hidden', - }, - image: { - width: '100%', - height: '100%', - objectFit: 'cover', - borderRadius: '4px', - }, - largeImageDimensions: { maxWidth: 254, maxHeight: 184, minHeight: 184, }, - smallImageDimensions: { + imageContainerMobile: { maxWidth: 127, maxHeight: 92, minHeight: 92, + minWidth: 127, }, - contentContainer: { - display: 'flex', - flexDirection: 'column', - flexGrow: 1, - }, - businessTitle: { - marginBottom: 0, - letterSpacing: '-0.4px', + cardImage: { + width: '100%', height: '100%', - color: '#1E1131', - margin: 0, - flex: 1, + borderRadius: '4px', }, - mobileBusinessTitle: { - marginBottom: 0, + businessTitleContainer: { letterSpacing: '-0.4px', - height: '100%', color: '#1E1131', - margin: 0, flex: 1, }, - icon: { + addressIcon: { height: '100%', width: '23px', }, @@ -83,87 +66,82 @@ const styles = (theme) => ({ color: '#666666', marginBottom: '2px', }, - titleAddressContainer: { - display: 'flex', - flexDirection: 'column', - marginBottom: 8, + topCardContainer: { + marginBottom: 10, width: '100%', + display: 'flex', }, - titleContainer: { - height: '100%', + titleBarContainer: { fontSize: '18px', fontWeight: 700, display: 'flex', + justifyContent: 'center', + alignItems: 'center', }, - mobileTitleContainer: { + mobileTitleBar: { fontSize: '16px', - fontWeight: 700, + }, + titleAddressParentContainer: { display: 'flex', + flexDirection: 'column', + flex: 1, }, - CTAcontainer: { + ctaParentContainer: { borderTop: '1px solid #E5E5E5', paddingTop: '12px', + minHeight: '40px', }, - buttonContainer: { + ctaContainer: { display: 'flex', - alignItems: 'flex-end', - textAlign: 'center', maxWidth: '800px', }, - tagContainer: { - display: 'flex', - flexDirection: 'column', - flexGrow: 3, - maxWidth: '800px', - }, - bottomContent: { - height: '100%', - display: 'flex', - flexDirection: 'column', - }, - mobileBottomContent: { - marginBottom: 10, - marginTop: 10, - marginLeft: 10, - }, - ratingContainer: { + ctaButtonContainer: { display: 'flex', textDecoration: 'none', marginRight: 'auto', + color: '#666666', }, - purpleColor: { + purpleIcon: { color: '#633AA3', - }, - grayColor: { - color: '#666666', + fontWeight: 500, }, addressString: { textDecoration: 'underline', + color: '#666666', }, - - hideElement: { - display: 'none', + ordinalNumberMargins: { + margin: '0 5px', }, - - mobileContainer: { + bottomCardContainer: { + display: 'flex', + flexDirection: 'column', + flex: 1, + }, + chipList: { display: 'flex', - marginLeft: '5px', - marginTop: '5px', + flex: 1, + maxWidth: '800px', }, - ctaSpacing: { - marginRight: 2, + cardInformationContainer: { + flexGrow: 1, + display: 'flex', + flexDirection: 'column', }, - indexNumberMargins: { - marginRight: 5, + businessCardContainer: { + display: 'flex', + margin: 16, + }, + shareButton: { + color: 'black', }, }); const convertAddressToGoogleMapsLink = (businessAddress) => { const googleAPIQuery = 'https://www.google.com/maps/dir/?api=1&destination='; - return googleAPIQuery + encodeURIComponent(businessAddress.address); + return googleAPIQuery + encodeURIComponent(businessAddress); }; -const formatTenDigitPhoneNumber = (phoneNumberString) => { +const formatPhoneNumber = (phoneNumberString) => { const cleaned = (`'' + ${phoneNumberString}`).replace(/\D/g, ''); const match = cleaned.match(/^(1|)?(\d{3})(\d{3})(\d{4})$/); if (match) { @@ -173,6 +151,214 @@ const formatTenDigitPhoneNumber = (phoneNumberString) => { return 'No number found'; }; +const Image = ( + { + useDesktop, classes, id, imageUrl, showImage, + }, +) => { + if (showImage === false) { + return null; + } + const placeholderImage = 'https://as2.ftcdn.net/v2/jpg/04/70/29/97/1000_F_470299797_UD0eoVMMSUbHCcNJCdv2t8B2g1GVqYgs.jpg'; + const ImageSizing = useDesktop ? '' : classes.imageContainerMobile; + const CardImage = imageUrl || placeholderImage; + const LinkToSpace = `/spaces/${id}`; + return ( + + + business + + + ); +}; + +const TopCardContent = ( + { + classes, + count, + useDesktop, + name, + address, + imageUrl, + id, + }, +) => ( + + + + + + + +); + +const OrdinalNumber = ({ classes, count }) => { + const ordinalNumberString = `${count}.`; + return ( + + {ordinalNumberString} + + ); +}; + +const ShareMenu = ({ classes }) => { + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + return ( + <> + + + + + Share + + + + ); +}; + +const TitleBar = ({ + classes, + useDesktop, + count, + businessName, +}) => { + const smallerFont = useDesktop ? '' : classes.mobileTitleBar; + const ContainerClasses = `${classes.titleBarContainer} ${smallerFont}`; + return ( + + + + {businessName} + + + + ); +}; + +const AddressRow = ({ classes, address }) => { + const AddressLink = convertAddressToGoogleMapsLink(address); + return ( + <> + + + + {address} + + +
+ + ); +}; + +const AddReviewCTA = ({ id, classes }) => { + const spacesUrl = `/spaces/${id}/reviews/new`; + return ( + + + Add Review + + ); +}; + +const RatingCTA = ({ classes, averageRating }) => { + const label = 'Rating'; + return ( + + {label} + + {averageRating} + + ); +}; + +const CallPhoneCTA = ({ phoneNumber, classes, useDesktop }) => { + if (phoneNumber === '' || phoneNumber === undefined) { + return null; + } + const phoneString = `tel:${phoneNumber}`; + const label = ({formatPhoneNumber(phoneNumber)}); + const displayLabelOnDesktop = useDesktop ? label : null; + return ( + + + {displayLabelOnDesktop} + + ); +}; + +const VisitWebsiteCTA = ({ classes, useDesktop }) => { + const label = useDesktop ? Visit Website : null; + return ( + + + {label} + + ); +}; + +const CTAs = ( + { + classes, id, averageRating, phoneNumber, useDesktop, + }, +) => ( + + + + + + + + +); + +const BottomCardContent = ( + { + classes, id, averageRating, phoneNumber, useDesktop, filters, + }, +) => ( + + + + + + +); + const BusinessCard = ({ business: { id, @@ -187,86 +373,38 @@ const BusinessCard = ({ url, }, classes, - overrideClasses, count, }) => { const useDesktop = useMediaQuery('(min-width:838px)'); - const Image = () => { - const CardImage = imageUrl || 'https://as2.ftcdn.net/v2/jpg/04/70/29/97/1000_F_470299797_UD0eoVMMSUbHCcNJCdv2t8B2g1GVqYgs.jpg'; - const ImageDimension = useDesktop ? classes.largeImageDimensions : classes.smallImageDimensions; - return ( - - - business - - - ); - }; - return ( - - {useDesktop ? : null} - - - {useDesktop ? null : } - - -

- - {count} - . - - {name} -

- -
- - - - - {address} - - - -
-
- - - - - - - - - Add Review - - - Rating - - {averageRating} - - - - {useDesktop ? ( - - {formatTenDigitPhoneNumber(phoneNumber)} - - ) : null} - - - - {useDesktop ? Visit Website : null} - - - - + + + + +
@@ -284,3 +422,21 @@ BusinessCard.defaultProps = { }; export default withStyles(styles)(BusinessCard); + +// eslint-disable-next-line import/no-mutable-exports +export let Tests = { + convertAddressToGoogleMapsLink, + formatPhoneNumber, + OrdinalNumber, + ShareMenu, + TitleBar, + AddressRow, + AddReviewCTA, + RatingCTA, + CallPhoneCTA, + VisitWebsiteCTA, +}; + +if (process.env.NODE_ENV !== 'test') { + Tests = undefined; +} diff --git a/src/components/BusinessCard/BusinessCard.test.jsx b/src/components/BusinessCard/BusinessCard.test.jsx index c8d59cb4..b65d24ac 100644 --- a/src/components/BusinessCard/BusinessCard.test.jsx +++ b/src/components/BusinessCard/BusinessCard.test.jsx @@ -1,10 +1,210 @@ +/* eslint-disable no-unused-vars */ + import React from 'react'; -import { render } from '@testing-library/react'; -// eslint-disable-next-line no-unused-vars +import { getByText, render, screen } from '@testing-library/react'; +import { Share } from '@material-ui/icons'; +import userEvent from '@testing-library/user-event'; +import { BrowserRouter } from 'react-router-dom'; +import BusinessCard, { Tests } from './BusinessCard'; import MockChipList from '../ChipList'; -import BusinessCard from './BusinessCard'; jest.mock('../ChipList', () => () => (<>)); + +const { + convertAddressToGoogleMapsLink, + formatPhoneNumber, + OrdinalNumber, + ShareMenu, + TitleBar, + AddressRow, + AddReviewCTA, + RatingCTA, + CallPhoneCTA, + VisitWebsiteCTA, +} = Tests; + +describe('BusinessCard', () => { + describe('convertAddressToGoogleMapsLink', () => { + const googleAPIQuery = 'https://www.google.com/maps/dir/?api=1&destination='; + it('It should return a link to Google Maps that is URL encoded', () => { + const googleMapsLink = convertAddressToGoogleMapsLink('1701 Pennsylvania Ave NW Washington, DC DC 20006'); + const stringToMatch = `${googleAPIQuery}1701%20Pennsylvania%20Ave%20NW%20Washington%2C%20DC%20DC%2020006`; + expect(googleMapsLink).toBe(stringToMatch); + }); + it('It should return a different link to Google Maps that is URL encoded', () => { + const googleMapsLink = convertAddressToGoogleMapsLink('651 Florida Ave NW Washington, DC DC 20001'); + const stringToMatch = `${googleAPIQuery}651%20Florida%20Ave%20NW%20Washington%2C%20DC%20DC%2020001`; + expect(googleMapsLink).toBe(stringToMatch); + }); + }); + describe('formatPhoneNumber', () => { + it('It should return a string saying no phone number found if no phone number is passed in', () => { + const phoneNumber = formatPhoneNumber(undefined); + expect(phoneNumber).toBe('No number found'); + }); + }); + describe('OrdinalNumber', () => { + const dummyProps = { + classes: {}, + count: 1, + }; + it('It should take an integer value and add a . in front of it', () => { + render(); + expect(screen.getByText('1.')).toBeInTheDocument(); + }); + }); + describe('ShareMenu', () => { + it('It should open up a menu item that has the label share on click', () => { + render(); + const shareButton = screen.getByTestId('share-button'); + expect(screen.queryByText('Share')).not.toBeInTheDocument(); + + userEvent.click(shareButton); + expect(screen.getByText('Share')).toBeInTheDocument(); + }); + }); + describe('TitleBar', () => { + const dummyProps = { + classes: {}, + useDesktop: true, + count: 5, + businessName: 'Free Newspapers for $10.99', + }; + it('It should render a business name', () => { + render(); + expect(screen.getByText('Free Newspapers for $10.99')).toBeInTheDocument(); + }); + it('It should render an ordinal number', () => { + render(); + expect(screen.getByText('5.')).toBeInTheDocument(); + }); + }); + describe('AddressRow', () => { + const dummyProps = { + classes: {}, + address: '1701 Pennsylvania Ave NW Washington, DC DC 20006', + }; + it('It should have an icon', () => { + render(); + const icon = screen.getByTestId('location-icon'); + expect(icon).toBeInTheDocument(); + }); + it('It should have the address label', () => { + render(); + const address = screen.getByText('1701 Pennsylvania Ave NW Washington, DC DC 20006'); + expect(address).toBeInTheDocument(); + }); + it('It should have an href value of google maps', () => { + render(); + const addressLink = screen.getByRole('link'); + expect(addressLink).toHaveAttribute('href', 'https://www.google.com/maps/dir/?api=1&destination=1701%20Pennsylvania%20Ave%20NW%20Washington%2C%20DC%20DC%2020006'); + }); + }); + describe('AddReviewCTA', () => { + const dummyProps = { + classes: {}, + id: 1, + }; + const renderReviewCTA = () => { + render( + + + , + ); + }; + it('It should have a href of /spaces/id/reviews/new', () => { + renderReviewCTA(); + expect(screen.getByRole('link')).toHaveAttribute('href', '/spaces/1/reviews/new'); + }); + it('It should have a correct label', () => { + renderReviewCTA(); + expect(screen.getByText('Add Review')).toBeInTheDocument(); + }); + it('It should have an icon', () => { + renderReviewCTA(); + expect(screen.getByTestId('review-icon')).toBeInTheDocument(); + }); + }); + describe('RatingCTA', () => { + const dummyProps = { + classes: {}, + averageRating: 4.1, + }; + it('It should have a correct label', () => { + render(); + const label = screen.getByText('Rating'); + expect(label).toBeInTheDocument(); + }); + it('It should display the average rating', () => { + render(); + expect(screen.getByText('4.1')).toBeInTheDocument(); + }); + }); + describe('CallPhoneCTA', () => { + const dummyProps = { + classes: {}, + phoneNumber: '521-742-1234', + useDesktop: true, + }; + it('It should not render if no phone number is provided', () => { + render(); + expect(screen.queryByTestId('phone-icon')).not.toBeInTheDocument(); + }); + it('It should render the phone number after formatting', () => { + render(); + expect(screen.getByText('521', { exact: false })).toBeInTheDocument(); + }); + it('It should have a icon if rendered with a phone number', () => { + render(); + expect(screen.getByTestId('phone-icon')).toBeInTheDocument(); + }); + const smallWidthProps = { + classes: {}, + phoneNumber: '521-742-1234', + useDesktop: false, + }; + it('It should not render the phone number if the screen width is too small', () => { + render(); + expect(screen.queryByText('521', { exact: false })).not.toBeInTheDocument(); + }); + it('It should render the icon even if the screen width is too small for the string phone number', () => { + render(); + expect(screen.getByTestId('phone-icon')).toBeInTheDocument(); + }); + }); + describe('VisitWebsiteCTA', () => { + const dummyProps = { + classes: {}, + useDesktop: true, + }; + it('It should have a correct label for large dimensions', () => { + render(); + const label = screen.getByText('Visit Website'); + expect(label).toBeInTheDocument(); + }); + it('It should render an icon', () => { + render(); + const icon = screen.getByTestId('visit-website-icon'); + expect(icon).toBeInTheDocument(); + }); + const smallWidthProps = { + classes: {}, + useDesktop: false, + }; + it('It should not render the label for smaller dimensions', () => { + render(); + const label = screen.queryByText('Visit Website'); + expect(label).not.toBeInTheDocument(); + }); + it('It should still render the icon for smaller dimensions', () => { + render(); + const icon = screen.getByTestId('visit-website-icon'); + expect(icon).toBeInTheDocument(); + }); + }); +}); + +/* test('renders the BusinessCardComponent', () => { const props = { id: '123', @@ -47,3 +247,4 @@ test('renders the BusinessCardComponent', () => { const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); +*/ diff --git a/src/components/FilterPanel/FilterPanel.jsx b/src/components/FilterPanel/FilterPanel.jsx index ab0163c7..fad5c21f 100644 --- a/src/components/FilterPanel/FilterPanel.jsx +++ b/src/components/FilterPanel/FilterPanel.jsx @@ -56,6 +56,7 @@ const styles = { }, alignItems: 'baseline', maxHeight: '80px', + marginTop: '5px', marginBottom: '5px', }, nameFilter: { @@ -120,6 +121,10 @@ const styles = { }, marginBottom: '188px', }, + applyMobile: { + boxShadow: '0px 0px 4px rgba(0, 0, 0, 0.25)', + marginBottom: 0, + }, resultCount: { display: 'inline-block', marginBottom: '20px', @@ -294,7 +299,6 @@ const FilterPanel = ({ placeholder="Search by name" value={nameFilterVal} InputProps={{ - disableUnderline: true, padding: 5, classes: { borderRadius: 0 }, endAdornment: ( @@ -458,7 +462,7 @@ const FilterPanel = ({ {priceFilter} {indicatorFilters}
-
+
{`${resultCount} Search Result${resultCount === 1 ? '' : 's'}`} diff --git a/src/components/Pagination/Pagination.jsx b/src/components/Pagination/Pagination.jsx index b89b15a6..d72fe185 100644 --- a/src/components/Pagination/Pagination.jsx +++ b/src/components/Pagination/Pagination.jsx @@ -1,5 +1,3 @@ -/* eslint-disable no-unused-vars */ - import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { useHistory } from 'react-router-dom'; @@ -8,6 +6,8 @@ import Input from '@material-ui/core/Input'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import { withStyles } from '@material-ui/core/styles'; import { Button, Typography, Link } from '@material-ui/core'; +import NavigateNextIcon from '@material-ui/icons/NavigateNext'; +import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore'; import useQuery from '../../hooks/useQuery'; @@ -39,6 +39,8 @@ const styles = () => ({ flexGrow: 1, justifyContent: 'center', fontSize: '16px', + height: '100%', + }, navigationContainer: { display: 'flex', @@ -90,7 +92,7 @@ const styles = () => ({ }, mobilePaginationListContainer: { display: 'flex', - marginTop: 44, + marginTop: 0, justifyContent: 'center', }, paginationLabel: { @@ -108,9 +110,10 @@ const styles = () => ({ color: '#FFFFFF', backgroundColor: '#633AA3', borderRadius: '2px', + fontWeight: 500, }, hideDisplay: { - display: 'none', + visibility: 'hidden', }, buttonOpenGoToPage: { maxWidth: 28, @@ -125,6 +128,17 @@ const styles = () => ({ fontSize: 30, fontWeight: 700, }, + centerText: { + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + }, + alignPaginationRange: { + height: '100%', + display: 'flex', + justifyContent: 'center', + }, }); const getPageLink = (history, query, perPage, page) => { @@ -139,8 +153,8 @@ const NextButton = ({ pageLink, classes }) => { const removeUnderline = classes.paginationLabel; const nextClasses = `${color} ${removeUnderline} ${classes.expandNavigationButtons}`; return ( - - {'>'} + + ); }; @@ -151,11 +165,24 @@ const BackButton = ({ pageLink, classes }) => { const backClasses = `${color} ${removeUnderline} ${classes.expandNavigationButtons}`; return ( - {'<'} + ); }; +const CheckValidPageNumber = (totalPages, input) => { + if (typeof input !== 'string' && typeof input !== 'number') { + return false; + } + const pageNum = Number(input); + const checkValidType = Number.isInteger(pageNum); + const checkValidRange = pageNum >= 1 && pageNum <= totalPages; + if (checkValidType && checkValidRange) { + return true; + } + return false; +}; + const GoToPage = ({ classes, totalPages, @@ -168,13 +195,20 @@ const GoToPage = ({ const GoToPageClasses = `${classes.goToPageContainer} ${ShowContent}`; const handleSubmit = (event) => { event.preventDefault(); + if (CheckValidPageNumber(totalPages, input) === false) { + return; + } navigationObject.history.push(getPageLink(...Object.values(navigationObject), input)); + navigationObject.history.go( + navigationObject.history.location.pathname + navigationObject.history.location.search, + ); }; return (
{label} ( - (totalCount > 0) - && ( - - {calculateRange} - - ) -); +const RangeOfResults = ( + { + classes, totalCount, calculateRange, desktopDimensions, + }, +) => { + if (desktopDimensions === false) { + return null; + } + return ( + (totalCount > 0) && (desktopDimensions) + && ( + + + {calculateRange} + + + ) + ); +}; const OpenGoToPageButton = ({ goToPageLabel, setShowButton, classes }) => { const [toggle, setToggle] = useState(true); @@ -205,7 +250,7 @@ const OpenGoToPageButton = ({ goToPageLabel, setShowButton, classes }) => { setShowButton(toggle); }} > - + {goToPageLabel} @@ -230,15 +275,23 @@ const PaginationButton = ({ ); } const isCurrentPage = currPage === pageNumber ? classes.currentPageButton : ''; - const PaginationButtonClasses = `${classes.paginationLabel} ${isCurrentPage}`; + const PaginationButtonClasses = `${classes.paginationLabel} ${classes.activeColor} ${isCurrentPage}`; + const PageNumberBox = ( +
+ {pageNumber} +
+ ); + if (currPage === pageNumber) { + return ( +
+ {PageNumberBox} +
+ ); + } return ( - <> - -
- {pageNumber} -
- - + + {PageNumberBox} + ); }; @@ -259,6 +312,7 @@ const RenderPaginationButtons = ( currPage={currPage} link={page.link} setShowButton={setShowButton} + key={page.pageNumber} /> )) ); @@ -269,7 +323,7 @@ const calculatePaginationMenu = (totalPages, page, labelForGoToPage, history, qu currPage: page, nextPage: ((page + 1 < totalPages) ? page + 1 : null), nextNextPage: ((page + 2 < totalPages) ? page + 2 : null), - ellipsis: ((page + 3 <= totalPages) ? labelForGoToPage : null), + ellipsis: ((page + 2 <= totalPages) ? labelForGoToPage : null), lastPage: ((totalPages !== page) ? totalPages : null), }; pagesToRender = Object.values(pagesToRender).filter((pgNum) => pgNum !== null); @@ -279,6 +333,13 @@ const calculatePaginationMenu = (totalPages, page, labelForGoToPage, history, qu return pagesWithLinks; }; +const CalculatePageRange = (page, perPage, totalCount) => { + const startingRange = (page - 1) * perPage + 1; + const endingRange = (page) * perPage < totalCount ? (page) * perPage : totalCount; + const resultString = endingRange <= 1 ? 'Result' : 'Results'; + return `Showing ${startingRange} - ${endingRange} of ${totalCount} ${resultString}`; +}; + const Pagination = ({ totalCount, page, @@ -317,13 +378,6 @@ const Pagination = ({ nextButton = `${nextLink}?${query.toString()}`; } - const CalculatePageRange = () => { - const startingRange = (page - 1) * perPage + 1; - const endingRange = (page) * perPage < totalCount ? (page) * perPage : totalCount; - const resultString = endingRange <= 1 ? 'Result' : 'Results'; - return `Showing ${startingRange} - ${endingRange} of ${totalCount} ${resultString}`; - }; - const navigationObject = { history, query, perPage, }; @@ -336,7 +390,8 @@ const Pagination = ({
@@ -380,3 +435,22 @@ Pagination.propTypes = { Pagination.defaultProps = {}; export default withStyles(styles)(Pagination); + +// eslint-disable-next-line import/no-mutable-exports +export let Tests = { + getPageLink, + NextButton, + BackButton, + CheckValidPageNumber, + GoToPage, + RangeOfResults, + OpenGoToPageButton, + PaginationButton, + RenderPaginationButtons, + calculatePaginationMenu, + CalculatePageRange, +}; + +if (process.env.NODE_ENV !== 'test') { + Tests = undefined; +} diff --git a/src/components/Pagination/Pagination.test.jsx b/src/components/Pagination/Pagination.test.jsx index 083ecc8e..2fefcaed 100644 --- a/src/components/Pagination/Pagination.test.jsx +++ b/src/components/Pagination/Pagination.test.jsx @@ -1,9 +1,122 @@ +/* eslint-disable */ import React from 'react'; -import { render } from '@testing-library/react'; +import { cleanup, render, screen } from '@testing-library/react'; -import Pagination from './Pagination'; +import NavigateNextIcon from '@material-ui/icons/NavigateNext'; + +import Pagination, { Tests } from './Pagination'; +import { Check } from '@material-ui/icons'; + +const { + getPageLink, + NextButton, + BackButton, + CheckValidPageNumber, + GoToPage, + RangeOfResults, + OpenGoToPageButton, + PaginationButton, + RenderPaginationButtons, + calculatePaginationMenu, + CalculatePageRange, +} = Tests; test.skip('Pagination', () => { const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); + +describe('Pagination Component', () => { + describe('NextButton', () => { + const testProps = { + classes: {}, + pageLink: '/', + } + const { container } = render(); + const iconHtml = container.innerHTML; + cleanup(); + it('It should have a correct icon label', () => { + render(); + const nextButton = screen.getByTestId('next-button-icon'); + expect(nextButton.innerHTML).toBe(iconHtml); + }); + it('It should have an href value if passed in', () => { + render(); + const nextButton = screen.getByTestId('next-button-icon'); + expect(nextButton).toHaveAttribute('href', '/'); + }); + it('It should not have an href if pageLink was undefined', () => { + const noLinkProps = { + classes: {}, + pageLink: undefined, + }; + render(); + const nextButton = screen.getByTestId('next-button-icon'); + expect(nextButton).not.toHaveAttribute('href'); + }); + it('It should have a correct aria-label value', () => { + render(); + expect(screen.getByLabelText('Go next page')).toBeInTheDocument(); + }); + }); + + describe('CheckValidPageNumber', () => { + it('It should return false if the input was not of type string or type number', () => { + expect(CheckValidPageNumber(10, null)).toBe(false); + expect(CheckValidPageNumber(20, undefined)).toBe(false); + expect(CheckValidPageNumber(10, { input: 1 })).toBe(false); + }); + it('It should return false if the value is a string or number but is not a integer', () => { + expect(CheckValidPageNumber(10, 4.52)).toBe(false); + expect(CheckValidPageNumber(20, '11.11')).toBe(false); + }); + it('It should return false if the input is greater than totalPages', () => { + expect(CheckValidPageNumber(1, '2')).toBe(false); + expect(CheckValidPageNumber(1000000, 10000001)).toBe(false); + }); + it('It should return true if the input is of type number or string, an integer and within the range 1-totalPages', () => { + expect(CheckValidPageNumber(20, '20')).toBe(true); + expect(CheckValidPageNumber(20, 20)).toBe(true); + expect(CheckValidPageNumber(20, '15')).toBe(true); + expect(CheckValidPageNumber(20, 15)).toBe(true); + expect(CheckValidPageNumber(40, 1)).toBe(true); + expect(CheckValidPageNumber(5000000, 102460)).toBe(true); + }); + }); + + describe('RangeOfResults', () => { + it('It should not render if it is smaller than the media query, desktopDimensions is false', () => { + const noRenderProps = { + classes: {}, + totalCount: 40, + calculateRange: 'Showing', + desktopDimensions: false, + }; + render(); + const rangeOfResults = screen.queryByText('Showing', { exact: false }); + expect(rangeOfResults).not.toBeInTheDocument(); + }); + it('It should not render if the totalCount of results is 0', () => { + const noRenderProps = { + classes: {}, + totalCount: 0, + calculateRange: 'Showing', + desktopDimensions: true, + }; + render(); + const rangeOfResults = screen.queryByText('Showing', { exact: false }); + expect(rangeOfResults).not.toBeInTheDocument(); + }); + it('It should render the range of results', () => { + const dummyProps = { + classes: {}, + totalCount: 40, + calculateRange: `Showing ${5} - ${10} of ${40} results`, + desktopDimensions: true, + }; + render(); + const rangeOfResults = screen.queryByText('Showing', { exact: false }); + expect(rangeOfResults).toBeInTheDocument(); + }); + }); +}); diff --git a/src/components/SupportButton/SupportButton.jsx b/src/components/SupportButton/SupportButton.jsx new file mode 100644 index 00000000..017adc3f --- /dev/null +++ b/src/components/SupportButton/SupportButton.jsx @@ -0,0 +1,87 @@ +import React, { useState } from 'react'; + +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + withStyles, +} from '@material-ui/core/'; + +const styles = (theme) => ({ + centerContent: { + display: 'flex', + justifyContent: 'center', + }, + searchButton: { + [theme.breakpoints.up('lg')]: { + minWidth: 122.4, + minHeight: 40.8, + }, + backgroundColor: '#FCFBFE', + textTransform: 'none', + border: '1px solid #EBE5F6', + fontWeight: 600, + marginBottom: 4, + }, +}); + +const SupportButton = ({ classes }) => { + const [showSupportDialog, setShowSupportDialog] = useState(false); + + return ( + + + + + setShowSupportDialog(false)} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + + Leave The Lavender Book? + + + + Are you sure you want to leave The Lavender Book? You will be taken + to Google Forms to contact Support. + + + + + + + + + ); +}; + +SupportButton.propTypes = {}; + +SupportButton.defaultProps = {}; + +export default withStyles(styles)(SupportButton); diff --git a/src/components/SupportButton/SupportButton.md b/src/components/SupportButton/SupportButton.md new file mode 100644 index 00000000..43da2d23 --- /dev/null +++ b/src/components/SupportButton/SupportButton.md @@ -0,0 +1,5 @@ +## SupportButton + +```jsx + +``` \ No newline at end of file diff --git a/src/components/SupportButton/SupportButton.test.jsx b/src/components/SupportButton/SupportButton.test.jsx new file mode 100644 index 00000000..007d8eda --- /dev/null +++ b/src/components/SupportButton/SupportButton.test.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { screen, render } from '@testing-library/react'; + +import SupportButton from './SupportButton'; + +describe('SupportButton', () => { + it('It should render a correct label', () => { + render(); + const button = screen.getByText('Report a Problem'); + expect(button).toBeInTheDocument(); + }); +}); diff --git a/src/components/SupportButton/index.js b/src/components/SupportButton/index.js new file mode 100644 index 00000000..75dc2130 --- /dev/null +++ b/src/components/SupportButton/index.js @@ -0,0 +1,3 @@ +import SupportButton from './SupportButton'; + +export default SupportButton; diff --git a/src/routes/Search/Search.jsx b/src/routes/Search/Search.jsx index 71ff0aab..df1f4dab 100644 --- a/src/routes/Search/Search.jsx +++ b/src/routes/Search/Search.jsx @@ -1,30 +1,30 @@ /* eslint-disable no-unused-vars */ -import React, { useEffect, useState } from 'react'; - +import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { geolocated, geoPropTypes } from 'react-geolocated'; import cx from 'classnames'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import { withStyles } from '@material-ui/core/styles'; - import Button from '@material-ui/core/Button'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; import CircularProgress from '@material-ui/core/CircularProgress'; import Typography from '@material-ui/core/Typography'; import SearchIcon from '@material-ui/icons/Search'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import BusinessCard from '../../components/BusinessCard'; import FilterPanel from '../../components/FilterPanel'; import Pagination from '../../components/Pagination'; - import useSearch from './hooks/useSearch'; import SearchForm from './SearchForm'; +import SupportButton from '../../components/SupportButton/SupportButton'; -const styles = (theme) => ({ - result: { - // This is for setting the margins of the results returned from search. - }, +const styles = () => ({ content: { backgroundColor: '#FFFFFF', width: '100%', @@ -37,15 +37,17 @@ const styles = (theme) => ({ paddingTop: '32px', backgroundColor: '#f2f2f2', }, - searchResult: { + desktopMargins: { marginLeft: 40, marginBottom: 20, marginRight: 142, + flex: 1, }, - searchResultBreakpoint: { + mobileMargins: { marginLeft: 20, marginRight: 20, marginBottom: 20, + flex: 1, }, emptyStateWrapper: { marginTop: 60, @@ -64,67 +66,340 @@ const styles = (theme) => ({ marginRight: 20, }, }, - searchresultsText: { - flexGrow: 2, - }, filterButton: { - width: '100px !important', + width: '76px !important', marginBottom: '16px', height: 36, border: '#EBE5F6 1px solid', + padding: 8, + gap: 10, + borderRadius: 4, + marginRight: 8, }, filterButtonText: { fontWeight: 600, fontSize: '14px', lineHeight: '20px', + textTransform: 'none', + }, + filterTextColor: { color: '#1E1131', }, - searchCountHeader: { + clearAllColor: { + border: 0, + color: '#633AA3', + }, + numOfResultsContainer: { fontWeight: 600, fontSize: '32px', color: '#1E1131', lineHeight: '32px', marginBottom: 16, }, - leftSideFilter: { + numResultsMobileFont: { + fontSize: '20px', + lineHeight: '25px', + marginBottom: 8, + }, + sortLeftContainer: { flex: 1, }, - rightSideFilter: { + sortMenuContainer: { display: 'flex', + textAlign: 'center', + alignItems: 'center', + color: '#633AA3', + textTransform: 'none', }, - filterDropdown: { + sortMobileMenuContainer: { display: 'flex', + textAlign: 'center', + alignItems: 'center', + width: '100%', + minHeight: '40px', + backgroundColor: '#F2F2F2', + marginBottom: 8, + lineHeight: '24px', + fontSize: '16px', }, - boldedText: { + searchSettingLabel: { fontWeight: 600, lineHeight: '17.5px', color: '#1E1131', marginRight: 6, }, - purpleText: { + searchSettingMobileLabel: { + fontWeight: 400, + lineHeight: '24px', + color: '#1E1131', + fontSize: '16px', + marginLeft: 32, + }, + rowContainer: { display: 'flex', - color: '#633AA3', }, - arrowDropdown: { - marginRight: 6, - paddingBottom: 4, - verticalAlign: 'middle', + columnContainer: { + display: 'flex', + flexDirection: 'column', }, - hiddenElement: { - display: 'none', + cardMargins: { + marginBottom: 40, }, - searchBodyContainer: { - width: '100%', + headerMargins: { + marginTop: 24, + }, + overrideListItem: { + backgroundColor: 'pink', + }, + selectedChoice: { + fontWeight: 600, + backgroundColor: '#E5E5E5', + boxShadow: '0px 2px 2px rgba(0, 0, 0, 0.2)', }, }); +const ReusableMenu = ( + { + classes, menuValues, menuStrings, onMenuClick, sortLabel, currentValue = menuValues[0], mobile, + }, +) => { + const SortContainerClass = mobile ? classes.sortMobileMenuContainer : classes.sortMenuContainer; + const SearchSettingClass = mobile ? classes.searchSettingMobileLabel : classes.searchSettingLabel; + + const [anchorEl, setAnchorEl] = React.useState(null); + const [selectedIndex, setSelectedIndex] = React.useState(1); + const open = Boolean(anchorEl); + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleMenuItemClick = (event, index) => { + setSelectedIndex(index); + setAnchorEl(null); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + return ( + <> +
+ + {sortLabel} + + + + {menuValues.map((value, index) => { + const boldedText = index === selectedIndex ? classes.selectedChoice : ''; + return ( + handleMenuItemClick(event, index)} + className={boldedText} + /* eslint-disable react/no-array-index-key */ + key={index} + > + {`${value} ${menuStrings}`} + + ); + })} + +
+ + ); +}; + +const querySearch = () => ( + null +); + +const DistanceMenu = ({ classes, mobile }) => ( + +); + +const SortByMenu = ({ classes, mobile }) => ( + +); + +const ShowingPerPageMenu = ({ classes, mobile }) => ( + +); + +const TopSortRow = ({ classes, mobile }) => { + const ContainerClass = mobile ? classes.columnContainer : classes.rowContainer; + return ( +
+ {mobile ? null + : ( +
+ +
+ )} +
+ + +
+
+ ); +}; + +const SortRowMobile = ({ classes, useDesktop }) => { + if (useDesktop) { + return null; + } + return ( +
+ +
+ ); +}; + +const NumberOfResultsHeader = ({ classes, pagination, mobile }) => { + const resultString = pagination.total_count >= 2 ? 'Results' : 'Result'; + const numOfResultsString = `${pagination.total_count} Search ${resultString}`; + const applyFonts = mobile ? classes.numResultsMobileFont : ''; + return ( +
+ {numOfResultsString} +
+ ); +}; + +const OpenFilterPanelButton = ( + { + classes, openFilter, setOpenFilter, clearFilters, display, + }, +) => { + if (display === false) { + return null; + } + return ( +
+ + +
+ ); +}; + +const FilterPanelAside = ({ + openFilter, + setOpenFilter, + isWiderThanBreakpoint, + indicators, + search, + updateSearch, + searchResults, + classes, +}) => { + const ShowFilterPanel = searchResults !== null && searchResults.length > 0 + && (isWiderThanBreakpoint || openFilter); + if (ShowFilterPanel === false) { + return null; + } + return ( +
+ setOpenFilter(false)} + type={isWiderThanBreakpoint ? 'desktop' : 'mobile'} + allIndicators={indicators} + search={search} + updateSearch={updateSearch} + resultCount={searchResults.length} + /> +
+ ); +}; + +const SearchBodyHeader = ({ + classes, pagination, setOpenFilter, openFilter, useDesktop, display, +}) => { + if (display === false) { + return null; + } + return ( +
+ + + +
+ ); +}; + const Search = ({ classes, coords, isGeolocationEnabled, }) => { - const isWiderThanBreakpoint = useMediaQuery('(min-width:1376px)'); - const [openFilter, setOpenFilter] = useState(false); const { updateSearch, @@ -136,6 +411,7 @@ const Search = ({ userLocation, indicators = [], } = useSearch({ userCoords: coords, isGeolocationEnabled }); + const onSearchSubmit = async (searchTerm) => { updateSearch(searchTerm); }; @@ -156,6 +432,11 @@ const Search = ({ updateFilters('rating', changedFilters.stars); }; + const isWiderThanBreakpoint = useMediaQuery('(min-width:1376px)'); + const useDesktop = useMediaQuery('(min-width:838px)'); + const SearchPageMargins = isWiderThanBreakpoint ? classes.desktopMargins : classes.mobileMargins; + const FoundOneOrMoreResults = searchResults !== null && searchResults.length > 0; + const isGeoLoading = isGeolocationEnabled && coords === null; return ( @@ -170,109 +451,49 @@ const Search = ({ /> )}
- {searchResults !== null - && searchResults.length > 0 - && (isWiderThanBreakpoint || openFilter) - && ( -
- setOpenFilter(false)} - type={isWiderThanBreakpoint ? 'desktop' : 'mobile'} - allIndicators={indicators} - search={search} - updateSearch={updateSearch} - resultCount={searchResults.length} - /> -
- )} -
- {searchResults !== null && searchResults.length > 0 - ? ( -
-
- {`${pagination.total_count} Search ${pagination.totalCount >= 2 ? 'Result' : 'Results'}`} -
- {!isWiderThanBreakpoint && ( -
- -
- )} -
-
-
- Showing: - - 10 per page - - -
-
-
-
- Distance: - - 5 miles - - -
-
- Sort by: - - Highly Rated - - -
-
-
-
- ) - : null} + +
+
- {searchResults !== null && searchResults.length > 0 - && searchResults.map((result, index) => ( -
( +
+ - -
- ))} + count={(pagination.page - 1) * 10 + index + 1} + /> +
+ ))}
- {pagination && pagination !== null && ( -
+ {pagination && pagination !== null && !loading && ( +
+ +
)}
- {searchResults !== null && search.searchTerm !== null && searchResults.length === 0 @@ -311,3 +532,20 @@ Search.props = { Search.props = { ...Search.props, ...geoPropTypes }; export default geolocated({ positionOptions: { timeout: 5000 } })(withStyles(styles)(Search)); + +// eslint-disable-next-line import/no-mutable-exports +export let Tests = { + ReusableMenu, + DistanceMenu, + SortByMenu, + ShowingPerPageMenu, + TopSortRow, + SortRowMobile, + NumberOfResultsHeader, + OpenFilterPanelButton, + FilterPanelAside, +}; + +if (process.env.NODE_ENV !== 'test') { + Tests = undefined; +} diff --git a/src/routes/Search/Search.test.jsx b/src/routes/Search/Search.test.jsx new file mode 100644 index 00000000..15dfb161 --- /dev/null +++ b/src/routes/Search/Search.test.jsx @@ -0,0 +1,48 @@ +/* eslint-disable */ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import Search, { Tests } from './Search'; +import userEvent from '@testing-library/user-event'; + +const { + SortByMenu, +} = Tests; + +describe('Search Page', () => { + describe('SortByMenu', () => { + const dummyProps = { + classes: {}, + mobile: false, + }; + it('It should render a button', () => { + render(); + const button = screen.getByRole('button'); + expect(button).toBeInTheDocument(); + }); + it('It should render a label', () => { + render(); + const label = screen.getByTestId('menu-dropdown-label'); + expect(label).toBeInTheDocument(); + }); + it('It should render a label that prefixes the value', () => { + render(); + const label = screen.getByTestId('menu-dropdown-label'); + expect(label.textContent).toContain('Sort by:'); + }); + it('It should render the current value on the label', () => { + render(); + const currentValue = screen.getByText('Highly Rated'); + expect(currentValue).toBeInTheDocument(); + }); + it('It should render menu items on click', () => { + render(); + const button = screen.getByRole('button'); + const menuItem = screen.queryByText('Recently Added'); + expect(menuItem).not.toBeInTheDocument(); + + userEvent.click(button); + expect(screen.getByText('Recently Added')).toBeInTheDocument(); + }); + }); +});