Skip to content

Commit

Permalink
Here's the first crack at it.
Browse files Browse the repository at this point in the history
  • Loading branch information
popkinj committed Jan 27, 2025
1 parent 1d32d51 commit 1339481
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 47 deletions.
4 changes: 3 additions & 1 deletion frontend/e2e/pages/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export const map_page = async (page: Page) => {
await expect(
page.getByRole('button', { name: 'Polygon Search' }),
).toBeVisible()
await expect(page.getByRole('button', { name: 'Point Search' })).toBeVisible()
await expect(
page.getByRole('button', { name: 'Radius Search' }),
).toBeVisible()

const searchBy = page.getByText('Search By:')
await expect(searchBy).toBeVisible()
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ export enum ActiveToolEnum {
/**
* The smallest allowed radius in meters for the Point Search feature.
*/
export const MIN_CIRCLE_RADIUS = 500
export const MIN_CIRCLE_RADIUS = 1000
2 changes: 1 addition & 1 deletion frontend/src/pages/map/MapView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ describe('Test suite for MapView', () => {
it('should render the MapView and test point search', async () => {
const { user } = renderComponent(themeBreakpointValues.xxl, mockOmrrData)

const pointSearchBtn = screen.getByRole('button', { name: 'Point Search' })
const pointSearchBtn = screen.getByRole('button', { name: 'Radius Search' })
await user.click(pointSearchBtn)

const cancelBtn = screen.getByRole('button', { name: 'Cancel' })
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/map/drawer/MapBottomDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function getTitle(
case ActiveToolEnum.polygonSearch:
return 'Polygon Search'
case ActiveToolEnum.pointSearch:
return 'Point Search'
return 'Radius Search'
case ActiveToolEnum.searchBy:
return 'Search By'
case ActiveToolEnum.filterBy:
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/map/search/MapSearch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Test suite for MapSearch', () => {
screen.getByRole('button', { name: 'Find Me' })
screen.getByRole('button', { name: 'Layers' })
screen.getByRole('button', { name: 'Polygon Search' })
screen.getByRole('button', { name: 'Point Search' })
screen.getByRole('button', { name: 'Radius Search' })
screen.getByText('Search By:')
screen.getByRole('button', { name: 'Filter by Facility Type' })

Expand Down
172 changes: 132 additions & 40 deletions frontend/src/pages/map/search/PointSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,66 @@
import { useDispatch } from 'react-redux'
import { Button, Slider, Typography } from '@mui/material'
import { Button, Slider, Typography, TextField } from '@mui/material'
import clsx from 'clsx'

import DropdownButton from '@/components/DropdownButton'
import { MIN_CIRCLE_RADIUS } from '@/constants/constants'
import { clearActiveTool } from '@/features/map/map-slice'
import { clearActiveTool, toggleActiveTool } from '@/features/map/map-slice'
import {
resetPointFilter,
setPointFilterRadius,
usePointFilterRadius,
} from '@/features/omrr/omrr-slice'
import { formatDistance } from '@/utils/utils'
import { ActiveToolEnum } from '@/constants/constants'

import CloseIcon from '@/assets/svgs/fa-close.svg?react'
import CheckIcon from '@/assets/svgs/fa-check.svg?react'

interface Props {
isSmall?: boolean
className?: string
showControls?: boolean
}

export function PointSearch({ isSmall = false, className }: Readonly<Props>) {
const styles = {
sliderContainer: {
display: 'flex',
alignItems: 'center',
gap: '20px',
},
sliderWrapper: {
flex: 1,
},
labelContainer: {
display: 'flex',
justifyContent: 'space-between',
width: '100%',
},
textField: {
marginTop: '-24px',
'& input[type=number]': {
MozAppearance: 'textfield',
},
'& input[type=number]::-webkit-outer-spin-button, & input[type=number]::-webkit-inner-spin-button':
{
WebkitAppearance: 'none',
margin: 0,
},
},
input: {
textAlign: 'right',
paddingRight: '2px',
paddingLeft: '2px',
paddingTop: '2px',
paddingBottom: '2px',
},
} as const

export function PointSearch({
isSmall = false,
className,
showControls = true,
}: Readonly<Props>) {
const dispatch = useDispatch()
const radius = usePointFilterRadius()

Expand All @@ -28,6 +69,11 @@ export function PointSearch({ isSmall = false, className }: Readonly<Props>) {
dispatch(clearActiveTool())
}

const onOk = () => {
dispatch(toggleActiveTool(ActiveToolEnum.pointSearch))
// No dispatches needed - just let the dropdown close naturally
}

const onRadiusChange = (_ev: any, value: number | number[]) => {
const newRadius = Math.max(
Array.isArray(value) ? value[0] : value,
Expand All @@ -36,57 +82,103 @@ export function PointSearch({ isSmall = false, className }: Readonly<Props>) {
dispatch(setPointFilterRadius(newRadius))
}

const onInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value =
event.target.value === '' ? 1 : parseInt(event.target.value, 10)
if (!isNaN(value)) {
const newRadius = Math.min(
Math.max(value * 1000, MIN_CIRCLE_RADIUS),
400000,
)
dispatch(setPointFilterRadius(newRadius))
}
}

const sliderBox = (
<div className="point-search-slider-content">
{isSmall && (
<Typography className="point-search-slider-text">
Set Radius:
</Typography>
)}
<Slider
className={clsx(
'point-search-slider',
isSmall && 'point-search-slider--shrink',
)}
aria-label="Search radius"
valueLabelDisplay="off"
min={MIN_CIRCLE_RADIUS}
// 500 km is roughly half the size of BC
max={500000}
step={MIN_CIRCLE_RADIUS}
defaultValue={MIN_CIRCLE_RADIUS}
value={radius}
onChange={onRadiusChange}
/>
<Typography className="point-search-slider-text">
{formatDistance(radius, 1)}
</Typography>
<div style={styles.sliderContainer}>
<div style={styles.sliderWrapper}>
<Slider
className={clsx(
'point-search-slider',
isSmall && 'point-search-slider--shrink',
)}
aria-label="Search radius"
valueLabelDisplay="off"
min={MIN_CIRCLE_RADIUS}
max={400000}
step={MIN_CIRCLE_RADIUS}
defaultValue={MIN_CIRCLE_RADIUS}
value={radius}
onChange={onRadiusChange}
/>
<div style={styles.labelContainer}>
<Typography variant="caption">1 km</Typography>
<Typography variant="caption">400 km</Typography>
</div>
</div>
<TextField
className="point-search-input"
size="small"
type="number"
sx={styles.textField}
value={Math.round(radius / 1000)}
onChange={onInputChange}
InputProps={{
endAdornment: <Typography variant="caption">km</Typography>,
inputProps: {
min: 1,
max: 400,
inputMode: 'numeric',
style: styles.input,
},
}}
/>
</div>
</div>
)

return isSmall ? (
sliderBox
) : (
<div className={clsx('point-search', className)}>
<Button
color="primary"
variant="contained"
size="medium"
onClick={onCancel}
startIcon={<CloseIcon className="point-cancel-icon" />}
>
Cancel
</Button>
<DropdownButton
id="pointSearchSetRadiusButton"
color="primary"
variant="contained"
size="medium"
menuClassName="point-search-menu"
dropdownContent={sliderBox}
>
Set Radius
</DropdownButton>
{showControls && (
<>
<Button
color="primary"
variant="contained"
size="medium"
onClick={onCancel}
startIcon={<CloseIcon className="point-cancel-icon" />}
>
Cancel
</Button>
<DropdownButton
id="pointSearchSetRadiusButton"
color="primary"
variant="contained"
size="medium"
menuClassName="point-search-menu"
dropdownContent={sliderBox}
>
Set Radius
</DropdownButton>
<Button
color="primary"
variant="contained"
size="medium"
onClick={onOk}
startIcon={<CheckIcon className="point-ok-icon" />}
>
Ok
</Button>
</>
)}
</div>
)
}
4 changes: 2 additions & 2 deletions frontend/src/pages/map/search/PointSearchButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ export function PointSearchButton({ isActive }: Readonly<Props>) {
)}
onClick={onClick}
startIcon={
<PointIcon title="Point search icon" className="point-search-icon" />
<PointIcon title="Radius search icon" className="point-search-icon" />
}
>
Point Search
Radius Search
</Button>
)
}

0 comments on commit 1339481

Please sign in to comment.