Skip to content

Commit

Permalink
feat: custom map markers with thumbnails
Browse files Browse the repository at this point in the history
  • Loading branch information
pozil committed Oct 2, 2023
1 parent b610b29 commit 8b27b96
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,46 @@ const MOCK_PROPERTIES = {

// Sample error for loadScript error
const LOAD_SCRIPT_ERROR = {
body: { message: 'An internal server error has occurred' },
body: { message: 'Mock load script error has occurred' },
ok: false,
status: 400,
statusText: 'Bad Request'
};

const LEAFLET_STUB = {
map: () => ({
setView: () => {},
scrollWheelZoom: {
disable: () => {}
},
removeLayer: () => {}
}),
tileLayer: () => ({
addTo: () => {}
}),
divIcon: () => {},
marker: () => ({
on: () => {},
bindTooltip: () => {}
}),
layerGroup: () => ({
addTo: () => {}
})
};

describe('c-property-list-map', () => {
beforeEach(() => {
// Inject Leaflet stub as a global 'L' variable
global.L = LEAFLET_STUB;
});

afterEach(() => {
// The jsdom instance is shared across test cases in a single file so reset the DOM
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
// Clear mocks so that every test run has a clean implementation
jest.clearAllMocks();
// Reset mocks so that every test run has a clean implementation
jest.resetAllMocks();
// Clear leaflet global
global.L = undefined;
});
Expand Down Expand Up @@ -106,7 +132,7 @@ describe('c-property-list-map', () => {
// Check if toast event has been fired
expect(handler).toHaveBeenCalled();
expect(handler.mock.calls[0][0].detail.title).toBe(
'Error loading Leaflet'
'Error while loading Leaflet'
);
expect(handler.mock.calls[0][0].detail.variant).toBe('error');
});
Expand Down Expand Up @@ -140,7 +166,8 @@ describe('c-property-list-map', () => {
it('updates map when properties are received', async () => {
// Mock leaflet and add it as a global 'L' variable
const markerMock = jest.fn(() => ({
on: () => {}
on: () => {},
bindTooltip: () => {}
}));
const layerGroupAddToMock = jest.fn();
const leafletMock = {
Expand Down Expand Up @@ -174,6 +201,9 @@ describe('c-property-list-map', () => {
// Emit mock properties
getPagedPropertyList.emit(MOCK_PROPERTIES);

// Wait for any asynchronous DOM updates
await flushPromises();

// Check that markers are set up with property data
expect(markerMock).toHaveBeenCalledTimes(
MOCK_PROPERTIES.records.length
Expand Down
31 changes: 31 additions & 0 deletions force-app/main/default/lwc/propertyListMap/propertyListMap.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
.map {
height: 550px;
}

.tooltip-picture {
position: relative;
width: 150px;
height: 150px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}

.tooltip-picture .lower-third {
position: absolute;
/* @sldsValidatorIgnore */
bottom: 0;
/* @sldsValidatorIgnore */
left: 0;
/* @sldsValidatorIgnore */
right: 0;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 0.15rem;
}

.lower-third h1 {
font-weight: bold;
/* @sldsValidatorIgnore */
white-space: nowrap;
overflow: hidden;
/* @sldsValidatorIgnore */
text-overflow: ellipsis;
}
70 changes: 46 additions & 24 deletions force-app/main/default/lwc/propertyListMap/propertyListMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import LEAFLET from '@salesforce/resourceUrl/leafletjs';
import getPagedPropertyList from '@salesforce/apex/PropertyController.getPagedPropertyList';

const LEAFLET_NOT_LOADED = 0;
const LEAFLET_LOADING = 1;
const LEAFLET_READY = 2;

export default class PropertyListMap extends LightningElement {
properties = [];

// Map
isLeafletInitialized = false;
leafletState = LEAFLET_NOT_LOADED;
map;
propertyLayer;

Expand Down Expand Up @@ -73,49 +77,56 @@ export default class PropertyListMap extends LightningElement {
}

async renderedCallback() {
if (this.isLeafletInitialized) {
return;
if (this.leafletState === LEAFLET_NOT_LOADED) {
await this.initializeLeaflet();
}
this.isLeafletInitialized = true;
}

async initializeLeaflet() {
try {
// Leaflet is loading
this.leafletState = LEAFLET_LOADING;

// Load resource files
await Promise.all([
loadScript(this, `${LEAFLET}/leaflet.js`),
loadStyle(this, `${LEAFLET}/leaflet.css`)
]);
this.initializeLeaflet();

// Configure map
const mapElement = this.template.querySelector('.map');
this.map = L.map(mapElement, {
zoomControl: true,
tap: false
// eslint-disable-next-line no-magic-numbers
});
this.map.setView([42.356045, -71.08565], 13);
this.map.scrollWheelZoom.disable();
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(this.map);

// Leaflet is ready
this.leafletState = LEAFLET_READY;

// Display properties
this.displayProperties();
} catch (error) {
const message = error.message || error.body.message;
this.dispatchEvent(
new ShowToastEvent({
title: 'Error loading Leaflet',
title: 'Error while loading Leaflet',
message,
variant: 'error'
})
);
}
}

initializeLeaflet() {
const mapElement = this.template.querySelector('.map');
this.map = L.map(mapElement, {
zoomControl: true,
tap: false
// eslint-disable-next-line no-magic-numbers
}).setView([42.356045, -71.08565], 13);
this.map.scrollWheelZoom.disable();
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(this.map);

// Display properties
this.displayProperties();
}

displayProperties() {
// Stop if leaflet isn't ready yet
if (!this.isLeafletInitialized) {
if (this.leafletState !== LEAFLET_READY) {
return;
}

Expand Down Expand Up @@ -143,9 +154,11 @@ export default class PropertyListMap extends LightningElement {
property.Location__Latitude__s,
property.Location__Longitude__s
];
const tooltipMarkup = this.getTooltipMarkup(property);
const marker = L.marker(latLng, { icon });
marker.propertyId = property.Id;
marker.on('click', markerClickHandler);
marker.bindTooltip(tooltipMarkup, { offset: [45, -40] });
return marker;
});

Expand All @@ -160,4 +173,13 @@ export default class PropertyListMap extends LightningElement {
this.minBedrooms = filters.minBedrooms;
this.minBathrooms = filters.minBathrooms;
}

getTooltipMarkup(property) {
return `<div class="tooltip-picture" style="background-image:url(${property.Thumbnail__c})">
<div class="lower-third">
<h1>${property.Name}</h1>
<p>Beds: ${property.Beds__c} - Baths: ${property.Baths__c}</p>
</div>
</div>`;
}
}

0 comments on commit 8b27b96

Please sign in to comment.