Skip to content

Commit

Permalink
feat: add tags to map filters
Browse files Browse the repository at this point in the history
  • Loading branch information
benfurber committed Nov 7, 2024
1 parent 69bd593 commit 968a7dc
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 99 deletions.
61 changes: 35 additions & 26 deletions packages/components/src/MapFilterList/MapFilterList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Flex, Heading, Image, Text } from 'theme-ui'
import { Flex, Heading, Text } from 'theme-ui'

import { Button } from '../Button/Button'
import { ButtonIcon } from '../ButtonIcon/ButtonIcon'
import { MemberBadge } from '../MemberBadge/MemberBadge'
import { MapFilterListItem } from './MapFilterListItem'
Expand All @@ -16,31 +17,35 @@ export interface IProps {
availableFilters: MapFilterOptionsList
onClose: () => void
onFilterChange: (filter: MapFilterOption) => void
pinsCount: number
}

export const MapFilterList = (props: IProps) => {
const { activeFilters, availableFilters, onClose, onFilterChange } = props
const { activeFilters, availableFilters, onClose, onFilterChange, pinsCount } = props

const profileFilters = availableFilters.filter(
({ filterType }) => filterType === 'profileType',
)
const workspaceFilters = availableFilters.filter(
({ filterType }) => filterType === 'workspaceType',

const tagFilters = availableFilters.filter(
({ filterType }) => filterType === 'profileTag',
)

const isActive = (checkingFilter: string) =>
!!activeFilters.find((filter) => filter.label === checkingFilter)

const buttonLabel = `Show ${pinsCount} result${pinsCount === 1 ? '' : 's'}`

return (
<Flex
data-cy="MapFilterList"
sx={{
flexDirection: 'column',
position: 'relative',
gap: 2,
gap: 4,
}}
>
<Flex sx={{ gap: 2 }}>
<Flex sx={{ alignItems: 'center', gap: 2, justifyContent: 'space-between' }}>
<Heading as="h3" variant="small">
So what are you looking for?
</Heading>
Expand All @@ -53,11 +58,11 @@ export const MapFilterList = (props: IProps) => {
/>
</Flex>

{workspaceFilters.length > 0 && (
<>
<Text>Workspace:</Text>
{profileFilters.length > 0 && (
<Flex sx={{gap: 1, flexDirection: 'column'}}>
<Text>Profiles:</Text>
<MapFilterListWrapper>
{workspaceFilters.map((typeFilter, index) => {
{profileFilters.map((typeFilter, index) => {
const onClick = () => onFilterChange(typeFilter)

return (
Expand All @@ -66,48 +71,52 @@ export const MapFilterList = (props: IProps) => {
key={index}
onClick={onClick}
>
{typeFilter.imageSrc && (
<Image
src={typeFilter.imageSrc}
sx={{ height: 30, width: 30 }}
/>
)}
<MemberBadge
size={30}
profileType={typeFilter._id as ProfileTypeName}
/>
<Text variant="quiet" sx={{ fontSize: 1 }}>
{typeFilter.label}
</Text>
</MapFilterListItem>
)
})}
</MapFilterListWrapper>
</>
</Flex>
)}

{profileFilters.length > 0 && (
<>
<Text>Profiles:</Text>
{tagFilters.length > 0 && (
<Flex sx={{gap: 1, flexDirection: 'column'}}>
<Text>Activities:</Text>
<MapFilterListWrapper>
{profileFilters.map((typeFilter, index) => {
{tagFilters.map((typeFilter, index) => {
const onClick = () => onFilterChange(typeFilter)

return (
<MapFilterListItem
active={isActive(typeFilter.label)}
key={index}
onClick={onClick}
sx={{ maxWidth: 'auto', width: 'auto' }}
>
<MemberBadge
size={30}
profileType={typeFilter._id as ProfileTypeName}
/>
<Text variant="quiet" sx={{ fontSize: 1 }}>
{typeFilter.label}
</Text>
</MapFilterListItem>
)
})}
</MapFilterListWrapper>
</>
</Flex>
)}

<Button
data-cy="MapFilterList-ShowResultsButton"
icon="sliders"
onClick={() => onClose()}
sx={{alignSelf: 'flex-start'}}
>
{buttonLabel}
</Button>
</Flex>
)
}
8 changes: 7 additions & 1 deletion packages/components/src/MapFilterList/MapFilterListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { CardButton } from '../CardButton/CardButton'

import type { ThemeUIStyleObject } from 'theme-ui'

interface IProps {
active: boolean
onClick: () => void
children: React.ReactNode
sx?: ThemeUIStyleObject | undefined
}

export const MapFilterListItem = ({ active, onClick, children }: IProps) => {
export const MapFilterListItem = ({ active, onClick, children, sx }: IProps) => {
return (
<CardButton
data-cy={`MapFilterListItem${active ? '-active' : ''}`}
onClick={onClick}
extrastyles={{
display: 'flex',
maxWidth: ['100%', '49%'],
width: '500px',
flexDirection: 'row',
backgroundColor: 'offWhite',
padding: 1,
Expand All @@ -27,6 +32,7 @@ export const MapFilterListItem = ({ active, onClick, children }: IProps) => {
borderColor: 'offWhite',
':hover': { borderColor: 'offWhite' },
}),
...sx,
}}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ export const MapFilterListWrapper = ({ children }: IProps) => (
data-cy="MapFilterList"
sx={{
listStyle: 'none',
flexWrap: 'nowrap',
flexWrap: 'wrap',
gap: 2,
flexDirection: 'column',
flexDirection: 'row',
padding: 0,
}}
>
Expand Down
6 changes: 4 additions & 2 deletions packages/components/src/ProfileTagsList/ProfileTagsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import { Flex } from 'theme-ui'
import { Category } from '../Category/Category'

import type { ITag } from 'oa-shared'
import type { ThemeUIStyleObject } from 'theme-ui';

export interface IProps {
tags: ITag[]
sx?: ThemeUIStyleObject | undefined
}

export const ProfileTagsList = ({ tags }: IProps) => {
export const ProfileTagsList = ({ sx, tags }: IProps) => {
return (
<Flex data-cy="ProfileTagsList" sx={{ gap: 2, flexWrap: 'wrap' }}>
<Flex sx={{ gap: 2, flexWrap: 'wrap', ...sx }}>
{tags.map(
(tag, index) =>
tag?.label && (
Expand Down
80 changes: 30 additions & 50 deletions src/pages/Maps/Content/MapView/MapWithList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Tooltip } from 'react-tooltip'
import { Button, Map } from 'oa-components'
import { Box, Flex } from 'theme-ui'

import { INITIAL_ZOOM } from '../../Maps.client'
import { allMapFilterOptions } from './allMapFilterOptions'
import { Clusters } from './Cluster.client'
import { latLongFilter } from './latLongFilter'
Expand Down Expand Up @@ -30,9 +31,11 @@ interface IProps {
setZoom: (arg: number) => void
zoom: number
promptUserLocation: () => Promise<void>
INITIAL_ZOOM: number
}

const ZOOM_IN_TOOLTIP = 'Zoom in to your location'
const ZOOM_OUT_TOOLTIP = 'Zoom out to world view'

export const MapWithList = (props: IProps) => {
const {
activePin,
Expand All @@ -46,7 +49,6 @@ export const MapWithList = (props: IProps) => {
setZoom,
zoom,
promptUserLocation,
INITIAL_ZOOM,
} = props

const [activePinFilters, setActivePinFilters] =
Expand All @@ -60,54 +62,55 @@ export const MapWithList = (props: IProps) => {
const availableFilters = useMemo(() => {
const pinDetails = pins.map(({ creator }) => [
creator?.profileType,
creator?.workspaceType,
...(creator?.tags ? creator.tags.map(({_id}) => _id) : []),
])
const filtersNeeded = [...new Set(pinDetails.flat())]

return allMapFilterOptions.filter((validFilter) =>
filtersNeeded.some((neededfilter) => neededfilter === validFilter._id),
)
}, [pins])

const buttonStyle = {
backgroundColor: 'white',
padding: '20px',
borderRadius: 99,
padding: 4,
':hover': {
backgroundColor: 'lightgray',
},
}

const ZOOM_IN_TOOLTIP = 'Zoom in to your location'
const ZOOM_OUT_TOOLTIP = 'Zoom out to world view'

useEffect(() => {
const workspaceTypeFilters = activePinFilters
.filter(({ filterType }) => filterType === 'workspaceType')
.map(({ _id }) => _id)

if (workspaceTypeFilters.length > 0) {
const workspaceFilteredList = allPinsInView.filter(
({ creator }) =>
creator?.workspaceType &&
workspaceTypeFilters.includes(creator.workspaceType),
)
return setFilteredPins(workspaceFilteredList)
}

const profileTypeFilters = activePinFilters
.filter(({ filterType }) => filterType === 'profileType')
.map(({ _id }) => _id)

const filteredPins: IMapPin[] = []

if (profileTypeFilters.length > 0) {
const profileTypeFilteredList = allPinsInView.filter(
({ creator }) =>
creator?.profileType &&
profileTypeFilters.includes(creator?.profileType),
)
return setFilteredPins(profileTypeFilteredList)
filteredPins.push(...profileTypeFilteredList)
}

const profileTagFilters = activePinFilters
.filter(({ filterType }) => filterType === 'profileTag')
.map(({ _id }) => _id)

if (profileTagFilters.length > 0) {
const listToFilter = filteredPins.length > 0 ? filteredPins : allPinsInView
const tagFilteredList = listToFilter.filter(({creator}) => {
const tagIds = creator?.tags?.map(({_id}) => _id)
return profileTagFilters.some((tagId) => tagIds?.includes(tagId) )
})
return setFilteredPins(tagFilteredList)
}

if (filteredPins.length === 0) { return setFilteredPins(allPinsInView) }
return setFilteredPins(filteredPins)

setFilteredPins(allPinsInView)
}, [activePinFilters, allPinsInView])

const handleLocationFilter = () => {
Expand All @@ -132,29 +135,7 @@ export const MapWithList = (props: IProps) => {
)
}

const addingWorkspaceTypeFilter =
changedOption.filterType === 'workspaceType'

if (addingWorkspaceTypeFilter) {
const existingWorkspaceTypeFilters = activePinFilters.filter(
({ filterType }) => filterType === 'workspaceType',
)

return setActivePinFilters([
{
_id: 'workspace',
filterType: 'profileType',
label: 'Workspace',
},
...existingWorkspaceTypeFilters,
changedOption,
])
}

const existingProfileTypeFilters = activePinFilters.filter(
({ filterType }) => filterType === 'profileType',
)
return setActivePinFilters([...existingProfileTypeFilters, changedOption])
return setActivePinFilters((activePinFilters) => [...activePinFilters, changedOption])
}

const isViewportGreaterThanTablet = window.innerWidth > 1024
Expand Down Expand Up @@ -257,15 +238,15 @@ export const MapWithList = (props: IProps) => {
<Box
sx={{
position: 'absolute',
top: 2,
right: 2,
top: 0,
right:0,
padding: 4,
zIndex: 1000,
display: 'flex',
flexDirection: 'column',
gap: 2,
}}
>
{/* Location button to Zoom in to your location */}
<Button
data-tooltip-content={ZOOM_IN_TOOLTIP}
data-cy="LocationViewButton"
Expand All @@ -279,7 +260,6 @@ export const MapWithList = (props: IProps) => {
/>
<Tooltip id="locationButton-tooltip" place="left" />

{/* Globe button to Zoom out to world view */}
<Button
data-tooltip-content={ZOOM_OUT_TOOLTIP}
data-cy="WorldViewButton"
Expand Down
8 changes: 7 additions & 1 deletion src/pages/Maps/Content/MapView/MapWithListHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,20 @@ export const MapWithListHeader = (props: IProps) => {
const toggleFilterModal = () => setShowFilters(!showFilters)
const hasFiltersSelected = activePinFilters.length !== 0

const sx = {
width: ['350px', '600px'],
minWidth: '350px',
}

return (
<>
<Modal onDidDismiss={toggleFilterModal} isOpen={showFilters}>
<Modal onDidDismiss={toggleFilterModal} isOpen={showFilters} sx={sx}>
<MapFilterList
activeFilters={activePinFilters}
availableFilters={availableFilters}
onClose={toggleFilterModal}
onFilterChange={onFilterChange}
pinsCount={filteredPins?.length || 0}
/>
</Modal>

Expand Down
Loading

0 comments on commit 968a7dc

Please sign in to comment.