Compare commits

..

No commits in common. "51e41b7bcebe0b6022d4becdc972d2db2309b999" and "b0eb263d93216d3b4591a33dbb9cb11d639e18dc" have entirely different histories.

12 changed files with 317 additions and 61 deletions

View file

@ -1,13 +1,37 @@
import { uMapAlert as Alert } from '../components/alerts/alert.js' import {
import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js' uMapAlert as Alert,
uMapAlertCreation as AlertCreation,
} from '../components/alerts/alert.js'
import {
AjaxAutocomplete,
AjaxAutocompleteMultiple,
AutocompleteDatalist,
} from './autocomplete.js'
import Browser from './browser.js'
import Caption from './caption.js'
import ContextMenu from './ui/contextmenu.js'
import Facets from './facets.js'
import { Formatter } from './formatter.js'
import Help from './help.js' import Help from './help.js'
import { ServerRequest } from './request.js' import Importer from './importer.js'
import Orderable from './orderable.js'
import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
import Rules from './rules.js'
import { SCHEMA } from './schema.js' import { SCHEMA } from './schema.js'
import Share from './share.js'
import Slideshow from './slideshow.js'
import { SyncEngine } from './sync/engine.js'
import Dialog from './ui/dialog.js'
import { EditPanel, FullPanel, Panel } from './ui/panel.js'
import Tooltip from './ui/tooltip.js'
import URLs from './urls.js'
import * as Utils from './utils.js' import * as Utils from './utils.js'
import * as Icon from './rendering/icon.js' import * as Icon from './rendering/icon.js'
import { LAYER_TYPES } from './data/layer.js' import { DataLayer, LAYER_TYPES } from './data/layer.js'
import { DataLayerPermissions, MapPermissions } from './permissions.js'
import { Point, LineString, Polygon } from './data/features.js' import { Point, LineString, Polygon } from './data/features.js'
import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.js' import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.js'
import * as SAVEMANAGER from './saving.js'
// Import modules and export them to the global scope. // Import modules and export them to the global scope.
// For the not yet module-compatible JS out there. // For the not yet module-compatible JS out there.
@ -15,18 +39,45 @@ import { LeafletMarker, LeafletPolyline, LeafletPolygon } from './rendering/ui.j
// By alphabetic order // By alphabetic order
window.U = { window.U = {
Alert, Alert,
AlertCreation,
AjaxAutocomplete, AjaxAutocomplete,
AjaxAutocompleteMultiple, AjaxAutocompleteMultiple,
AutocompleteDatalist,
Browser,
Caption,
ContextMenu,
DataLayer,
DataLayerPermissions,
Dialog,
EditPanel,
Facets,
Formatter,
FullPanel,
Help, Help,
HTTPError,
Icon, Icon,
Importer,
LAYER_TYPES, LAYER_TYPES,
LeafletMarker, LeafletMarker,
LeafletPolygon, LeafletPolygon,
LeafletPolyline, LeafletPolyline,
LineString, LineString,
MapPermissions,
NOKError,
Orderable,
Panel,
Point, Point,
Polygon, Polygon,
Request,
RequestError,
Rules,
SAVEMANAGER,
SCHEMA, SCHEMA,
ServerRequest, ServerRequest,
Share,
Slideshow,
SyncEngine,
Tooltip,
URLs,
Utils, Utils,
} }

View file

@ -1,7 +1,6 @@
import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js' import { DomEvent, DomUtil } from '../../vendors/leaflet/leaflet-src.esm.js'
import { translate } from './i18n.js' import { translate } from './i18n.js'
import * as Utils from './utils.js' import * as Utils from './utils.js'
import Dialog from './ui/dialog.js'
const SHORTCUTS = { const SHORTCUTS = {
DRAW_MARKER: { DRAW_MARKER: {
@ -167,7 +166,7 @@ const ENTRIES = {
export default class Help { export default class Help {
constructor(umap) { constructor(umap) {
this.umap = umap this.umap = umap
this.dialog = new Dialog({ className: 'dark', accept: false, cancel: false }) this.dialog = new U.Dialog({ className: 'dark', accept: false, cancel: false })
this.isMacOS = /mac/i.test( this.isMacOS = /mac/i.test(
// eslint-disable-next-line compat/compat -- Fallback available. // eslint-disable-next-line compat/compat -- Fallback available.
navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform navigator.userAgentData ? navigator.userAgentData.platform : navigator.platform

View file

@ -251,6 +251,7 @@ export default class Importer {
} }
full() { full() {
this.umap._leafletMap.once('postsync', this.umap._leafletMap._setDefaultCenter)
try { try {
if (this.files.length) { if (this.files.length) {
for (const file of this.files) { for (const file of this.files) {

View file

@ -193,6 +193,7 @@ export class MapPermissions extends ServerStored {
) )
if (!error) { if (!error) {
this.commit() this.commit()
this.umap._leafletMap.fire('postsync')
return true return true
} }
} }

View file

@ -12,7 +12,6 @@ import { translate } from '../i18n.js'
import { uMapAlert as Alert } from '../../components/alerts/alert.js' import { uMapAlert as Alert } from '../../components/alerts/alert.js'
import * as Utils from '../utils.js' import * as Utils from '../utils.js'
import * as Icon from './icon.js' import * as Icon from './icon.js'
import ContextMenu from '../ui/contextmenu.js'
// Those options are not saved on the server, so they can live here // Those options are not saved on the server, so they can live here
// instead of in umap.properties // instead of in umap.properties
@ -204,7 +203,7 @@ const ControlsMixin = {
</button> </button>
`) `)
rightContainer.appendChild(button) rightContainer.appendChild(button)
const menu = new ContextMenu({ className: 'dark', fixed: true }) const menu = new U.ContextMenu({ className: 'dark', fixed: true })
const actions = [ const actions = [
{ {
label: translate('New map'), label: translate('New map'),
@ -253,7 +252,7 @@ const ControlsMixin = {
}) })
const updateConnectedPeersCount = () => { const updateConnectedPeersCount = () => {
connectedPeersCount.innerHTML = this.umap.sync.getNumberOfConnectedPeers() connectedPeersCount.innerHTML = this.sync.getNumberOfConnectedPeers()
} }
updateConnectedPeersCount() updateConnectedPeersCount()
} }
@ -307,12 +306,8 @@ const ControlsMixin = {
}) })
}, },
renderCaptionBar: function () { initCaptionBar: function () {
if (this.options.noControl) return const container = DomUtil.create('div', 'umap-caption-bar', this._controlContainer)
const container =
this._controlContainer.querySelector('.umap-caption-bar') ||
DomUtil.create('div', 'umap-caption-bar', this._controlContainer)
container.innerHTML = ''
const name = DomUtil.create('h3', 'map-name', container) const name = DomUtil.create('h3', 'map-name', container)
DomEvent.disableClickPropagation(container) DomEvent.disableClickPropagation(container)
this.umap.addAuthorLink(container) this.umap.addAuthorLink(container)
@ -321,7 +316,8 @@ const ControlsMixin = {
'umap-about-link flat', 'umap-about-link flat',
container, container,
translate('Open caption'), translate('Open caption'),
() => this.umap.openCaption() this.umap.openCaption,
this
) )
DomUtil.createButton( DomUtil.createButton(
'umap-open-browser-link flat', 'umap-open-browser-link flat',
@ -338,8 +334,8 @@ const ControlsMixin = {
) )
} }
} }
this.umap.onceDatalayersLoaded(() => { this.umap.onceDatalayersLoaded(function () {
this.umap.slideshow.renderToolbox(container) this.slideshow.renderToolbox(container)
}) })
}, },
} }
@ -485,18 +481,11 @@ export const LeafletMap = BaseMap.extend({
}) })
}, },
setup: function () { attachToDom: function () {
this.initControls() this.initControls()
// Needs locate control and hash to exist // Needs locate control and hash to exist
this.initCenter() this.initCenter()
this.update()
},
update: function () {
this.setOptions(this.umap.properties)
this.initTileLayers() this.initTileLayers()
this.renderCaptionBar()
this.renderEditToolbar()
// Needs tilelayer to exist for minimap // Needs tilelayer to exist for minimap
this.renderControls() this.renderControls()
this.handleLimitBounds() this.handleLimitBounds()

View file

@ -2,7 +2,6 @@ import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.
import { translate } from './i18n.js' import { translate } from './i18n.js'
import * as Utils from './utils.js' import * as Utils from './utils.js'
import { AutocompleteDatalist } from './autocomplete.js' import { AutocompleteDatalist } from './autocomplete.js'
import Orderable from './orderable.js'
const EMPTY_VALUES = ['', undefined, null] const EMPTY_VALUES = ['', undefined, null]
@ -237,7 +236,7 @@ export default class Rules {
rule.renderToolbox(DomUtil.create('li', 'orderable', ul)) rule.renderToolbox(DomUtil.create('li', 'orderable', ul))
} }
const orderable = new Orderable(ul, this.onReorder.bind(this)) const orderable = new U.Orderable(ul, this.onReorder.bind(this))
} }
DomUtil.createButton('umap-add', body, translate('Add rule'), this.addRule, this) DomUtil.createButton('umap-add', body, translate('Add rule'), this.addRule, this)

View file

@ -83,7 +83,7 @@ export default class Slideshow extends WithTemplate {
play() { play() {
if (this._id) return if (this._id) return
if (this.umap.editEnabled || !this.umap.properties.slideshow.active) return if (this.umap.editEnabled || !this.umap.options.slideshow.active) return
L.DomUtil.addClass(document.body, this.CLASSNAME) L.DomUtil.addClass(document.body, this.CLASSNAME)
this._id = window.setInterval(L.bind(this.loop, this), this.options.delay) this._id = window.setInterval(L.bind(this.loop, this), this.options.delay)
this.startSpinner() this.startSpinner()

View file

@ -6,8 +6,8 @@ import { fieldInSchema } from '../utils.js'
*/ */
class BaseUpdater { class BaseUpdater {
constructor(umap) { constructor(map) {
this.umap = umap this.map = map
} }
updateObjectValue(obj, key, value) { updateObjectValue(obj, key, value) {
@ -32,8 +32,8 @@ class BaseUpdater {
} }
getDataLayerFromID(layerId) { getDataLayerFromID(layerId) {
if (layerId) return this.umap.getDataLayerByUmapId(layerId) if (layerId) return this.map.getDataLayerByUmapId(layerId)
return this.umap.defaultEditDataLayer() return this.map.defaultEditDataLayer()
} }
applyMessage(payload) { applyMessage(payload) {
@ -45,18 +45,18 @@ class BaseUpdater {
export class MapUpdater extends BaseUpdater { export class MapUpdater extends BaseUpdater {
update({ key, value }) { update({ key, value }) {
if (fieldInSchema(key)) { if (fieldInSchema(key)) {
this.updateObjectValue(this.umap, key, value) this.updateObjectValue(this.map, key, value)
} }
this.umap.render([key]) this.map.render([key])
} }
} }
export class DataLayerUpdater extends BaseUpdater { export class DataLayerUpdater extends BaseUpdater {
upsert({ value }) { upsert({ value }) {
// Inserts does not happen (we use multiple updates instead). // Inserts does not happen (we use multiple updates instead).
this.umap.createDataLayer(value, false) this.map.createDataLayer(value, false)
this.umap.render([]) this.map.render([])
} }
update({ key, metadata, value }) { update({ key, metadata, value }) {

View file

@ -32,7 +32,6 @@ import {
uMapAlertCreation as AlertCreation, uMapAlertCreation as AlertCreation,
uMapAlert as Alert, uMapAlert as Alert,
} from '../components/alerts/alert.js' } from '../components/alerts/alert.js'
import Orderable from './orderable.js'
export default class Umap extends ServerStored { export default class Umap extends ServerStored {
constructor(element, geojson) { constructor(element, geojson) {
@ -86,16 +85,11 @@ export default class Umap extends ServerStored {
if (center) { if (center) {
this._leafletMap.options.center = this._leafletMap.latLng(center) this._leafletMap.options.center = this._leafletMap.latLng(center)
} }
this._leafletMap.attachToDom()
// Needed to render controls
this.permissions = new MapPermissions(this)
this.urls = new URLs(this.properties.urls)
this.slideshow = new Slideshow(this, this.properties.slideshow)
this._leafletMap.setup()
if (geojson.properties.schema) this.overrideSchema(geojson.properties.schema) if (geojson.properties.schema) this.overrideSchema(geojson.properties.schema)
this.urls = new URLs(this.properties.urls)
this.panel = new Panel(this) this.panel = new Panel(this)
this.dialog = new Dialog({ className: 'dark' }) this.dialog = new Dialog({ className: 'dark' })
@ -177,12 +171,15 @@ export default class Umap extends ServerStored {
await this.loadDataFromQueryString() await this.loadDataFromQueryString()
} }
this.slideshow = new Slideshow(this, this.properties.slideshow)
this.permissions = new MapPermissions(this)
if (this.hasEditMode()) { if (this.hasEditMode()) {
this._leafletMap.initEditTools() this._leafletMap.initEditTools()
} }
if (!this.properties.noControl) { if (!this.properties.noControl) {
this.initShortcuts() this.initShortcuts()
this._leafletMap.initCaptionBar()
this._leafletMap.on('contextmenu', this.onContextMenu) this._leafletMap.on('contextmenu', this.onContextMenu)
this.onceDataLoaded(this.setViewFromQueryString) this.onceDataLoaded(this.setViewFromQueryString)
this.propagate() this.propagate()
@ -1248,7 +1245,7 @@ export default class Umap extends ServerStored {
render(fields) { render(fields) {
if (fields.includes('numberOfConnectedPeers')) { if (fields.includes('numberOfConnectedPeers')) {
this._leafletMap.renderEditToolbar() this.renderEditToolbar()
this.propagate() this.propagate()
} }
@ -1256,7 +1253,10 @@ export default class Umap extends ServerStored {
for (const impact of impacts) { for (const impact of impacts) {
switch (impact) { switch (impact) {
case 'ui': case 'ui':
this._leafletMap.update() this._leafletMap.setOptions(this.properties)
this._leafletMap.initCaptionBar()
this._leafletMap.renderEditToolbar()
this._leafletMap.renderControls()
this.browser.redraw() this.browser.redraw()
this.propagate() this.propagate()
break break
@ -1383,7 +1383,7 @@ export default class Umap extends ServerStored {
}) })
this.indexDatalayers() this.indexDatalayers()
} }
const orderable = new Orderable(ul, onReorder) const orderable = new U.Orderable(ul, onReorder)
const bar = DomUtil.create('div', 'button-bar', container) const bar = DomUtil.create('div', 'button-bar', container)
DomUtil.createButton( DomUtil.createButton(
@ -1499,13 +1499,17 @@ export default class Umap extends ServerStored {
dataLayer.fromUmapGeoJSON(geojson) dataLayer.fromUmapGeoJSON(geojson)
} }
this._leafletMap.update() // TODO: refactor with leafletMap init / render
this._leafletMap.setOptions(this.properties)
this._leafletMap.initTileLayers()
this._leafletMap.renderControls()
this._leafletMap.handleLimitBounds()
this.eachDataLayer((datalayer) => { this.eachDataLayer((datalayer) => {
if (mustReindex) datalayer.reindex() if (mustReindex) datalayer.reindex()
datalayer.redraw() datalayer.redraw()
}) })
this.propagate() this.propagate()
this._leafletMap._setDefaultCenter() this._leafletMap.fire('postsync')
this.isDirty = true this.isDirty = true
} }

View file

@ -66,10 +66,7 @@ export function getImpactsFromSchema(fields, schema) {
export function fieldInSchema(field, schema) { export function fieldInSchema(field, schema) {
const current_schema = schema || U.SCHEMA const current_schema = schema || U.SCHEMA
if (typeof field !== 'string') return false if (typeof field !== 'string') return false
const field_name = field const field_name = field.replace('options.', '').split('.')[0]
.replace('options.', '')
.replace('properties.', '')
.split('.')[0]
return current_schema[field_name] !== undefined return current_schema[field_name] !== undefined
} }

215
umap/static/umap/js/umap.js Normal file
View file

@ -0,0 +1,215 @@
L.Map.mergeOptions({
overlay: {},
datalayers: [],
maxZoomLimit: 24,
attributionControl: false,
editMode: 'advanced',
noControl: false, // Do not render any control.
name: '',
description: '',
// When a TileLayer is in TMS mode, it needs -y instead of y.
// This is usually handled by the TileLayer instance itself, but
// we cannot rely on this because of the y is overriden by Leaflet
// See https://github.com/Leaflet/Leaflet/pull/9201
// And let's remove this -y when this PR is merged and released.
demoTileInfos: { s: 'a', z: 9, x: 265, y: 181, '-y': 181, r: '' },
licences: [],
licence: '',
slideshow: {},
clickable: true,
permissions: {},
featuresHaveOwner: false,
})
U.Map = L.Map.extend({
includes: [ControlsMixin],
initialize: async function (el, geojson) {
this.sync_engine = new U.SyncEngine(this)
this.sync = this.sync_engine.proxy(this)
// Locale name (pt_PT, en_US…)
// To be used for Django localization
if (geojson.properties.locale) L.setLocale(geojson.properties.locale)
// Language code (pt-pt, en-us…)
// To be used in javascript APIs
if (geojson.properties.lang) L.lang = geojson.properties.lang
this.setOptionsFromQueryString(geojson.properties)
// Prevent default creation of controls
const zoomControl = geojson.properties.zoomControl
const fullscreenControl = geojson.properties.fullscreenControl
geojson.properties.zoomControl = false
geojson.properties.fullscreenControl = false
L.Map.prototype.initialize.call(this, el, geojson.properties)
if (geojson.properties.schema) this.overrideSchema(geojson.properties.schema)
// After calling parent initialize, as we are doing initCenter our-selves
if (geojson.geometry) this.options.center = this.latLng(geojson.geometry)
this.urls = new U.URLs(this.options.urls)
this.panel = new U.Panel(this)
this.dialog = new U.Dialog({ className: 'dark' })
this.tooltip = new U.Tooltip(this._controlContainer)
this.contextmenu = new U.ContextMenu()
if (this.hasEditMode()) {
this.editPanel = new U.EditPanel(this)
this.fullPanel = new U.FullPanel(this)
}
if (!this.options.noControl) {
L.DomEvent.on(document.body, 'dataloading', (e) => this.fire('dataloading', e))
L.DomEvent.on(document.body, 'dataload', (e) => this.fire('dataload', e))
}
this.server = new U.ServerRequest()
this.request = new U.Request()
this.initLoader()
this.name = this.options.name
this.description = this.options.description
this.demoTileInfos = this.options.demoTileInfos
this.options.zoomControl = zoomControl !== undefined ? zoomControl : true
this.options.fullscreenControl =
fullscreenControl !== undefined ? fullscreenControl : true
this.datalayersFromQueryString = L.Util.queryString('datalayers')
if (this.datalayersFromQueryString) {
this.datalayersFromQueryString = this.datalayersFromQueryString
.toString()
.split(',')
}
let editedFeature = null
try {
Object.defineProperty(this, 'editedFeature', {
get: () => editedFeature,
set: (feature) => {
if (editedFeature && editedFeature !== feature) {
editedFeature.endEdit()
}
editedFeature = feature
this.fire('seteditedfeature')
},
})
} catch (e) {
// Certainly IE8, which has a limited version of defineProperty
}
// Retrocompat
if (this.options.slideshow?.delay && this.options.slideshow.active === undefined) {
this.options.slideshow.active = true
}
if (this.options.advancedFilterKey) {
this.options.facetKey = this.options.advancedFilterKey
delete this.options.advancedFilterKey
}
// Global storage for retrieving datalayers and features.
this.datalayers = {} // All datalayers, including deleted.
this.datalayers_index = [] // Datalayers actually on the map and ordered.
this.features_index = {}
// Needed for actions labels
this.help = new U.Help(this)
this.formatter = new U.Formatter(this)
this.initControls()
// Needs locate control and hash to exist
this.initCenter()
this.initTileLayers()
// Needs tilelayer to exist for minimap
this.renderControls()
this.handleLimitBounds()
this.initDataLayers()
if (this.options.displayCaptionOnLoad) {
// Retrocompat
if (!this.options.onLoadPanel) {
this.options.onLoadPanel = 'caption'
}
delete this.options.displayCaptionOnLoad
}
if (this.options.displayDataBrowserOnLoad) {
// Retrocompat
if (!this.options.onLoadPanel) {
this.options.onLoadPanel = 'databrowser'
}
delete this.options.displayDataBrowserOnLoad
}
if (this.options.datalayersControl === 'expanded') {
if (!this.options.onLoadPanel) {
this.options.onLoadPanel = 'datalayers'
}
delete this.options.datalayersControl
}
if (this.options.onLoadPanel === 'facet') {
this.options.onLoadPanel = 'datafilters'
}
// TODO: remove me when moved to modules
// and inheriting from ServerStored
try {
Object.defineProperty(this, 'isDirty', {
get: () => U.SAVEMANAGER.has(this),
set: (status) => {
if (status) {
U.SAVEMANAGER.add(this)
} else {
U.SAVEMANAGER.remove(this)
}
},
})
} catch (e) {
// Certainly IE8, which has a limited version of defineProperty
}
this.on(
'baselayerchange',
function (e) {
if (this._controls.miniMap) this._controls.miniMap.onMainMapBaseLayerChange(e)
},
this
)
// Creation mode
if (!this.options.umap_id) {
if (!this.options.preview) {
this.isDirty = true
this.enableEdit()
}
this._default_extent = true
this.options.name = L._('Untitled map')
await this.loadDataFromQueryString()
}
this.slideshow = new U.Slideshow(this, this.options.slideshow)
this.permissions = new U.MapPermissions(this)
if (this.hasEditMode()) {
this.editTools = new U.Editable(this)
this.renderEditToolbar()
}
if (!this.options.noControl) {
this.initShortcuts()
this.initCaptionBar()
this.on('contextmenu', this.onContextMenu)
this.onceDataLoaded(this.setViewFromQueryString)
this.on('click', this.closeInplaceToolbar)
this.propagate()
}
window.onbeforeunload = () => (this.editEnabled && U.SAVEMANAGER.isDirty) || null
this.backup()
},
getFeatureById: function (id) {
let feature
for (const datalayer of this.datalayers_index) {
feature = datalayer.getFeatureById(id)
if (feature) return feature
}
},
})

View file

@ -161,7 +161,7 @@ def test_websocket_connection_can_sync_polygons(
@pytest.mark.xdist_group(name="websockets") @pytest.mark.xdist_group(name="websockets")
def test_websocket_connection_can_sync_map_properties( def test_websocket_connection_can_sync_map_properties(
new_page, live_server, websocket_server, tilelayer context, live_server, websocket_server, tilelayer
): ):
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS) map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
map.settings["properties"]["syncEnabled"] = True map.settings["properties"]["syncEnabled"] = True
@ -169,9 +169,9 @@ def test_websocket_connection_can_sync_map_properties(
DataLayerFactory(map=map, data={}) DataLayerFactory(map=map, data={})
# Create two tabs # Create two tabs
peerA = new_page() peerA = context.new_page()
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit") peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
peerB = new_page() peerB = context.new_page()
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit") peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
# Name change is synced # Name change is synced
@ -193,7 +193,7 @@ def test_websocket_connection_can_sync_map_properties(
@pytest.mark.xdist_group(name="websockets") @pytest.mark.xdist_group(name="websockets")
def test_websocket_connection_can_sync_datalayer_properties( def test_websocket_connection_can_sync_datalayer_properties(
new_page, live_server, websocket_server, tilelayer context, live_server, websocket_server, tilelayer
): ):
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS) map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
map.settings["properties"]["syncEnabled"] = True map.settings["properties"]["syncEnabled"] = True
@ -201,9 +201,9 @@ def test_websocket_connection_can_sync_datalayer_properties(
DataLayerFactory(map=map, data={}) DataLayerFactory(map=map, data={})
# Create two tabs # Create two tabs
peerA = new_page() peerA = context.new_page()
peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit") peerA.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
peerB = new_page() peerB = context.new_page()
peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit") peerB.goto(f"{live_server.url}{map.get_absolute_url()}?edit")
# Layer addition, name and type are synced # Layer addition, name and type are synced
@ -215,7 +215,7 @@ def test_websocket_connection_can_sync_datalayer_properties(
peerA.locator("body").press("Escape") peerA.locator("body").press("Escape")
peerB.get_by_role("link", name="Manage layers").click() peerB.get_by_role("link", name="Manage layers").click()
peerB.locator(".panel.right").get_by_role("button", name="Edit").first.click() peerB.get_by_role("button", name="Edit").first.click()
expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!") expect(peerB.locator('input[name="name"]')).to_have_value("synced layer!")
expect(peerB.get_by_role("combobox")).to_have_value("Choropleth") expect(peerB.get_by_role("combobox")).to_have_value("Choropleth")