From 2c790ef5bca59239ac860406f131f9c708fc93c3 Mon Sep 17 00:00:00 2001 From: Kea-Roy Ong <146872846+kea-roy@users.noreply.github.com> Date: Tue, 9 Apr 2024 17:54:19 -0400 Subject: [PATCH] Create DropDownWithLabel component and revamped all pages with dropdown component to avoid repetitive code. Fixed dropdown styling to better match figma --- .../ApartmentCard/ApartmentCards.tsx | 114 ++++++++-------- frontend/src/components/utils/DropDown.tsx | 35 ++++- .../components/utils/DropDownWithLabel.tsx | 60 +++++++++ frontend/src/pages/ApartmentPage.tsx | 121 ++++++----------- frontend/src/pages/BookmarksPage.tsx | 84 +++++++----- frontend/src/pages/LandlordPage.tsx | 122 ++++++------------ 6 files changed, 283 insertions(+), 253 deletions(-) create mode 100644 frontend/src/components/utils/DropDownWithLabel.tsx diff --git a/frontend/src/components/ApartmentCard/ApartmentCards.tsx b/frontend/src/components/ApartmentCard/ApartmentCards.tsx index 019549b3..5d0f0279 100644 --- a/frontend/src/components/ApartmentCard/ApartmentCards.tsx +++ b/frontend/src/components/ApartmentCard/ApartmentCards.tsx @@ -1,12 +1,12 @@ -import React, { ReactElement, useState } from 'react'; +import React, { ReactElement, useEffect, useState } from 'react'; import ApartmentCard from './ApartmentCard'; -import { Grid, Link, makeStyles, Button, Typography } from '@material-ui/core'; +import { Grid, Link, makeStyles, Button } from '@material-ui/core'; import { Link as RouterLink } from 'react-router-dom'; import { CardData } from '../../App'; import { loadingLength } from '../../constants/HomeConsts'; -import DropDown from '../utils/DropDown'; import { ApartmentWithId } from '../../../../common/types/db-types'; import { sortApartments } from '../../utils/sortApartments'; +import DropDownWithLabel from '../utils/DropDownWithLabel'; type Props = { data: CardData[]; @@ -34,13 +34,6 @@ const useStyles = makeStyles({ borderRight: 'none', borderBottom: 'none', }, - sortByButton: { - background: '#E8E8E8', - border: 'none', - borderRadius: '10px', - paddingRight: '5px', - paddingLeft: '5px', - }, }); /** @@ -58,14 +51,24 @@ const useStyles = makeStyles({ * @returns {ReactElement} ApartmentCards component. */ const ApartmentCards = ({ data, user, setUser }: Props): ReactElement => { - const { boundingBox, showMoreButton, horizontalLine, sortByButton } = useStyles(); - + const { boundingBox, showMoreButton, horizontalLine } = useStyles(); + const [isMobile, setIsMobile] = useState(false); const [resultsToShow, setResultsToShow] = useState(loadingLength); const handleShowMore = () => { setResultsToShow(resultsToShow + loadingLength); }; + // Handle resizing of the window depending on mobile and if it is clicked. + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth <= 600); + }; + handleResize(); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + type Fields = keyof CardData | keyof ApartmentWithId | 'originalOrder'; const [sortBy, setSortBy] = useState('originalOrder'); const [orderLowToHigh, setOrderLowToHigh] = useState(false); @@ -73,52 +76,47 @@ const ApartmentCards = ({ data, user, setUser }: Props): ReactElement => { return ( <> - - - Sort by: - - - { - setSortBy('originalOrder'); - setOrderLowToHigh(false); - }, - }, - { - item: 'Lowest Price', - callback: () => { - setSortBy('avgPrice'); - setOrderLowToHigh(true); - }, - }, - { - item: 'Highest Price', - callback: () => { - setSortBy('avgPrice'); - setOrderLowToHigh(false); - }, - }, - { - item: 'Lowest Rating', - callback: () => { - setSortBy('avgRating'); - setOrderLowToHigh(true); - }, - }, - { - item: 'Highest Rating', - callback: () => { - setSortBy('avgRating'); - setOrderLowToHigh(false); - }, - }, - ]} - /> - - + { + setSortBy('originalOrder'); + setOrderLowToHigh(false); + }, + }, + { + item: 'Lowest Price', + callback: () => { + setSortBy('avgPrice'); + setOrderLowToHigh(true); + }, + }, + { + item: 'Highest Price', + callback: () => { + setSortBy('avgPrice'); + setOrderLowToHigh(false); + }, + }, + { + item: 'Lowest Rating', + callback: () => { + setSortBy('avgRating'); + setOrderLowToHigh(true); + }, + }, + { + item: 'Highest Rating', + callback: () => { + setSortBy('avgRating'); + setOrderLowToHigh(false); + }, + }, + ]} + isMobile={isMobile} + /> {data && diff --git a/frontend/src/components/utils/DropDown.tsx b/frontend/src/components/utils/DropDown.tsx index 16017ecf..c87cabc6 100644 --- a/frontend/src/components/utils/DropDown.tsx +++ b/frontend/src/components/utils/DropDown.tsx @@ -27,8 +27,22 @@ import React, { useState } from 'react'; import { Button, Menu, MenuItem } from '@material-ui/core'; import { makeStyles } from '@material-ui/styles'; -import { ArrowDropDown, ArrowDropUp } from '@material-ui/icons'; -import SvgIcon from '@material-ui/core/SvgIcon'; + +const expandArrow = (direction: boolean) => { + return ( +
+ + + +
+ ); +}; type MenuElement = { item: string; @@ -37,6 +51,7 @@ type MenuElement = { type Props = { menuItems: MenuElement[]; + isMobile?: boolean; }; const useStyles = makeStyles({ @@ -47,7 +62,7 @@ const useStyles = makeStyles({ }, }); -export default function DropDown({ menuItems }: Props) { +export default function DropDown({ menuItems, isMobile }: Props) { const [anchorEl, setAnchorEl] = useState(null); const [selected, setSelected] = useState(menuItems[0].item || '-'); const { button } = useStyles(); @@ -69,9 +84,21 @@ export default function DropDown({ menuItems }: Props) { aria-expanded={open ? 'true' : undefined} onClick={handleClick} className={button} + style={{ + textTransform: 'none', + fontSize: '22px', + lineHeight: 'normal', + fontWeight: 'normal', + height: '51px', + paddingLeft: '18px', + paddingRight: '18px', + borderRadius: '10px', + backgroundColor: '#E8E8E8', + scale: isMobile ? '0.75' : '1', + }} > {selected} - + {expandArrow(open)} console.log('Item 1 selected') }, + * { item: 'Item 2', callback: () => console.log('Item 2 selected') }, + * { item: 'Item 3', callback: () => console.log('Item 3 selected') }, + * ]; + * + * + */ +import React from 'react'; +import { Typography, Grid } from '@material-ui/core'; +import DropDown from './DropDown'; + +type MenuElement = { + item: string; + callback: () => void; +}; + +interface DropDownWithLabelProps { + label: string; + menuItems: MenuElement[]; + labelStyle?: React.CSSProperties; + isMobile: boolean; +} + +const DropDownWithLabel: React.FC = ({ + label, + menuItems, + labelStyle, + isMobile, +}) => { + return ( +
+ + + + {label} + + + + + + +
+ ); +}; + +export default DropDownWithLabel; diff --git a/frontend/src/pages/ApartmentPage.tsx b/frontend/src/pages/ApartmentPage.tsx index 90ad3867..0501020a 100644 --- a/frontend/src/pages/ApartmentPage.tsx +++ b/frontend/src/pages/ApartmentPage.tsx @@ -30,7 +30,6 @@ import LinearProgress from '../components/utils/LinearProgress'; import { Likes, ReviewWithId } from '../../../common/types/db-types'; import axios from 'axios'; import { createAuthHeaders, subscribeLikes, getUser } from '../utils/firebase'; -import DropDown from '../components/utils/DropDown'; import { useParams } from 'react-router-dom'; import NotFoundPage from './NotFoundPage'; import HeartRating from '../components/utils/HeartRating'; @@ -41,6 +40,7 @@ import clsx from 'clsx'; import { sortReviews } from '../utils/sortReviews'; import savedIcon from '../assets/filled-large-saved-icon.png'; import unsavedIcon from '../assets/unfilled-large-saved-icon.png'; +import DropDownWithLabel from '../components/utils/DropDownWithLabel'; type Props = { user: firebase.User | null; @@ -53,13 +53,6 @@ export type RatingInfo = { }; const useStyles = makeStyles((theme) => ({ - sortByButton: { - background: '#E8E8E8', - border: 'none', - borderRadius: '10px', - paddingRight: '5px', - paddingLeft: '5px', - }, reviewButton: { borderRadius: '30px', marginTop: '10px', @@ -168,7 +161,6 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => { }; const { - sortByButton, reviewButton, aptRating, heartRating, @@ -543,21 +535,7 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => { )} - - - {isSaved - + - - - - Sort by: - - - { - setSortBy('date'); - }, - }, - { - item: 'Helpful', - callback: () => { - setSortBy('likes'); - }, - }, - ]} - /> - - + + { + setSortBy('date'); + }, + }, + { + item: 'Helpful', + callback: () => { + setSortBy('likes'); + }, + }, + ]} + isMobile={isMobile} + /> @@ -653,39 +626,29 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => { )} - - {!isMobile && ( - - - Sort by: - - - { - setSortBy('date'); - }, + + + {!isMobile && ( + { + setSortBy('date'); }, - { - item: 'Helpful', - callback: () => { - setSortBy('likes'); - }, + }, + { + item: 'Helpful', + callback: () => { + setSortBy('likes'); }, - ]} - /> - - - )} + }, + ]} + isMobile={isMobile} + /> + )} + {sortReviews(reviewData, sortBy) diff --git a/frontend/src/pages/BookmarksPage.tsx b/frontend/src/pages/BookmarksPage.tsx index 4c189da5..df72ba80 100644 --- a/frontend/src/pages/BookmarksPage.tsx +++ b/frontend/src/pages/BookmarksPage.tsx @@ -12,8 +12,8 @@ import { Likes, ReviewWithId } from '../../../common/types/db-types'; import axios from 'axios'; import { createAuthHeaders, getUser } from '../utils/firebase'; import ReviewComponent from '../components/Review/Review'; -import DropDown from '../components/utils/DropDown'; import { sortReviews } from '../utils/sortReviews'; +import DropDownWithLabel from '../components/utils/DropDownWithLabel'; type Props = { user: firebase.User | null; @@ -39,13 +39,6 @@ const useStyles = makeStyles((theme) => ({ fontFamily: 'Work Sans', fontWeight: 800, }, - sortByButton: { - background: '#E8E8E8', - border: 'none', - borderRadius: '10px', - paddingRight: '5px', - paddingLeft: '5px', - }, headerContainer: { marginTop: '2em', marginBottom: '2em', @@ -60,7 +53,7 @@ const useStyles = makeStyles((theme) => ({ * @returns BookmarksPage The BookmarksPage component. */ const BookmarksPage = ({ user, setUser }: Props): ReactElement => { - const { background, headerStyle, sortByButton, headerContainer, gridContainer } = useStyles(); + const { background, headerStyle, headerContainer, gridContainer } = useStyles(); const defaultShow = 6; const [toShow, setToShow] = useState(defaultShow); @@ -79,6 +72,14 @@ const BookmarksPage = ({ user, setUser }: Props): ReactElement => { const [likeStatuses, setLikeStatuses] = useState({}); const [toggle, setToggle] = useState(false); const [showMoreLessState, setShowMoreLessState] = useState('Show More'); + const [isMobile, setIsMobile] = useState(false); + + useEffect(() => { + const handleResize = () => setIsMobile(window.innerWidth <= 600); + handleResize(); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); useTitle('Bookmarks'); @@ -228,32 +229,51 @@ const BookmarksPage = ({ user, setUser }: Props): ReactElement => { - - - - Sort by: - - - { - setSortBy('date'); - }, + {!isMobile && ( + + { + setSortBy('date'); }, - { - item: 'Helpful', - callback: () => { - setSortBy('likes'); - }, + }, + { + item: 'Helpful', + callback: () => { + setSortBy('likes'); }, - ]} - /> - - - + }, + ]} + isMobile={isMobile} + /> + + )} + {isMobile && ( + + { + setSortBy('date'); + }, + }, + { + item: 'Helpful', + callback: () => { + setSortBy('likes'); + }, + }, + ]} + isMobile={isMobile} + /> + + )} {helpfulReviewsData.length === 0 && ( You have not marked any reviews helpful. diff --git a/frontend/src/pages/LandlordPage.tsx b/frontend/src/pages/LandlordPage.tsx index 753cdd76..83e0ae86 100644 --- a/frontend/src/pages/LandlordPage.tsx +++ b/frontend/src/pages/LandlordPage.tsx @@ -24,7 +24,6 @@ import LinearProgress from '../components/utils/LinearProgress'; import { Likes, ReviewWithId } from '../../../common/types/db-types'; import axios from 'axios'; import { createAuthHeaders, subscribeLikes, getUser } from '../utils/firebase'; -import DropDown from '../components/utils/DropDown'; import NotFoundPage from './NotFoundPage'; import { CardData } from '../App'; import { getAverageRating } from '../utils/average'; @@ -33,6 +32,7 @@ import { colors } from '../colors'; import { sortReviews } from '../utils/sortReviews'; import savedIcon from '../assets/filled-large-saved-icon.png'; import unsavedIcon from '../assets/unfilled-large-saved-icon.png'; +import DropDownWithLabel from '../components/utils/DropDownWithLabel'; export type RatingInfo = { feature: string; @@ -72,13 +72,6 @@ const useStyles = makeStyles((theme) => ({ marginTop: '10px', marginBottom: '10px', }, - sortByButton: { - background: '#E8E8E8', - border: 'none', - borderRadius: '10px', - paddingRight: '5px', - paddingLeft: '5px', - }, })); /** @@ -114,15 +107,8 @@ const LandlordPage = ({ user, setUser }: Props): ReactElement => { const saved = savedIcon; const unsaved = unsavedIcon; const [isSaved, setIsSaved] = useState(false); - const { - container, - leaveReviewContainer, - horizontalLine, - heartRating, - aptRating, - reviewButton, - sortByButton, - } = useStyles(); + const { container, leaveReviewContainer, horizontalLine, heartRating, aptRating, reviewButton } = + useStyles(); // useEffect hook to control the number of results to show based on screen size and reviewData length useEffect(() => { @@ -385,30 +371,25 @@ const LandlordPage = ({ user, setUser }: Props): ReactElement => { Leave a Review - - - - Sort by: - - - { - setSortBy('date'); - }, - }, - { - item: 'Helpful', - callback: () => { - setSortBy('likes'); - }, - }, - ]} - /> - - + + { + setSortBy('date'); + }, + }, + { + item: 'Helpful', + callback: () => { + setSortBy('likes'); + }, + }, + ]} + isMobile={isMobile} + /> @@ -463,21 +444,7 @@ const LandlordPage = ({ user, setUser }: Props): ReactElement => { - - - {isSaved - + - - - - Sort by: - - - { - setSortBy('date'); - }, - }, - { - item: 'Helpful', - callback: () => { - setSortBy('likes'); - }, - }, - ]} - /> - - + + { + setSortBy('date'); + }, + }, + { + item: 'Helpful', + callback: () => { + setSortBy('likes'); + }, + }, + ]} + isMobile={isMobile} + />