umap-project 3.0.4__py3-none-any.whl → 3.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (164) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/el/LC_MESSAGES/django.po +136 -56
  4. umap/locale/en/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/en/LC_MESSAGES/django.po +18 -18
  6. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/es/LC_MESSAGES/django.po +136 -56
  8. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  9. umap/locale/it/LC_MESSAGES/django.po +143 -63
  10. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/nl/LC_MESSAGES/django.po +1 -1
  12. umap/models.py +1 -0
  13. umap/settings/base.py +1 -0
  14. umap/static/umap/css/bar.css +1 -1
  15. umap/static/umap/css/tooltip.css +13 -0
  16. umap/static/umap/js/modules/autocomplete.js +7 -8
  17. umap/static/umap/js/modules/browser.js +89 -94
  18. umap/static/umap/js/modules/caption.js +6 -4
  19. umap/static/umap/js/modules/data/features.js +1 -19
  20. umap/static/umap/js/modules/data/layer.js +100 -61
  21. umap/static/umap/js/modules/facets.js +1 -1
  22. umap/static/umap/js/modules/form/fields.js +1 -1
  23. umap/static/umap/js/modules/help.js +4 -0
  24. umap/static/umap/js/modules/importer.js +1 -1
  25. umap/static/umap/js/modules/managers.js +46 -0
  26. umap/static/umap/js/modules/permissions.js +1 -1
  27. umap/static/umap/js/modules/rendering/controls.js +251 -0
  28. umap/static/umap/js/modules/rendering/layers/heat.js +5 -0
  29. umap/static/umap/js/modules/rendering/map.js +21 -10
  30. umap/static/umap/js/modules/rendering/ui.js +0 -1
  31. umap/static/umap/js/modules/rules.js +56 -46
  32. umap/static/umap/js/modules/schema.js +5 -1
  33. umap/static/umap/js/modules/share.js +2 -2
  34. umap/static/umap/js/modules/slideshow.js +1 -1
  35. umap/static/umap/js/modules/sync/engine.js +23 -9
  36. umap/static/umap/js/modules/ui/bar.js +2 -2
  37. umap/static/umap/js/modules/ui/base.js +13 -0
  38. umap/static/umap/js/modules/umap.js +69 -111
  39. umap/static/umap/js/umap.controls.js +0 -310
  40. umap/static/umap/js/umap.core.js +0 -40
  41. umap/static/umap/locale/am_ET.js +8 -3
  42. umap/static/umap/locale/am_ET.json +8 -3
  43. umap/static/umap/locale/ar.js +8 -3
  44. umap/static/umap/locale/ar.json +8 -3
  45. umap/static/umap/locale/ast.js +8 -3
  46. umap/static/umap/locale/ast.json +8 -3
  47. umap/static/umap/locale/bg.js +8 -3
  48. umap/static/umap/locale/bg.json +8 -3
  49. umap/static/umap/locale/br.js +8 -3
  50. umap/static/umap/locale/br.json +8 -3
  51. umap/static/umap/locale/ca.js +20 -17
  52. umap/static/umap/locale/ca.json +20 -17
  53. umap/static/umap/locale/cs_CZ.js +5 -2
  54. umap/static/umap/locale/cs_CZ.json +5 -2
  55. umap/static/umap/locale/da.js +8 -3
  56. umap/static/umap/locale/da.json +8 -3
  57. umap/static/umap/locale/de.js +5 -2
  58. umap/static/umap/locale/de.json +5 -2
  59. umap/static/umap/locale/el.js +92 -87
  60. umap/static/umap/locale/el.json +92 -87
  61. umap/static/umap/locale/en.js +6 -2
  62. umap/static/umap/locale/en.json +6 -2
  63. umap/static/umap/locale/en_US.json +8 -3
  64. umap/static/umap/locale/es.js +18 -15
  65. umap/static/umap/locale/es.json +18 -15
  66. umap/static/umap/locale/et.js +8 -3
  67. umap/static/umap/locale/et.json +8 -3
  68. umap/static/umap/locale/eu.js +5 -2
  69. umap/static/umap/locale/eu.json +5 -2
  70. umap/static/umap/locale/fa_IR.js +5 -2
  71. umap/static/umap/locale/fa_IR.json +5 -2
  72. umap/static/umap/locale/fi.js +8 -3
  73. umap/static/umap/locale/fi.json +8 -3
  74. umap/static/umap/locale/fr.js +5 -2
  75. umap/static/umap/locale/fr.json +5 -2
  76. umap/static/umap/locale/gl.js +5 -2
  77. umap/static/umap/locale/gl.json +5 -2
  78. umap/static/umap/locale/he.js +8 -3
  79. umap/static/umap/locale/he.json +8 -3
  80. umap/static/umap/locale/hr.js +8 -3
  81. umap/static/umap/locale/hr.json +8 -3
  82. umap/static/umap/locale/hu.js +5 -2
  83. umap/static/umap/locale/hu.json +5 -2
  84. umap/static/umap/locale/id.js +8 -3
  85. umap/static/umap/locale/id.json +8 -3
  86. umap/static/umap/locale/is.js +8 -3
  87. umap/static/umap/locale/is.json +8 -3
  88. umap/static/umap/locale/it.js +5 -2
  89. umap/static/umap/locale/it.json +5 -2
  90. umap/static/umap/locale/ja.js +8 -3
  91. umap/static/umap/locale/ja.json +8 -3
  92. umap/static/umap/locale/ko.js +8 -3
  93. umap/static/umap/locale/ko.json +8 -3
  94. umap/static/umap/locale/lt.js +8 -3
  95. umap/static/umap/locale/lt.json +8 -3
  96. umap/static/umap/locale/ms.js +8 -3
  97. umap/static/umap/locale/ms.json +8 -3
  98. umap/static/umap/locale/nl.js +7 -4
  99. umap/static/umap/locale/nl.json +7 -4
  100. umap/static/umap/locale/no.js +8 -3
  101. umap/static/umap/locale/no.json +8 -3
  102. umap/static/umap/locale/pl.js +8 -3
  103. umap/static/umap/locale/pl.json +8 -3
  104. umap/static/umap/locale/pl_PL.json +8 -3
  105. umap/static/umap/locale/pt.js +5 -2
  106. umap/static/umap/locale/pt.json +5 -2
  107. umap/static/umap/locale/pt_BR.js +8 -3
  108. umap/static/umap/locale/pt_BR.json +8 -3
  109. umap/static/umap/locale/pt_PT.js +5 -2
  110. umap/static/umap/locale/pt_PT.json +5 -2
  111. umap/static/umap/locale/ro.js +8 -3
  112. umap/static/umap/locale/ro.json +8 -3
  113. umap/static/umap/locale/ru.js +8 -3
  114. umap/static/umap/locale/ru.json +8 -3
  115. umap/static/umap/locale/sk_SK.js +8 -3
  116. umap/static/umap/locale/sk_SK.json +8 -3
  117. umap/static/umap/locale/sl.js +8 -3
  118. umap/static/umap/locale/sl.json +8 -3
  119. umap/static/umap/locale/sr.js +8 -3
  120. umap/static/umap/locale/sr.json +8 -3
  121. umap/static/umap/locale/sv.js +8 -3
  122. umap/static/umap/locale/sv.json +8 -3
  123. umap/static/umap/locale/th_TH.js +8 -3
  124. umap/static/umap/locale/th_TH.json +8 -3
  125. umap/static/umap/locale/tr.js +8 -3
  126. umap/static/umap/locale/tr.json +8 -3
  127. umap/static/umap/locale/uk_UA.js +8 -3
  128. umap/static/umap/locale/uk_UA.json +8 -3
  129. umap/static/umap/locale/vi.js +8 -3
  130. umap/static/umap/locale/vi.json +8 -3
  131. umap/static/umap/locale/vi_VN.json +8 -3
  132. umap/static/umap/locale/zh.js +8 -3
  133. umap/static/umap/locale/zh.json +8 -3
  134. umap/static/umap/locale/zh_CN.json +8 -3
  135. umap/static/umap/locale/zh_TW.Big5.json +8 -3
  136. umap/static/umap/locale/zh_TW.js +5 -2
  137. umap/static/umap/locale/zh_TW.json +5 -2
  138. umap/static/umap/map.css +9 -31
  139. umap/static/umap/vendors/togeojson/togeojson.es.js +350 -177
  140. umap/static/umap/vendors/togeojson/togeojson.es.mjs.map +1 -1
  141. umap/templates/umap/design_system.html +355 -0
  142. umap/templates/umap/map_init.html +3 -1
  143. umap/tests/base.py +2 -2
  144. umap/tests/fixtures/heatmap_data.json +1044 -0
  145. umap/tests/integration/test_basics.py +9 -1
  146. umap/tests/integration/test_browser.py +3 -3
  147. umap/tests/integration/test_conditional_rules.py +2 -2
  148. umap/tests/integration/test_datalayer.py +0 -1
  149. umap/tests/integration/test_edit_map.py +7 -7
  150. umap/tests/integration/test_facets_browser.py +2 -2
  151. umap/tests/integration/test_heatmap.py +41 -0
  152. umap/tests/integration/test_import.py +58 -1
  153. umap/tests/integration/test_map.py +7 -8
  154. umap/tests/integration/test_optimistic_merge.py +12 -4
  155. umap/tests/integration/test_querystring.py +1 -1
  156. umap/tests/integration/test_remote_data.py +79 -0
  157. umap/tests/integration/test_websocket_sync.py +2 -2
  158. umap/urls.py +1 -0
  159. umap/views.py +8 -1
  160. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/METADATA +11 -11
  161. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/RECORD +164 -158
  162. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/WHEEL +0 -0
  163. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/entry_points.txt +0 -0
  164. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/licenses/LICENSE +0 -0
@@ -61,13 +61,21 @@ def test_cannot_put_script_tag_in_datalayer_name_or_description(
61
61
  page.locator(".umap-field-description textarea").fill(
62
62
  '<p>before <script>alert("attack")</script> after</p>'
63
63
  )
64
- page.get_by_role("button", name="Save").click()
64
+ page.wait_for_timeout(300) # Wait for debounce
65
+ with page.expect_response(re.compile(".*/datalayer/create/.*")):
66
+ page.get_by_role("button", name="Save").click()
65
67
  page.get_by_role("button", name="About").click()
66
68
  # Title should contain raw HTML (we are using textContent)
67
69
  expect(page.get_by_text('<script>alert("attack")</script>')).to_be_visible()
68
70
  # Description should contain escaped HTML
69
71
  expect(page.get_by_text("before after")).to_be_visible()
70
72
 
73
+ # Reload the map
74
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}")
75
+ page.get_by_role("button", name="About").click()
76
+ expect(page.get_by_text('<script>alert("attack")</script>')).to_be_visible()
77
+ expect(page.get_by_text("before after")).to_be_visible()
78
+
71
79
 
72
80
  def test_login_from_map_page(live_server, page, tilelayer, settings, user, context):
73
81
  settings.ENABLE_ACCOUNT_LOGIN = True
@@ -465,19 +465,19 @@ def test_main_toolbox_toggle_all_layers(live_server, map, page):
465
465
  expect(page.locator(".datalayer.off")).to_have_count(1)
466
466
 
467
467
  # Click on button
468
- page.locator(".umap-browser [data-ref=toggle]").click()
468
+ page.locator(".umap-browser").get_by_title("Show/hide all layers").click()
469
469
  # Should have hidden the two other layers
470
470
  expect(page.locator(".datalayer.off")).to_have_count(3)
471
471
  expect(markers).to_have_count(0)
472
472
 
473
473
  # Click again
474
- page.locator(".umap-browser [data-ref=toggle]").click()
474
+ page.locator(".umap-browser").get_by_title("Show/hide all layers").click()
475
475
  # Should shown all layers
476
476
  expect(page.locator(".datalayer.off")).to_have_count(0)
477
477
  expect(markers).to_have_count(3)
478
478
 
479
479
  # Click again
480
- page.locator(".umap-browser [data-ref=toggle]").click()
480
+ page.locator(".umap-browser").get_by_title("Show/hide all layers").click()
481
481
  # Should hidden again all layers
482
482
  expect(page.locator(".datalayer.off")).to_have_count(3)
483
483
  expect(markers).to_have_count(0)
@@ -281,10 +281,10 @@ def test_can_deactive_rule_from_list(live_server, page, openmap):
281
281
  page.get_by_role("button", name="Edit").click()
282
282
  page.get_by_role("button", name="Map advanced properties").click()
283
283
  page.get_by_text("Conditional style rules").click()
284
- page.get_by_role("button", name="Show/hide layer").click()
284
+ page.get_by_role("button", name="Toggle rule").click()
285
285
  colors = getColors(markers)
286
286
  assert colors.count("rgb(240, 248, 255)") == 0
287
- page.get_by_role("button", name="Show/hide layer").click()
287
+ page.get_by_role("button", name="Toggle rule").click()
288
288
  colors = getColors(markers)
289
289
  assert colors.count("rgb(240, 248, 255)") == 3
290
290
 
@@ -1,5 +1,4 @@
1
1
  import json
2
- import os
3
2
  import re
4
3
 
5
4
  import pytest
@@ -175,14 +175,14 @@ def test_sortkey_impacts_datalayerindex(map, live_server, page):
175
175
 
176
176
  # By default, features are sorted by name (Third, Second, First)
177
177
  page.get_by_role("button", name="Open browser").click()
178
- page.get_by_role("heading", name="Show/hide layer").locator("i").click()
178
+ page.locator(".umap-browser .datalayer-name").click()
179
179
 
180
180
  first_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(0)
181
181
  second_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(1)
182
182
  third_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(2)
183
- assert "X Third" == first_listed_feature.text_content()
184
- assert "Y Second" == second_listed_feature.text_content()
185
- assert "Z First" == third_listed_feature.text_content()
183
+ assert "X Third" == first_listed_feature.text_content().strip()
184
+ assert "Y Second" == second_listed_feature.text_content().strip()
185
+ assert "Z First" == third_listed_feature.text_content().strip()
186
186
 
187
187
  # Change the default sortkey to be "key"
188
188
  page.get_by_role("button", name="Edit").click()
@@ -201,9 +201,9 @@ def test_sortkey_impacts_datalayerindex(map, live_server, page):
201
201
  first_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(0)
202
202
  second_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(1)
203
203
  third_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(2)
204
- assert "Z First" == first_listed_feature.text_content()
205
- assert "Y Second" == second_listed_feature.text_content()
206
- assert "X Third" == third_listed_feature.text_content()
204
+ assert "Z First" == first_listed_feature.text_content().strip()
205
+ assert "Y Second" == second_listed_feature.text_content().strip()
206
+ assert "X Third" == third_listed_feature.text_content().strip()
207
207
 
208
208
 
209
209
  def test_hover_tooltip_setting_should_be_persistent(live_server, map, page):
@@ -243,7 +243,7 @@ def test_facets_search_are_persistent_when_closing_panel(live_server, page, map)
243
243
 
244
244
  # Now let's filter
245
245
  odd.click()
246
- expect(page.locator("summary")).to_have_attribute("data-badge", " ")
246
+ expect(page.locator(".filters summary")).to_have_attribute("data-badge", " ")
247
247
  expect(page.locator(".umap-control-browse")).to_have_attribute("data-badge", " ")
248
248
  expect(markers).to_have_count(2)
249
249
  expect(panel.get_by_text("Point 2")).to_be_hidden()
@@ -264,7 +264,7 @@ def test_facets_search_are_persistent_when_closing_panel(live_server, page, map)
264
264
  expect(panel.get_by_text("Point 3")).to_be_visible()
265
265
 
266
266
  # Close panel
267
- expect(panel.locator("summary")).to_have_attribute("data-badge", " ")
267
+ expect(panel.locator(".filters summary")).to_have_attribute("data-badge", " ")
268
268
  expect(page.locator(".umap-control-browse")).to_have_attribute("data-badge", " ")
269
269
  panel.get_by_role("button", name="Close").click()
270
270
  page.get_by_role("button", name="Open browser").click()
@@ -0,0 +1,41 @@
1
+ import json
2
+ from pathlib import Path
3
+
4
+ import pytest
5
+ from playwright.sync_api import expect
6
+
7
+ from ..base import DataLayerFactory
8
+
9
+ pytestmark = pytest.mark.django_db
10
+
11
+
12
+ def test_heatmap(map, live_server, page):
13
+ path = Path(__file__).parent.parent / "fixtures/heatmap_data.json"
14
+ data = json.loads(path.read_text())
15
+ DataLayerFactory(data=data, map=map)
16
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
17
+ expect(page.locator("canvas.leaflet-heatmap-layer")).to_be_visible()
18
+
19
+
20
+ def test_can_create_heatmap_after_import(live_server, page, tilelayer, settings):
21
+ settings.UMAP_ALLOW_ANONYMOUS = True
22
+ page.goto(f"{live_server.url}/en/map/new")
23
+ page.get_by_title("Import data").click()
24
+ file_input = page.locator("input[type='file']")
25
+ with page.expect_file_chooser() as fc_info:
26
+ file_input.click()
27
+ file_chooser = fc_info.value
28
+ path = Path(__file__).parent.parent / "fixtures/heatmap_data.json"
29
+ file_chooser.set_files(path)
30
+ page.get_by_role("button", name="Import data", exact=True).click()
31
+ page.get_by_role("button", name="Manage layers").click()
32
+ page.get_by_role("button", name="Edit", exact=True).click()
33
+ page.get_by_role("combobox").select_option("Heat")
34
+ page.get_by_role("button", name="Save draft").click()
35
+ expect(page.locator("canvas.leaflet-heatmap-layer")).to_be_visible()
36
+
37
+ # Test we can delete it and save
38
+ page.get_by_text("Advanced actions").click()
39
+ page.get_by_role("button", name="Delete").click()
40
+ page.get_by_role("button", name="Save draft").click()
41
+ expect(page.locator("canvas.leaflet-heatmap-layer")).to_be_hidden()
@@ -435,6 +435,63 @@ def test_import_geometry_collection(live_server, page, tilelayer):
435
435
  expect(paths).to_have_count(2)
436
436
 
437
437
 
438
+ def test_import_geometry_collection_in_feature(live_server, page, tilelayer):
439
+ data = {
440
+ "type": "Feature",
441
+ "properties": {"name": "foobar"},
442
+ "geometry": {
443
+ "type": "GeometryCollection",
444
+ "geometries": [
445
+ {"type": "Point", "coordinates": [-80.6608, 35.0493]},
446
+ {
447
+ "type": "Polygon",
448
+ "coordinates": [
449
+ [
450
+ [-80.6645, 35.0449],
451
+ [-80.6634, 35.0460],
452
+ [-80.6625, 35.0455],
453
+ [-80.6638, 35.0442],
454
+ [-80.6645, 35.0449],
455
+ ]
456
+ ],
457
+ },
458
+ {
459
+ "type": "LineString",
460
+ "coordinates": [
461
+ [-80.66237, 35.05950],
462
+ [-80.66269, 35.05926],
463
+ [-80.66284, 35.05893],
464
+ [-80.66308, 35.05833],
465
+ [-80.66385, 35.04387],
466
+ [-80.66303, 35.04371],
467
+ ],
468
+ },
469
+ ],
470
+ },
471
+ }
472
+ page.goto(f"{live_server.url}/map/new/")
473
+ page.get_by_title("Open browser").click()
474
+ layers = page.locator(".umap-browser .datalayer")
475
+ markers = page.locator(".leaflet-marker-icon")
476
+ paths = page.locator("path")
477
+ expect(markers).to_have_count(0)
478
+ expect(paths).to_have_count(0)
479
+ expect(layers).to_have_count(0)
480
+ button = page.get_by_title("Import data")
481
+ expect(button).to_be_visible()
482
+ button.click()
483
+ textarea = page.locator(".umap-import textarea")
484
+ textarea.fill(json.dumps(data))
485
+ page.locator('select[name="format"]').select_option("geojson")
486
+ page.get_by_role("button", name="Import data", exact=True).click()
487
+ # A layer has been created
488
+ expect(layers).to_have_count(1)
489
+ expect(markers).to_have_count(1)
490
+ expect(paths).to_have_count(2)
491
+ # Geometries are treated as separate features.
492
+ expect(page.get_by_text("foobar")).to_have_count(3)
493
+
494
+
438
495
  def test_import_multipolygon(live_server, page, tilelayer):
439
496
  data = {
440
497
  "type": "Feature",
@@ -847,7 +904,7 @@ def test_import_from_datasets(page, live_server, tilelayer, settings):
847
904
  page.get_by_role("button", name="Import data", exact=True).click()
848
905
  expect(page.locator(".leaflet-marker-icon")).to_be_visible()
849
906
  page.get_by_role("button", name="Open browser").click()
850
- expect(page.locator("h5").get_by_text("Good data")).to_be_visible()
907
+ expect(page.locator("summary").get_by_text("Good data")).to_be_visible()
851
908
 
852
909
 
853
910
  def test_import_osm_relation(tilelayer, live_server, page):
@@ -1,4 +1,3 @@
1
- import os
2
1
  import re
3
2
 
4
3
  import pytest
@@ -46,7 +45,7 @@ def test_default_view_without_datalayer_should_use_default_center(
46
45
  page.goto(f"{live_server.url}{map.get_absolute_url()}?onLoadPanel=datalayers")
47
46
  # Hash is defined, so map is initialized
48
47
  expect(page).to_have_url(re.compile(r".*#7/48\..+/13\..+"))
49
- layers = page.locator(".umap-browser .datalayer h5")
48
+ layers = page.locator(".umap-browser .datalayer summary")
50
49
  expect(layers).to_have_count(1)
51
50
 
52
51
 
@@ -60,7 +59,7 @@ def test_default_view_latest_without_datalayer_should_use_default_center(
60
59
  page.goto(f"{live_server.url}{map.get_absolute_url()}?onLoadPanel=datalayers")
61
60
  # Hash is defined, so map is initialized
62
61
  expect(page).to_have_url(re.compile(r".*#7/48\..+/13\..+"))
63
- layers = page.locator(".umap-browser .datalayer h5")
62
+ layers = page.locator(".umap-browser .datalayer summary")
64
63
  expect(layers).to_have_count(1)
65
64
 
66
65
 
@@ -74,7 +73,7 @@ def test_default_view_data_without_datalayer_should_use_default_center(
74
73
  page.goto(f"{live_server.url}{map.get_absolute_url()}?onLoadPanel=datalayers")
75
74
  # Hash is defined, so map is initialized
76
75
  expect(page).to_have_url(re.compile(r".*#7/48\..+/13\..+"))
77
- layers = page.locator(".umap-browser .datalayer h5")
76
+ layers = page.locator(".umap-browser .datalayer summary")
78
77
  expect(layers).to_have_count(1)
79
78
 
80
79
 
@@ -84,7 +83,7 @@ def test_default_view_latest_with_marker(map, live_server, datalayer, page):
84
83
  page.goto(f"{live_server.url}{map.get_absolute_url()}?onLoadPanel=datalayers")
85
84
  # Hash is defined, so map is initialized
86
85
  expect(page).to_have_url(re.compile(r".*#7/48\..+/14\..+"))
87
- layers = page.locator(".umap-browser .datalayer h5")
86
+ layers = page.locator(".umap-browser .datalayer summary")
88
87
  expect(layers).to_have_count(1)
89
88
  expect(page.locator(".leaflet-popup")).to_be_visible()
90
89
 
@@ -113,7 +112,7 @@ def test_default_view_latest_with_line(map, live_server, page):
113
112
  map.save()
114
113
  page.goto(f"{live_server.url}{map.get_absolute_url()}?onLoadPanel=datalayers")
115
114
  expect(page).to_have_url(re.compile(r".*#8/48\..+/2\..+"))
116
- layers = page.locator(".umap-browser .datalayer h5")
115
+ layers = page.locator(".umap-browser .datalayer summary")
117
116
  expect(layers).to_have_count(1)
118
117
 
119
118
 
@@ -144,7 +143,7 @@ def test_default_view_latest_with_polygon(map, live_server, page):
144
143
  map.save()
145
144
  page.goto(f"{live_server.url}{map.get_absolute_url()}?onLoadPanel=datalayers")
146
145
  expect(page).to_have_url(re.compile(r".*#8/48\..+/2\..+"))
147
- layers = page.locator(".umap-browser .datalayer h5")
146
+ layers = page.locator(".umap-browser .datalayer summary")
148
147
  expect(layers).to_have_count(1)
149
148
 
150
149
 
@@ -173,7 +172,7 @@ def test_remote_layer_should_not_be_used_as_datalayer_for_created_features(
173
172
  toggle = page.get_by_role("button", name="Open browser")
174
173
  expect(toggle).to_be_visible()
175
174
  toggle.click()
176
- layers = page.locator(".umap-browser .datalayer h5")
175
+ layers = page.locator(".umap-browser .datalayer summary")
177
176
  expect(layers).to_have_count(1)
178
177
  map_el = page.locator("#map")
179
178
  add_marker = page.get_by_title("Draw a marker")
@@ -50,6 +50,8 @@ def test_created_markers_are_merged(context, live_server, tilelayer):
50
50
  "editMode": "advanced",
51
51
  "inCaption": True,
52
52
  "id": str(datalayer.pk),
53
+ "rank": 0,
54
+ "remoteData": {},
53
55
  }
54
56
 
55
57
  # Now navigate to this map from another tab
@@ -87,6 +89,8 @@ def test_created_markers_are_merged(context, live_server, tilelayer):
87
89
  "inCaption": True,
88
90
  "editMode": "advanced",
89
91
  "id": str(datalayer.pk),
92
+ "rank": 0,
93
+ "remoteData": {},
90
94
  }
91
95
 
92
96
  # Now create another marker in the first tab
@@ -105,7 +109,8 @@ def test_created_markers_are_merged(context, live_server, tilelayer):
105
109
  "inCaption": True,
106
110
  "editMode": "advanced",
107
111
  "id": str(datalayer.pk),
108
- "permissions": {"edit_status": 1},
112
+ "rank": 0,
113
+ "remoteData": {},
109
114
  }
110
115
 
111
116
  # And again
@@ -124,7 +129,8 @@ def test_created_markers_are_merged(context, live_server, tilelayer):
124
129
  "inCaption": True,
125
130
  "editMode": "advanced",
126
131
  "id": str(datalayer.pk),
127
- "permissions": {"edit_status": 1},
132
+ "rank": 0,
133
+ "remoteData": {},
128
134
  }
129
135
  expect(marker_pane_p1).to_have_count(4)
130
136
 
@@ -145,7 +151,8 @@ def test_created_markers_are_merged(context, live_server, tilelayer):
145
151
  "inCaption": True,
146
152
  "editMode": "advanced",
147
153
  "id": str(datalayer.pk),
148
- "permissions": {"edit_status": 1},
154
+ "rank": 0,
155
+ "remoteData": {},
149
156
  }
150
157
  expect(marker_pane_p2).to_have_count(5)
151
158
 
@@ -271,7 +278,8 @@ def test_same_second_edit_doesnt_conflict(context, live_server, tilelayer):
271
278
  "inCaption": True,
272
279
  "editMode": "advanced",
273
280
  "id": str(datalayer.pk),
274
- "permissions": {"edit_status": 1},
281
+ "rank": 0,
282
+ "remoteData": {},
275
283
  }
276
284
 
277
285
 
@@ -63,5 +63,5 @@ def test_zoom_control(map, live_server, datalayer, page):
63
63
  expect(control).to_be_visible()
64
64
  page.goto(f"{live_server.url}{map.get_absolute_url()}?zoomControl=null")
65
65
  expect(control).to_be_hidden()
66
- page.get_by_title("More controls").click()
66
+ page.locator(".umap-control-more").click()
67
67
  expect(control).to_be_visible()
@@ -0,0 +1,79 @@
1
+ from playwright.sync_api import expect
2
+
3
+ from umap.models import Map
4
+
5
+ from ..base import DataLayerFactory
6
+
7
+
8
+ def test_dynamic_remote_data(page, live_server, tilelayer, map):
9
+ data = [
10
+ {
11
+ "type": "FeatureCollection",
12
+ "features": [
13
+ {
14
+ "type": "Feature",
15
+ "properties": {"name": "Point 2"},
16
+ "geometry": {
17
+ "type": "Point",
18
+ "coordinates": [4.3375, 11.2707],
19
+ },
20
+ }
21
+ ],
22
+ },
23
+ {
24
+ "type": "FeatureCollection",
25
+ "features": [
26
+ {
27
+ "type": "Feature",
28
+ "properties": {"name": "Point 1"},
29
+ "geometry": {
30
+ "type": "Point",
31
+ "coordinates": [4.3375, 12.2707],
32
+ },
33
+ }
34
+ ],
35
+ },
36
+ ]
37
+
38
+ def handle(route):
39
+ route.fulfill(json=data.pop())
40
+
41
+ settings = {
42
+ "remoteData": {
43
+ "url": "https://remote.org/data.json",
44
+ "format": "geojson",
45
+ "dynamic": True,
46
+ },
47
+ "showLabel": True,
48
+ }
49
+ DataLayerFactory(map=map, settings=settings)
50
+ map.edit_status = Map.ANONYMOUS
51
+ map.settings["properties"]["zoom"] = 6
52
+ map.settings["geometry"] = {
53
+ "type": "Point",
54
+ "coordinates": [5, 12],
55
+ }
56
+ map.save()
57
+
58
+ # Intercept the route to the proxy
59
+ page.route("https://remote.org/data.json", handle)
60
+ page.goto(f"{live_server.url}{map.get_absolute_url()}")
61
+
62
+ expect(page.get_by_role("tooltip", name="Point 1")).to_be_visible()
63
+
64
+ # Now drag the map
65
+ map_el = page.locator("#map")
66
+ map_el.drag_to(
67
+ map_el,
68
+ source_position={"x": 100, "y": 100},
69
+ target_position={"x": 110, "y": 110},
70
+ )
71
+
72
+ expect(page.get_by_role("tooltip", name="Point 2")).to_be_visible()
73
+ # Needed otherwise it found two (!) tooltip with name "Point 1"…
74
+ page.wait_for_timeout(300)
75
+ expect(page.get_by_role("tooltip", name="Point 1")).to_be_hidden()
76
+
77
+ # Map must not be dirty
78
+ page.get_by_role("button", name="Edit").click()
79
+ expect(page.locator(".edit-undo")).to_be_disabled()
@@ -397,7 +397,7 @@ def test_should_sync_datalayers(new_page, asgi_live_server, tilelayer):
397
397
 
398
398
  # Make sure this new marker is in Layer 2 for peerB
399
399
  # Show features for this layer in the brower.
400
- peerB.get_by_role("heading", name="Layer 2").locator(".datalayer-name").click()
400
+ peerB.locator("summary").filter(has_text="Layer 2").click()
401
401
  expect(peerB.locator("li").filter(has_text="Layer 2")).to_be_visible()
402
402
  peerB.locator(".panel.left").get_by_role("button", name="Show/hide layer").nth(
403
403
  1
@@ -534,7 +534,7 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
534
534
 
535
535
  # Make sure only one layer has been created on peer B
536
536
  peerB.get_by_role("button", name="Open browser").click()
537
- expect(peerB.locator("h5").get_by_text("Layer 1")).to_be_visible()
537
+ expect(peerB.locator("summary").get_by_text("Layer 1")).to_be_visible()
538
538
  peerB.get_by_role("button", name="Close").click()
539
539
 
540
540
  # Save and quit edit mode again
umap/urls.py CHANGED
@@ -205,6 +205,7 @@ urlpatterns += i18n_patterns(
205
205
  )
206
206
  urlpatterns += (
207
207
  path("stats/", cache_page(60 * 60)(views.stats), name="stats"),
208
+ path("design_system/", views.design_system, name="design_system"),
208
209
  path(
209
210
  "favicon.ico",
210
211
  cache_control(max_age=60 * 60 * 24, immutable=True, public=True)(
umap/views.py CHANGED
@@ -668,7 +668,7 @@ class MapDetailMixin(SessionMixin):
668
668
  geojson["properties"] = {}
669
669
  geojson["properties"].update(properties)
670
670
  geojson["properties"]["datalayers"] = self.get_datalayers()
671
- context["map_settings"] = json_dumps(geojson, indent=settings.DEBUG)
671
+ context["map_settings"] = json_dumps(geojson, indent=settings.DEBUG or None)
672
672
  self.set_preconnect(geojson["properties"], context)
673
673
  return context
674
674
 
@@ -1410,6 +1410,13 @@ def stats(request):
1410
1410
  )
1411
1411
 
1412
1412
 
1413
+ class DesignSystem(TemplateView):
1414
+ template_name = "umap/design_system.html"
1415
+
1416
+
1417
+ design_system = DesignSystem.as_view()
1418
+
1419
+
1413
1420
  @require_GET
1414
1421
  @cache_control(max_age=60 * 60 * 24, immutable=True, public=True) # One day.
1415
1422
  def webmanifest(request):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: umap-project
3
- Version: 3.0.4
3
+ Version: 3.0.6
4
4
  Summary: Create maps with OpenStreetMap layers in a minute and embed them in your site.
5
5
  Author-email: Yohan Boniface <yb@enix.org>
6
6
  Maintainer-email: David Larlet <david@larlet.fr>
@@ -19,9 +19,9 @@ Requires-Python: >=3.10
19
19
  Requires-Dist: django-agnocomplete==2.2.0
20
20
  Requires-Dist: django-environ==0.12.0
21
21
  Requires-Dist: django-probes==1.7.0
22
- Requires-Dist: django==5.1.8
23
- Requires-Dist: pillow==11.1.0
24
- Requires-Dist: psycopg==3.2.6
22
+ Requires-Dist: django==5.2.1
23
+ Requires-Dist: pillow==11.2.1
24
+ Requires-Dist: psycopg==3.2.8
25
25
  Requires-Dist: rcssmin==1.2.1
26
26
  Requires-Dist: requests==2.32.3
27
27
  Requires-Dist: rjsmin==1.2.4
@@ -31,26 +31,26 @@ Provides-Extra: dev
31
31
  Requires-Dist: djlint==1.36.4; extra == 'dev'
32
32
  Requires-Dist: hatch==1.14.1; extra == 'dev'
33
33
  Requires-Dist: isort==6.0.1; extra == 'dev'
34
- Requires-Dist: mkdocs-material==9.6.10; extra == 'dev'
34
+ Requires-Dist: mkdocs-material==9.6.12; extra == 'dev'
35
35
  Requires-Dist: mkdocs-static-i18n==1.3.0; extra == 'dev'
36
36
  Requires-Dist: mkdocs==1.6.1; extra == 'dev'
37
- Requires-Dist: pymdown-extensions==10.14.3; extra == 'dev'
38
- Requires-Dist: ruff==0.11.4; extra == 'dev'
37
+ Requires-Dist: pymdown-extensions==10.15; extra == 'dev'
38
+ Requires-Dist: ruff==0.11.9; extra == 'dev'
39
39
  Requires-Dist: vermin==1.6.0; extra == 'dev'
40
40
  Provides-Extra: docker
41
- Requires-Dist: uvicorn==0.34.0; extra == 'docker'
41
+ Requires-Dist: uvicorn==0.34.2; extra == 'docker'
42
42
  Provides-Extra: s3
43
43
  Requires-Dist: django-storages[s3]==1.14.6; extra == 's3'
44
44
  Provides-Extra: sync
45
- Requires-Dist: pydantic==2.11.2; extra == 'sync'
45
+ Requires-Dist: pydantic==2.11.4; extra == 'sync'
46
46
  Requires-Dist: redis==5.2.1; extra == 'sync'
47
47
  Requires-Dist: websockets==15.0.1; extra == 'sync'
48
48
  Provides-Extra: test
49
49
  Requires-Dist: daphne==4.1.2; extra == 'test'
50
50
  Requires-Dist: factory-boy==3.3.3; extra == 'test'
51
- Requires-Dist: moto[s3]==5.1.3; extra == 'test'
51
+ Requires-Dist: moto[s3]==5.1.4; extra == 'test'
52
52
  Requires-Dist: playwright>=1.39; extra == 'test'
53
- Requires-Dist: pytest-django==4.10.0; extra == 'test'
53
+ Requires-Dist: pytest-django==4.11.1; extra == 'test'
54
54
  Requires-Dist: pytest-playwright==0.7.0; extra == 'test'
55
55
  Requires-Dist: pytest-rerunfailures==15.0; extra == 'test'
56
56
  Requires-Dist: pytest-xdist<4,>=3.5.0; extra == 'test'