Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update map setting display #3862

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 24 additions & 18 deletions packages/components/src/MapWithPin/MapPin.client.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react'
import { useRef } from 'react'
import { Marker } from 'react-leaflet'
import L from 'leaflet'

import customMarkerIcon from '../../assets/icons/map-marker.png'

import type { DivIcon } from 'leaflet'

const customMarker = L.icon({
iconUrl: customMarkerIcon,
iconSize: [20, 28],
Expand All @@ -15,31 +17,35 @@ export interface IProps {
lat: number
lng: number
}
draggable: boolean
ondragend(lng: number): void
onDrag(lng: number): void
markerIcon?: DivIcon
onClick?: () => void
}

export const MapPin = (props: IProps) => {
const markerRef = React.useRef(null)
const markerRef = useRef(null)

const handleDrag = () => {
const marker: any = markerRef.current

if (!marker) {
return
}

const markerLatLng = marker.leafletElement.getLatLng()
if (props.onDrag) {
props.onDrag(markerLatLng)
}
}

return (
<Marker
draggable={props.draggable}
ondragend={() => {
const marker: any = markerRef.current

if (!marker) {
return null
}

const markerLatLng = marker.leafletElement.getLatLng()
if (props.ondragend) {
props.ondragend(markerLatLng)
}
}}
draggable
onDrag={handleDrag}
position={[props.position.lat, props.position.lng]}
ref={markerRef}
icon={customMarker}
icon={props.markerIcon || customMarker}
onclick={props.onClick}
/>
)
}
3 changes: 1 addition & 2 deletions packages/components/src/MapWithPin/MapPin.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ export const Default: StoryFn<typeof MapPin> = () => {
return (
<MapPin
position={position}
draggable={true}
ondragend={(lng: number) => {
onDrag={(lng: number) => {
position.lng = lng
}}
/>
Expand Down
142 changes: 78 additions & 64 deletions packages/components/src/MapWithPin/MapWithPin.client.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as React from 'react'
import { useState } from 'react'
import { ZoomControl } from 'react-leaflet'
import { Alert, Box, Flex, Text } from 'theme-ui'

Expand All @@ -7,33 +7,38 @@ import { Map } from '../Map/Map.client'
import { OsmGeocoding } from '../OsmGeocoding/OsmGeocoding'
import { MapPin } from './MapPin.client'

import type { LeafletMouseEvent } from 'leaflet'
import type { DivIcon, LeafletMouseEvent } from 'leaflet'
import type { Map as MapType } from 'react-leaflet'
import type { Result } from '../OsmGeocoding/types'

import 'leaflet/dist/leaflet.css'

const useUserLocation = 'Use my current location'
const mapInstructions =
"You can click on the map, or drag the marker to adjust it's position."
"To move your pin, grab it to move it or double click where you want it to go. Tap on your pin to see how it'll look on the map."

export interface Props {
mapRef: React.RefObject<MapType>
position: {
lat: number
lng: number
}
draggable: boolean
markerIcon?: DivIcon
updatePosition?: any
center?: any
zoom?: number
hasUserLocation?: boolean
onClickMapPin?: () => void
popup?: React.ReactNode
}

export const MapWithPin = (props: Props) => {
const [zoom, setZoom] = React.useState(props.zoom || 1)
const [center, setCenter] = React.useState(
const [dragging, setDragging] = useState<boolean>(false)
const [zoom, setZoom] = useState(props.zoom || 1)
const [center, setCenter] = useState(
props.center || [props.position.lat, props.position.lng],
)
const { draggable, position } = props
const { mapRef, position, markerIcon, onClickMapPin, popup } = props

const hasUserLocation = props.hasUserLocation || false
const onPositionChanged =
Expand All @@ -58,22 +63,20 @@ export const MapWithPin = (props: Props) => {
)
}

const onClick = (evt: LeafletMouseEvent) => {
const onDblClick = (evt: LeafletMouseEvent) => {
onPositionChanged({ ...evt.latlng })
}

return (
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
{draggable && (
<Alert
variant="info"
sx={{
marginTop: 2,
}}
>
<Text sx={{ fontSize: 1 }}>{mapInstructions}</Text>
</Alert>
)}
<Alert
variant="info"
sx={{
marginTop: 2,
}}
>
<Text sx={{ fontSize: 1 }}>{mapInstructions}</Text>
</Alert>
<div
style={{
position: 'relative',
Expand All @@ -84,66 +87,77 @@ export const MapWithPin = (props: Props) => {
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
padding: 2,
width: '100%',
zIndex: 2,
padding: 2,
bottom: 0,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
{draggable && (
<Flex style={{ width: '280px' }}>
<OsmGeocoding
callback={(data: Result) => {
if (data.lat && data.lon) {
onPositionChanged({
lat: data.lat,
lng: data.lon,
})
setCenter([data.lat, data.lon])
setZoom(15)
}
<Flex style={{ width: '280px' }}>
<OsmGeocoding
callback={(data: Result) => {
if (data.lat && data.lon) {
onPositionChanged({
lat: data.lat,
lng: data.lon,
})
setCenter([data.lat, data.lon])
setZoom(15)
}
}}
acceptLanguage="en"
/>
{hasUserLocation && (
<Button
type="button"
mx={2}
onClick={(evt) => {
evt.preventDefault()
setLocationToNavigatorLocation()
}}
countrycodes=""
acceptLanguage="en"
/>
{hasUserLocation && (
<Button
type="button"
mx={2}
onClick={(evt) => {
evt.preventDefault()
setLocationToNavigatorLocation()
}}
>
{useUserLocation}
</Button>
)}
</Flex>
)}
>
{useUserLocation}
</Button>
)}
</Flex>
</Box>
<Map
ref={mapRef}
className="markercluster-map settings-page"
center={center}
zoom={zoom}
zoomControl={false}
setZoom={setZoom}
onclick={onClick}
ondblclick={onDblClick}
doubleClickZoom={false}
style={{
height: '300px',
height: '500px',
zIndex: 1,
}}
ondragstart={() => setDragging(true)}
ondragend={() => setDragging(false)}
>
<ZoomControl position="topright" />
<MapPin
position={position}
draggable={draggable}
ondragend={(evt: any) => {
if (evt.lat && evt.lng)
onPositionChanged({
lat: evt.lat,
lng: evt.lng,
})
}}
/>
<ZoomControl position="bottomleft" />
{!dragging && (
<>
{popup}
<MapPin
position={position}
markerIcon={markerIcon}
onClick={onClickMapPin}
onDrag={(evt: any) => {
if (evt.lat && evt.lng)
onPositionChanged({
lat: evt.lat,
lng: evt.lng,
})
}}
/>
</>
)}
</Map>
</div>
</Flex>
Expand Down
7 changes: 6 additions & 1 deletion packages/components/src/MapWithPin/MapWithPin.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { useRef } from 'react'

import { MapWithPin } from './MapWithPin.client'

import type { Meta, StoryFn } from '@storybook/react'
import type { Map } from 'react-leaflet'

export default {
title: 'Map/MapWithPin',
Expand All @@ -9,10 +12,12 @@ export default {

export const Default: StoryFn<typeof MapWithPin> = () => {
const position = { lat: 0, lng: 0 }
const newMapRef = useRef<Map>(null)

return (
<MapWithPin
mapRef={newMapRef}
position={position}
draggable={true}
updatePosition={(_position: { lat: number; lng: number }) => {
position.lat = _position.lat
position.lng = _position.lng
Expand Down
13 changes: 10 additions & 3 deletions src/pages/Maps/Content/MapView/Popup.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MapMemberCard, PinProfile } from 'oa-components'
import { IModerationStatus } from 'oa-shared'
import { MAP_GROUPINGS } from 'src/stores/Maps/maps.groupings'

import type { IMapPin, IMapPinWithDetail } from 'oa-shared'
import type { ILatLng, IMapPin, IMapPinWithDetail } from 'oa-shared'
import type { Map } from 'react-leaflet'

import './popup.css'
Expand All @@ -15,12 +15,13 @@ interface IProps {
mapRef: React.RefObject<Map>
newMap?: boolean
onClose?: () => void
customPosition?: ILatLng
}

export const Popup = (props: IProps) => {
const leafletRef = useRef<LeafletPopup>(null)
const activePin = props.activePin as IMapPinWithDetail
const { mapRef, newMap, onClose } = props
const { mapRef, newMap, onClose, customPosition } = props

useEffect(() => {
openPopup()
Expand All @@ -47,8 +48,14 @@ export const Popup = (props: IProps) => {
activePin.location && (
<LeafletPopup
ref={leafletRef}
position={[activePin.location.lat, activePin.location.lng]}
position={
customPosition
? customPosition
: [activePin.location.lat, activePin.location.lng]
}
offset={new L.Point(2, -10)}
closeOnClick={false}
closeOnEscapeKey={false}
closeButton={false}
className={activePin !== undefined ? '' : 'closed'}
minWidth={230}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Maps/Content/MapView/Sprites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const createClusterIcon = () => {
}
}

export const createMarkerIcon = (pin: IMapPin) => {
export const createMarkerIcon = (pin: IMapPin, draggable?: boolean) => {
const icon =
pin.moderation === IModerationStatus.ACCEPTED
? Workspace.findWorkspaceBadge(pin.type, true, pin.verified)
Expand All @@ -45,7 +45,7 @@ export const createMarkerIcon = (pin: IMapPin) => {
}
return L.divIcon({
className: `icon-marker icon-${pin.type}`,
html: `<img data-cy="pin-${pin._id}" src="${icon}" />`,
html: `<img data-cy="pin-${pin._id}" src="${icon}" style="${draggable ? 'cursor: grab' : ''}" />`,
iconSize: L.point(38, 38, true),
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/User/user.routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { UserRole } from 'oa-shared'

import { AuthRoute } from '../common/AuthRoute'
import { NotFoundPage } from '../NotFound/NotFound'
import { SettingsPage } from '../UserSettings/SettingsPage'
import { SettingsPage } from '../UserSettings/SettingsPage.client'
import { UserProfile } from './content/UserProfile'

export const UserRoutes = (
Expand Down
15 changes: 15 additions & 0 deletions src/pages/UserSettings/SettingsPageMapPin.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ vi.mock('src/common/hooks/useCommonStores', () => ({
},
mapsStore: {
getPin: vi.fn().mockResolvedValue(mockPin),
getPinDetail: vi.fn().mockResolvedValue(mockPin),
},
themeStore: {
currentTheme: {
id: 'string',
siteName: 'string',
logo: 'string',
badge: 'string',
avatar: 'string',
howtoHeading: 'string',
academyResource: 'string',
styles: {
communityProgramURL: '',
},
},
},
},
}),
Expand Down
Loading
Loading