mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 07:26:30 +00:00
feat(maps): the phone trip map keeps its credit in the bottom right corner
The credit button sat in the middle of the round controls band, beside the locate button. On the trip map it now has the bottom right corner to itself, a gap above the dock (or above the stage bar on the road trip tab), and the band floats one credit row higher, so the switcher, the compass and the locate button stay on one line. MMapArea marks its layer as a credit corner and defines the floor the map ends at; mobile.css places both GL engines' compact control there, keeps an opened GL credit's button in the corner, moves the Mapbox wordmark into the same row instead of under the compass, and unfolds the Leaflet credit to the left of its button. MapAttributionToggle reads the same two variables with today's values as the fallback, so the other phone maps and the desktop keep their placement.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// FE-MAP-ATTRIB-001 to FE-MAP-ATTRIB-003
|
||||
import { fireEvent, render } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import MapAttributionToggle from './MapAttributionToggle'
|
||||
|
||||
// Outside a TranslationProvider t(key) echoes the key, so the label is asserted as its key.
|
||||
const BAND = 'calc(var(--bottom-nav-h, 84px) + 12px)'
|
||||
|
||||
describe('MapAttributionToggle', () => {
|
||||
it('FE-MAP-ATTRIB-001: it follows a credit corner where the map has one, and keeps the locate band where it has not', () => {
|
||||
const { getByRole } = render(<MapAttributionToggle open={false} onToggle={() => {}} bottomOffset={BAND} />)
|
||||
const style = getByRole('button').style
|
||||
|
||||
// Both numbers come from the map around it first. A map without a credit corner
|
||||
// defines neither variable, so the fallbacks are the old slot beside the locate button.
|
||||
expect(style.right).toBe('var(--m-credit-right, 62px)')
|
||||
expect(style.bottom).toBe(`var(--m-credit-bottom, ${BAND})`)
|
||||
})
|
||||
|
||||
it('FE-MAP-ATTRIB-002: it is labelled, says whether the credit is open, and toggles once per tap', () => {
|
||||
const onToggle = vi.fn()
|
||||
const { getByRole, rerender } = render(<MapAttributionToggle open={false} onToggle={onToggle} bottomOffset={BAND} />)
|
||||
const button = getByRole('button')
|
||||
|
||||
expect(button.getAttribute('title')).toBe('map.attribution')
|
||||
expect(button.getAttribute('aria-label')).toBe('map.attribution')
|
||||
expect(button.getAttribute('aria-expanded')).toBe('false')
|
||||
|
||||
fireEvent.click(button)
|
||||
expect(onToggle).toHaveBeenCalledTimes(1)
|
||||
|
||||
rerender(<MapAttributionToggle open onToggle={onToggle} bottomOffset={BAND} />)
|
||||
expect(getByRole('button').getAttribute('aria-expanded')).toBe('true')
|
||||
})
|
||||
|
||||
it('FE-MAP-ATTRIB-003: wherever it sits, it stays above the Leaflet control corners', () => {
|
||||
const { getByRole } = render(<MapAttributionToggle open={false} onToggle={() => {}} bottomOffset={BAND} />)
|
||||
const style = getByRole('button').style
|
||||
|
||||
// index.css pins `.leaflet-bottom` to z-index 1, and the open credit lives in that
|
||||
// corner. The (i) sits in the same row now, so it has to stay the one on top.
|
||||
expect(style.position).toBe('absolute')
|
||||
expect(style.zIndex).toBe('1000')
|
||||
})
|
||||
})
|
||||
@@ -5,7 +5,10 @@ import { useTranslation } from '../../i18n'
|
||||
interface MapAttributionToggleProps {
|
||||
open: boolean
|
||||
onToggle: () => void
|
||||
/** Same band as the locate button, so the two sit on one line. */
|
||||
/**
|
||||
* Where the (i) sits on a map that defines no credit corner: the locate button's band,
|
||||
* so the two share a line.
|
||||
*/
|
||||
bottomOffset: string
|
||||
}
|
||||
|
||||
@@ -34,9 +37,13 @@ export default function MapAttributionToggle({ open, onToggle, bottomOffset }: M
|
||||
aria-expanded={open}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: bottomOffset,
|
||||
// Clear of the locate button at right: 12 plus its 42, with a gap.
|
||||
right: 62,
|
||||
// A map that gives the credit a corner of its own (the phone trip map, see
|
||||
// `m-credit-corner` in mobile.css) places the (i) through these two variables, the
|
||||
// same two its GL engines are placed by. Every other map defines neither, so the
|
||||
// fallbacks keep the button where it always was: in the locate button's band,
|
||||
// clear of that button at right: 12 plus its 42, with a gap.
|
||||
bottom: `var(--m-credit-bottom, ${bottomOffset})`,
|
||||
right: 'var(--m-credit-right, 62px)',
|
||||
zIndex: 1000,
|
||||
width: 30,
|
||||
height: 30,
|
||||
|
||||
@@ -988,6 +988,32 @@ describe('MapView live location', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Follow my location' }))
|
||||
expect(geoMock.cycleMode).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('FE-COMP-MAPVIEW-077: the credit (i) is a phone control, and it opens the credit through a class on the wrapper', () => {
|
||||
const desktop = render(<MapView />)
|
||||
expect(screen.queryByRole('button', { name: 'Map credits' })).toBeNull()
|
||||
desktop.unmount()
|
||||
|
||||
Object.defineProperty(window, 'innerWidth', { configurable: true, writable: true, value: 420 })
|
||||
const { container } = render(<MapView />)
|
||||
const toggle = screen.getByRole('button', { name: 'Map credits' })
|
||||
const wrapper = container.querySelector('div.w-full.h-full.relative') as HTMLElement
|
||||
// Starts closed: on a phone the map is the screen, and the credit is one tap away.
|
||||
expect(toggle.getAttribute('aria-expanded')).toBe('false')
|
||||
expect(wrapper.classList.contains('m-attrib-open')).toBe(false)
|
||||
// The locate button's band is only the fallback; a credit corner around the map wins.
|
||||
expect(toggle.style.bottom).toBe('var(--m-credit-bottom, calc(var(--bottom-nav-h, 84px) + 12px))')
|
||||
|
||||
// A class on the wrapper rather than markup in Leaflet's own container, which Leaflet
|
||||
// rewrites whenever a layer with an attribution comes or goes.
|
||||
fireEvent.click(toggle)
|
||||
expect(toggle.getAttribute('aria-expanded')).toBe('true')
|
||||
expect(wrapper.classList.contains('m-attrib-open')).toBe(true)
|
||||
|
||||
fireEvent.click(toggle)
|
||||
expect(toggle.getAttribute('aria-expanded')).toBe('false')
|
||||
expect(wrapper.classList.contains('m-attrib-open')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('MapView bounds fitting', () => {
|
||||
|
||||
Reference in New Issue
Block a user