fix(maps): let an uploaded place image fill its marker

A place with an uploaded image showed a plain disc of its category
colour on the map, while a picked suggestion looked fine. The marker
sized its img with width and height attributes, and stylesheet rules
beat those: Tailwind's preflight gives every img height: auto, and
Leaflet's marker pane adds width: auto. Suggested and auto photos reach
the marker as 48px square thumbs, so nothing showed. A phone photo kept
its own proportions on the GL map, a sliver inside the circle, and its
full pixel size on Leaflet, where only the rounded-off corner of a
4000px image lands inside the circle. Both maps now build the img
through one helper that sizes it in its style, so object-fit: cover
crops any photo to the circle.

The thumb loading on both maps was keyed on place ids, so a change of
picture under the same place went unnoticed until a reload:
- Taking an upload off a place left the category dot instead of
  bringing its auto photo back.
- Picking another suggested photo kept the thumb of the first one,
  which was cached under the provider id. A picked photo is now its own
  cache key.
This commit is contained in:
Maurice
2026-09-19 13:35:44 +02:00
parent b88b92add7
commit 98e2938033
6 changed files with 195 additions and 19 deletions
@@ -1180,6 +1180,31 @@ describe('MapView photo thumbnails', () => {
expect(thumbCallbacks.size).toBe(0)
expect(vi.mocked(photoService.fetchPhoto)).not.toHaveBeenCalled()
})
it('FE-COMP-MAPVIEW-080: an uploaded photo fills its marker whatever its proportions', () => {
// Leaflet's marker pane sets `width: auto` on every img in it, so a photo sized
// by attributes was drawn at its full pixel size and the circle only ever showed
// the transparent corner of it, over the category colour.
render(<MapView places={[buildMapPlace({ id: 27, lat: 48, lng: 2, image_url: '/uploads/places/wide.jpg' })]} />)
const holder = document.createElement('div')
holder.innerHTML = iconHtmlOf(screen.getAllByTestId('marker')[0])
const img = holder.querySelector('img')!
expect(img.getAttribute('src')).toBe('/uploads/places/wide.jpg')
expect(img.style.width).toBe('100%')
expect(img.style.height).toBe('100%')
})
it('FE-COMP-MAPVIEW-081: taking the upload off a place asks for its auto photo again, without a reload', () => {
const place = buildMapPlace({ id: 28, lat: 48, lng: 2, google_place_id: 'gp-28', name: 'Tower', image_url: '/uploads/places/own.jpg' })
const { rerender } = render(<MapView places={[place]} />)
expect(vi.mocked(photoService.fetchPhoto)).not.toHaveBeenCalled()
rerender(<MapView places={[{ ...place, image_url: null }]} />)
expect(vi.mocked(photoService.fetchPhoto)).toHaveBeenCalledWith('gp-28', 'gp-28', 48, 2, 'Tower')
expect(thumbCallbacks.has('gp-28')).toBe(true)
})
})
// The marker HTML is a hand-built string handed to L.divIcon, i.e. innerHTML.