umap-project 3.3.6__py3-none-any.whl → 3.4.0__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.
Files changed (239) hide show
  1. umap/__init__.py +1 -1
  2. umap/context_processors.py +4 -1
  3. umap/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  4. umap/locale/cs_CZ/LC_MESSAGES/django.po +43 -33
  5. umap/locale/da/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/da/LC_MESSAGES/django.po +43 -33
  7. umap/locale/de/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/de/LC_MESSAGES/django.po +35 -29
  9. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  10. umap/locale/el/LC_MESSAGES/django.po +35 -29
  11. umap/locale/en/LC_MESSAGES/django.po +47 -41
  12. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  13. umap/locale/es/LC_MESSAGES/django.po +43 -33
  14. umap/locale/et/LC_MESSAGES/django.mo +0 -0
  15. umap/locale/et/LC_MESSAGES/django.po +58 -54
  16. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  17. umap/locale/eu/LC_MESSAGES/django.po +43 -33
  18. umap/locale/fa_IR/LC_MESSAGES/django.mo +0 -0
  19. umap/locale/fa_IR/LC_MESSAGES/django.po +43 -33
  20. umap/locale/fr/LC_MESSAGES/django.mo +0 -0
  21. umap/locale/fr/LC_MESSAGES/django.po +36 -30
  22. umap/locale/gl/LC_MESSAGES/django.mo +0 -0
  23. umap/locale/gl/LC_MESSAGES/django.po +43 -33
  24. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  25. umap/locale/hu/LC_MESSAGES/django.po +35 -29
  26. umap/locale/is/LC_MESSAGES/django.mo +0 -0
  27. umap/locale/is/LC_MESSAGES/django.po +43 -33
  28. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  29. umap/locale/it/LC_MESSAGES/django.po +43 -33
  30. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  31. umap/locale/nl/LC_MESSAGES/django.po +35 -29
  32. umap/locale/pl/LC_MESSAGES/django.mo +0 -0
  33. umap/locale/pl/LC_MESSAGES/django.po +114 -103
  34. umap/locale/pt/LC_MESSAGES/django.mo +0 -0
  35. umap/locale/pt/LC_MESSAGES/django.po +43 -33
  36. umap/locale/th_TH/LC_MESSAGES/django.mo +0 -0
  37. umap/locale/th_TH/LC_MESSAGES/django.po +310 -109
  38. umap/locale/zh_TW/LC_MESSAGES/django.mo +0 -0
  39. umap/locale/zh_TW/LC_MESSAGES/django.po +80 -70
  40. umap/management/commands/switch_user.py +2 -2
  41. umap/migrations/0018_datalayer_uuid.py +1 -1
  42. umap/models.py +7 -3
  43. umap/settings/local.py.sample +1 -1
  44. umap/static/umap/base.css +89 -32
  45. umap/static/umap/content.css +129 -33
  46. umap/static/umap/css/bar.css +82 -20
  47. umap/static/umap/css/browser.css +163 -0
  48. umap/static/umap/css/contextmenu.css +15 -0
  49. umap/static/umap/css/dialog.css +36 -16
  50. umap/static/umap/css/form.css +123 -33
  51. umap/static/umap/css/icon.css +46 -3
  52. umap/static/umap/css/panel.css +7 -3
  53. umap/static/umap/css/popup.css +34 -8
  54. umap/static/umap/css/tooltip.css +8 -4
  55. umap/static/umap/img/16-white.svg +26 -8
  56. umap/static/umap/img/16.svg +1 -1
  57. umap/static/umap/img/source/16-white.svg +36 -18
  58. umap/static/umap/img/source/16.svg +1 -1
  59. umap/static/umap/js/components/alerts/alert.css +69 -31
  60. umap/static/umap/js/components/alerts/alert.js +20 -2
  61. umap/static/umap/js/components/base.js +1 -1
  62. umap/static/umap/js/modules/browser.js +69 -61
  63. umap/static/umap/js/modules/caption.js +10 -7
  64. umap/static/umap/js/modules/data/features.js +85 -60
  65. umap/static/umap/js/modules/data/fields.js +446 -0
  66. umap/static/umap/js/modules/data/layer.js +78 -184
  67. umap/static/umap/js/modules/domutils.js +109 -0
  68. umap/static/umap/js/modules/filters.js +780 -0
  69. umap/static/umap/js/modules/form/builder.js +8 -5
  70. umap/static/umap/js/modules/form/fields.js +111 -221
  71. umap/static/umap/js/modules/formatter.js +24 -1
  72. umap/static/umap/js/modules/help.js +4 -3
  73. umap/static/umap/js/modules/i18n.js +1 -1
  74. umap/static/umap/js/modules/importer.js +1 -1
  75. umap/static/umap/js/modules/importers/opendata.js +15 -0
  76. umap/static/umap/js/modules/importers/openrouteservice.js +6 -1
  77. umap/static/umap/js/modules/managers.js +2 -2
  78. umap/static/umap/js/modules/permissions.js +39 -31
  79. umap/static/umap/js/modules/rendering/controls.js +11 -9
  80. umap/static/umap/js/modules/rendering/icon.js +3 -8
  81. umap/static/umap/js/modules/rendering/layers/base.js +1 -1
  82. umap/static/umap/js/modules/rendering/layers/classified.js +18 -11
  83. umap/static/umap/js/modules/rendering/layers/cluster.js +5 -3
  84. umap/static/umap/js/modules/rendering/layers/heat.js +27 -21
  85. umap/static/umap/js/modules/rendering/template.js +50 -23
  86. umap/static/umap/js/modules/rendering/ui.js +29 -23
  87. umap/static/umap/js/modules/rules.js +38 -44
  88. umap/static/umap/js/modules/schema.js +3 -6
  89. umap/static/umap/js/modules/share.js +5 -4
  90. umap/static/umap/js/modules/tableeditor.js +50 -38
  91. umap/static/umap/js/modules/templates.js +2 -3
  92. umap/static/umap/js/modules/ui/bar.js +55 -23
  93. umap/static/umap/js/modules/ui/dialog.js +38 -27
  94. umap/static/umap/js/modules/ui/panel.js +23 -8
  95. umap/static/umap/js/modules/ui/tooltip.js +6 -5
  96. umap/static/umap/js/modules/umap.js +151 -56
  97. umap/static/umap/js/modules/utils.js +24 -2
  98. umap/static/umap/js/umap.core.js +1 -110
  99. umap/static/umap/locale/am_ET.js +52 -17
  100. umap/static/umap/locale/am_ET.json +52 -17
  101. umap/static/umap/locale/ar.js +52 -17
  102. umap/static/umap/locale/ar.json +52 -17
  103. umap/static/umap/locale/ast.js +52 -17
  104. umap/static/umap/locale/ast.json +52 -17
  105. umap/static/umap/locale/bg.js +52 -17
  106. umap/static/umap/locale/bg.json +52 -17
  107. umap/static/umap/locale/br.js +48 -22
  108. umap/static/umap/locale/br.json +48 -22
  109. umap/static/umap/locale/ca.js +52 -17
  110. umap/static/umap/locale/ca.json +52 -17
  111. umap/static/umap/locale/cs_CZ.js +52 -17
  112. umap/static/umap/locale/cs_CZ.json +52 -17
  113. umap/static/umap/locale/da.js +54 -17
  114. umap/static/umap/locale/da.json +54 -17
  115. umap/static/umap/locale/de.js +51 -16
  116. umap/static/umap/locale/de.json +51 -16
  117. umap/static/umap/locale/el.js +52 -17
  118. umap/static/umap/locale/el.json +52 -17
  119. umap/static/umap/locale/en.js +53 -16
  120. umap/static/umap/locale/en.json +53 -16
  121. umap/static/umap/locale/en_US.json +52 -17
  122. umap/static/umap/locale/es.js +54 -17
  123. umap/static/umap/locale/es.json +54 -17
  124. umap/static/umap/locale/et.js +91 -56
  125. umap/static/umap/locale/et.json +91 -56
  126. umap/static/umap/locale/eu.js +84 -49
  127. umap/static/umap/locale/eu.json +84 -49
  128. umap/static/umap/locale/fa_IR.js +52 -17
  129. umap/static/umap/locale/fa_IR.json +52 -17
  130. umap/static/umap/locale/fi.js +52 -17
  131. umap/static/umap/locale/fi.json +52 -17
  132. umap/static/umap/locale/fr.js +53 -16
  133. umap/static/umap/locale/fr.json +53 -16
  134. umap/static/umap/locale/gl.js +52 -17
  135. umap/static/umap/locale/gl.json +52 -17
  136. umap/static/umap/locale/he.js +52 -17
  137. umap/static/umap/locale/he.json +52 -17
  138. umap/static/umap/locale/hr.js +52 -17
  139. umap/static/umap/locale/hr.json +52 -17
  140. umap/static/umap/locale/hu.js +59 -24
  141. umap/static/umap/locale/hu.json +59 -24
  142. umap/static/umap/locale/id.js +52 -17
  143. umap/static/umap/locale/id.json +52 -17
  144. umap/static/umap/locale/is.js +52 -17
  145. umap/static/umap/locale/is.json +52 -17
  146. umap/static/umap/locale/it.js +52 -17
  147. umap/static/umap/locale/it.json +52 -17
  148. umap/static/umap/locale/ja.js +52 -17
  149. umap/static/umap/locale/ja.json +52 -17
  150. umap/static/umap/locale/ko.js +52 -17
  151. umap/static/umap/locale/ko.json +52 -17
  152. umap/static/umap/locale/lt.js +52 -17
  153. umap/static/umap/locale/lt.json +52 -17
  154. umap/static/umap/locale/ms.js +52 -17
  155. umap/static/umap/locale/ms.json +52 -17
  156. umap/static/umap/locale/nl.js +52 -17
  157. umap/static/umap/locale/nl.json +52 -17
  158. umap/static/umap/locale/no.js +52 -17
  159. umap/static/umap/locale/no.json +52 -17
  160. umap/static/umap/locale/pl.js +53 -17
  161. umap/static/umap/locale/pl.json +53 -17
  162. umap/static/umap/locale/pl_PL.json +52 -17
  163. umap/static/umap/locale/pt.js +52 -17
  164. umap/static/umap/locale/pt.json +52 -17
  165. umap/static/umap/locale/pt_BR.js +52 -17
  166. umap/static/umap/locale/pt_BR.json +52 -17
  167. umap/static/umap/locale/pt_PT.js +52 -17
  168. umap/static/umap/locale/pt_PT.json +52 -17
  169. umap/static/umap/locale/ro.js +52 -17
  170. umap/static/umap/locale/ro.json +52 -17
  171. umap/static/umap/locale/ru.js +52 -17
  172. umap/static/umap/locale/ru.json +52 -17
  173. umap/static/umap/locale/si.js +1 -1
  174. umap/static/umap/locale/si.json +1 -1
  175. umap/static/umap/locale/sk_SK.js +52 -17
  176. umap/static/umap/locale/sk_SK.json +52 -17
  177. umap/static/umap/locale/sl.js +52 -17
  178. umap/static/umap/locale/sl.json +52 -17
  179. umap/static/umap/locale/sr.js +52 -17
  180. umap/static/umap/locale/sr.json +52 -17
  181. umap/static/umap/locale/sv.js +52 -17
  182. umap/static/umap/locale/sv.json +52 -17
  183. umap/static/umap/locale/th_TH.js +52 -17
  184. umap/static/umap/locale/th_TH.json +52 -17
  185. umap/static/umap/locale/tr.js +52 -17
  186. umap/static/umap/locale/tr.json +52 -17
  187. umap/static/umap/locale/uk_UA.js +52 -17
  188. umap/static/umap/locale/uk_UA.json +52 -17
  189. umap/static/umap/locale/vi.js +52 -17
  190. umap/static/umap/locale/vi.json +52 -17
  191. umap/static/umap/locale/vi_VN.json +52 -17
  192. umap/static/umap/locale/zh.js +52 -17
  193. umap/static/umap/locale/zh.json +52 -17
  194. umap/static/umap/locale/zh_CN.json +52 -17
  195. umap/static/umap/locale/zh_TW.Big5.json +52 -17
  196. umap/static/umap/locale/zh_TW.js +52 -16
  197. umap/static/umap/locale/zh_TW.json +52 -16
  198. umap/static/umap/map.css +63 -226
  199. umap/static/umap/unittests/utils.js +18 -0
  200. umap/static/umap/vars.css +23 -5
  201. umap/templates/umap/components/alerts/alert.html +32 -29
  202. umap/templates/umap/css.html +2 -1
  203. umap/templates/umap/login_popup_end.html +18 -9
  204. umap/templates/umap/user_map_table.html +7 -2
  205. umap/tests/integration/conftest.py +10 -6
  206. umap/tests/integration/test_anonymous_owned_map.py +90 -37
  207. umap/tests/integration/test_basics.py +25 -1
  208. umap/tests/integration/test_browser.py +37 -0
  209. umap/tests/integration/test_conditional_rules.py +107 -52
  210. umap/tests/integration/test_draw_polygon.py +6 -0
  211. umap/tests/integration/test_draw_polyline.py +11 -0
  212. umap/tests/integration/test_edit_marker.py +1 -1
  213. umap/tests/integration/test_export_map.py +19 -0
  214. umap/tests/integration/test_fields.py +541 -0
  215. umap/tests/integration/test_filters.py +616 -0
  216. umap/tests/integration/test_iframe.py +1 -1
  217. umap/tests/integration/test_import.py +38 -42
  218. umap/tests/integration/test_map_preview.py +1 -1
  219. umap/tests/integration/test_picto.py +1 -1
  220. umap/tests/integration/test_popup.py +31 -0
  221. umap/tests/integration/test_remote_data.py +60 -4
  222. umap/tests/integration/test_save.py +1 -1
  223. umap/tests/integration/test_share.py +4 -4
  224. umap/tests/integration/test_tableeditor.py +31 -7
  225. umap/tests/integration/test_websocket_sync.py +71 -20
  226. umap/tests/test_dashboard.py +11 -1
  227. umap/tests/test_statics.py +2 -2
  228. umap/tests/test_utils.py +19 -2
  229. umap/tests/test_views.py +1 -1
  230. umap/urls.py +1 -0
  231. umap/utils.py +8 -1
  232. umap/views.py +5 -0
  233. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/METADATA +15 -15
  234. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/RECORD +237 -233
  235. umap/static/umap/js/modules/facets.js +0 -164
  236. umap/tests/integration/test_facets_browser.py +0 -279
  237. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/WHEEL +0 -0
  238. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/entry_points.txt +0 -0
  239. {umap_project-3.3.6.dist-info → umap_project-3.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,616 @@
1
+ import copy
2
+ import re
3
+
4
+ import pytest
5
+ from playwright.sync_api import expect
6
+
7
+ from umap.models import DataLayer, Map
8
+
9
+ from ..base import DataLayerFactory
10
+
11
+ pytestmark = pytest.mark.django_db
12
+
13
+
14
+ DATALAYER_DATA1 = {
15
+ "type": "FeatureCollection",
16
+ "features": [
17
+ {
18
+ "type": "Feature",
19
+ "properties": {
20
+ "mytype": "even",
21
+ "name": "Point 2",
22
+ "mynumber": 10,
23
+ "mydate": "2024/04/14 12:19:17",
24
+ },
25
+ "geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
26
+ },
27
+ {
28
+ "type": "Feature",
29
+ "properties": {
30
+ "mytype": "odd",
31
+ "name": "Point 1",
32
+ "mynumber": 12,
33
+ "mydate": "2024/03/13 12:20:20",
34
+ },
35
+ "geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
36
+ },
37
+ ],
38
+ "_umap_options": {
39
+ "name": "Calque 1",
40
+ },
41
+ }
42
+
43
+
44
+ DATALAYER_DATA2 = {
45
+ "type": "FeatureCollection",
46
+ "features": [
47
+ {
48
+ "type": "Feature",
49
+ "properties": {
50
+ "mytype": "even",
51
+ "name": "Point 4",
52
+ "mynumber": 10,
53
+ "mydate": "2024/08/18 13:14:15",
54
+ },
55
+ "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]},
56
+ },
57
+ {
58
+ "type": "Feature",
59
+ "properties": {
60
+ "mytype": "odd",
61
+ "name": "Point 3",
62
+ "mynumber": 14,
63
+ "mydate": "2024-04-14T10:19:17.000Z",
64
+ },
65
+ "geometry": {"type": "Point", "coordinates": [4.372559, 47.945786]},
66
+ },
67
+ ],
68
+ "_umap_options": {
69
+ "name": "Calque 2",
70
+ },
71
+ }
72
+
73
+
74
+ DATALAYER_DATA3 = {
75
+ "type": "FeatureCollection",
76
+ "features": [
77
+ {
78
+ "type": "Feature",
79
+ "properties": {"name": "a polygon"},
80
+ "geometry": {
81
+ "type": "Polygon",
82
+ "coordinates": [
83
+ [
84
+ [2.12, 49.57],
85
+ [1.08, 49.02],
86
+ [2.51, 47.55],
87
+ [3.19, 48.77],
88
+ [2.12, 49.57],
89
+ ]
90
+ ],
91
+ },
92
+ },
93
+ ],
94
+ "_umap_options": {"name": "Calque 2", "browsable": False},
95
+ }
96
+
97
+
98
+ def test_simple_facet_search(live_server, page, map):
99
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
100
+ map.settings["properties"]["filters"] = [
101
+ {"fieldKey": "mytype", "label": "My type"},
102
+ {"fieldKey": "mynumber", "label": "My number", "widget": "MinMax"},
103
+ ]
104
+ map.settings["properties"]["fields"] = [
105
+ {"key": "mytype", "type": "String"},
106
+ {"key": "mynumber", "type": "Number"},
107
+ ]
108
+ map.settings["properties"]["showLabel"] = True
109
+ map.save()
110
+ DataLayerFactory(map=map, data=DATALAYER_DATA1)
111
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
112
+ DataLayerFactory(map=map, data=DATALAYER_DATA3)
113
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
114
+ panel = page.locator(".panel.left.on")
115
+ expect(panel).to_have_class(re.compile(".*expanded.*"))
116
+ expect(panel.locator(".umap-browser")).to_be_visible()
117
+ # From a non browsable datalayer, should not be impacted
118
+ paths = page.locator(".leaflet-overlay-pane path")
119
+ expect(paths).to_be_visible()
120
+ expect(panel).to_be_visible()
121
+ # Facet name
122
+ expect(page.get_by_text("My type")).to_be_visible()
123
+ # Facet values
124
+ oven = page.get_by_text("even")
125
+ odd = page.get_by_text("odd")
126
+ expect(oven).to_be_visible()
127
+ expect(odd).to_be_visible()
128
+ expect(paths).to_be_visible()
129
+ markers = page.locator(".leaflet-marker-icon")
130
+ expect(markers).to_have_count(4)
131
+ # Tooltips
132
+ # Sometimes PW founds two tooltips with the same name, but cannot reproduce it.
133
+ page.wait_for_timeout(300)
134
+ expect(page.get_by_role("tooltip", name="Point 1")).to_be_visible()
135
+ expect(page.get_by_role("tooltip", name="Point 2")).to_be_visible()
136
+ expect(page.get_by_role("tooltip", name="Point 3")).to_be_visible()
137
+ expect(page.get_by_role("tooltip", name="Point 4")).to_be_visible()
138
+
139
+ # Datalist
140
+ expect(panel.get_by_text("Point 1")).to_be_visible()
141
+ expect(panel.get_by_text("Point 2")).to_be_visible()
142
+ expect(panel.get_by_text("Point 3")).to_be_visible()
143
+ expect(panel.get_by_text("Point 4")).to_be_visible()
144
+
145
+ # Now let's filter
146
+ odd.click()
147
+ expect(markers).to_have_count(2)
148
+ expect(page.get_by_role("tooltip", name="Point 2")).to_be_hidden()
149
+ expect(page.get_by_role("tooltip", name="Point 4")).to_be_hidden()
150
+ expect(page.get_by_role("tooltip", name="Point 1")).to_be_visible()
151
+ expect(page.get_by_role("tooltip", name="Point 3")).to_be_visible()
152
+ expect(panel.get_by_text("Point 2")).to_be_hidden()
153
+ expect(panel.get_by_text("Point 4")).to_be_hidden()
154
+ expect(panel.get_by_text("Point 1")).to_be_visible()
155
+ expect(panel.get_by_text("Point 3")).to_be_visible()
156
+ expect(paths).to_be_visible
157
+ # Now let's filter
158
+ odd.click()
159
+ expect(markers).to_have_count(4)
160
+ expect(paths).to_be_visible()
161
+
162
+ # Let's filter using the number facet
163
+ expect(page.get_by_text("My Number")).to_be_visible()
164
+ expect(page.get_by_label("Min")).to_have_value("10")
165
+ expect(page.get_by_label("Max")).to_have_value("14")
166
+ page.get_by_label("Min").fill("11")
167
+ page.keyboard.press("Tab") # Move out of the input, so the "change" event is sent
168
+ expect(markers).to_have_count(2)
169
+ expect(paths).to_be_visible()
170
+ page.get_by_label("Max").fill("13")
171
+ page.keyboard.press("Tab")
172
+ expect(markers).to_have_count(1)
173
+
174
+ # Now let's combine
175
+ page.get_by_label("Min").fill("10")
176
+ page.keyboard.press("Tab")
177
+ expect(markers).to_have_count(3)
178
+ odd.click()
179
+ expect(markers).to_have_count(1)
180
+ expect(paths).to_be_visible()
181
+
182
+
183
+ def test_date_facet_search(live_server, page, map):
184
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
185
+ map.settings["properties"]["filters"] = [
186
+ {"fieldKey": "mydate", "label": "Date filter", "widget": "MinMax"}
187
+ ]
188
+ map.settings["properties"]["fields"] = [{"key": "mydate", "type": "Date"}]
189
+ map.save()
190
+ DataLayerFactory(map=map, data=DATALAYER_DATA1)
191
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
192
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/47.5/-1.5")
193
+ markers = page.locator(".leaflet-marker-icon")
194
+ expect(markers).to_have_count(4)
195
+ expect(page.get_by_text("Date Filter")).to_be_visible()
196
+ expect(page.get_by_label("From")).to_have_value("2024-03-13")
197
+ expect(page.get_by_label("Until")).to_have_value("2024-08-18")
198
+ page.get_by_label("From").fill("2024-03-14")
199
+ expect(markers).to_have_count(3)
200
+ page.get_by_label("Until").fill("2024-08-17")
201
+ expect(markers).to_have_count(2)
202
+
203
+
204
+ def test_choice_with_empty_value(live_server, page, map):
205
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
206
+ map.settings["properties"]["fields"] = [{"key": "mytype", "type": "String"}]
207
+ map.settings["properties"]["filters"] = [{"fieldKey": "mytype", "label": "My type"}]
208
+ map.save()
209
+ data = copy.deepcopy(DATALAYER_DATA1)
210
+ data["features"][0]["properties"]["mytype"] = ""
211
+ del data["features"][1]["properties"]["mytype"]
212
+ DataLayerFactory(map=map, data=data)
213
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
214
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/47.5/-1.5")
215
+ expect(page.get_by_text("<empty value>")).to_be_visible()
216
+ markers = page.locator(".leaflet-marker-icon")
217
+ expect(markers).to_have_count(4)
218
+ page.get_by_text("<empty value>").click()
219
+ expect(markers).to_have_count(2)
220
+
221
+
222
+ def test_number_with_zero_value(live_server, page, map):
223
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
224
+ map.settings["properties"]["filters"] = [
225
+ {"fieldKey": "mynumber", "label": "Filter", "widget": "MinMax"}
226
+ ]
227
+ map.settings["properties"]["fields"] = [{"key": "mynumber", "type": "Number"}]
228
+ map.save()
229
+ data = copy.deepcopy(DATALAYER_DATA1)
230
+ data["features"][0]["properties"]["mynumber"] = 0
231
+ DataLayerFactory(map=map, data=data)
232
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
233
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/47.5/-1.5")
234
+ expect(page.get_by_label("Min")).to_have_value("0")
235
+ expect(page.get_by_label("Max")).to_have_value("14")
236
+ page.get_by_label("Min").fill("1")
237
+ page.keyboard.press("Tab") # Move out of the input, so the "change" event is sent
238
+ markers = page.locator(".leaflet-marker-icon")
239
+ expect(markers).to_have_count(3)
240
+
241
+
242
+ def test_facets_search_are_persistent_when_closing_panel(live_server, page, map):
243
+ map.settings["properties"]["onLoadPanel"] = "datafilters"
244
+ map.settings["properties"]["filters"] = [
245
+ {"fieldKey": "mytype", "label": "My type"},
246
+ {"fieldKey": "mynumber", "label": "My Number", "widget": "MinMax"},
247
+ ]
248
+ map.settings["properties"]["fields"] = [
249
+ {"key": "mytype"},
250
+ {"key": "mynumber", "type": "Number"},
251
+ ]
252
+ map.save()
253
+ DataLayerFactory(map=map, data=DATALAYER_DATA1)
254
+ DataLayerFactory(map=map, data=DATALAYER_DATA2)
255
+ page.goto(f"{live_server.url}{map.get_absolute_url()}#6/48.948/1.670")
256
+ panel = page.locator(".panel.left")
257
+
258
+ # Facet values
259
+ odd = page.get_by_label("odd")
260
+ markers = page.locator(".leaflet-marker-icon")
261
+ expect(markers).to_have_count(4)
262
+
263
+ # Datalist in the browser
264
+ expect(panel.get_by_text("Point 1")).to_be_visible()
265
+ expect(panel.get_by_text("Point 2")).to_be_visible()
266
+ expect(panel.get_by_text("Point 3")).to_be_visible()
267
+ expect(panel.get_by_text("Point 4")).to_be_visible()
268
+
269
+ # Now let's filter
270
+ odd.click()
271
+ expect(page.locator(".filters summary")).to_have_attribute("data-badge", " ")
272
+ expect(page.locator(".umap-control-browse")).to_have_attribute("data-badge", " ")
273
+ expect(markers).to_have_count(2)
274
+ expect(panel.get_by_text("Point 2")).to_be_hidden()
275
+ expect(panel.get_by_text("Point 4")).to_be_hidden()
276
+ expect(panel.get_by_text("Point 1")).to_be_visible()
277
+ expect(panel.get_by_text("Point 3")).to_be_visible()
278
+
279
+ # Let's filter using the number facet
280
+ expect(panel.get_by_label("Min")).to_have_value("10")
281
+ expect(panel.get_by_label("Max")).to_have_value("14")
282
+ page.get_by_label("Min").fill("13")
283
+ page.keyboard.press("Tab") # Move out of the input, so the "change" event is sent
284
+ expect(panel.get_by_label("Min")).to_have_attribute("data-modified", "true")
285
+ expect(markers).to_have_count(1)
286
+ expect(panel.get_by_text("Point 2")).to_be_hidden()
287
+ expect(panel.get_by_text("Point 4")).to_be_hidden()
288
+ expect(panel.get_by_text("Point 1")).to_be_hidden()
289
+ expect(panel.get_by_text("Point 3")).to_be_visible()
290
+
291
+ # Close panel
292
+ expect(panel.locator(".filters summary")).to_have_attribute("data-badge", " ")
293
+ expect(page.locator(".umap-control-browse")).to_have_attribute("data-badge", " ")
294
+ panel.get_by_role("button", name="Close").click()
295
+ page.get_by_role("button", name="Open browser").click()
296
+ expect(panel.get_by_label("Min")).to_have_value("13")
297
+ expect(panel.get_by_label("Min")).to_have_attribute("data-modified", "true")
298
+ expect(panel.get_by_label("odd")).to_be_checked()
299
+
300
+ # Datalist in the browser should be inchanged
301
+ expect(panel.get_by_text("Point 2")).to_be_hidden()
302
+ expect(panel.get_by_text("Point 4")).to_be_hidden()
303
+ expect(panel.get_by_text("Point 1")).to_be_hidden()
304
+ expect(panel.get_by_text("Point 3")).to_be_visible()
305
+
306
+
307
+ def test_can_load_legacy_facetKey(live_server, page, openmap):
308
+ openmap.settings["properties"]["facetKey"] = (
309
+ "mytype|My Type|radio,mynumber|My Number|number,mydate|My Date|date"
310
+ )
311
+ openmap.save()
312
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
313
+ expect(
314
+ page.get_by_text("The map has been upgraded to latest version, please save it.")
315
+ ).to_be_visible()
316
+ with page.expect_response(re.compile("./update/settings/.*")):
317
+ page.get_by_role("button", name="Save", exact=True).click()
318
+ saved = Map.objects.first()
319
+ assert "facetKey" not in saved.settings["properties"]
320
+ assert saved.settings["properties"]["filters"] == [
321
+ {
322
+ "fieldKey": "mytype",
323
+ "label": "My Type",
324
+ "widget": "Radio",
325
+ },
326
+ {
327
+ "fieldKey": "mynumber",
328
+ "label": "My Number",
329
+ "widget": "MinMax",
330
+ },
331
+ {
332
+ "fieldKey": "mydate",
333
+ "label": "My Date",
334
+ "widget": "MinMax",
335
+ },
336
+ ]
337
+ assert saved.settings["properties"]["fields"] == [
338
+ {"key": "mytype", "type": "String"},
339
+ {"key": "mynumber", "type": "Number"},
340
+ {"key": "mydate", "type": "Date"},
341
+ ]
342
+
343
+
344
+ def test_deleting_field_should_delete_filter(live_server, page, openmap, datalayer):
345
+ datalayer.settings["fields"] = [
346
+ {"key": "name", "type": "String"},
347
+ {"key": "foobar", "type": "Number"},
348
+ {"key": "description", "type": "Text"},
349
+ ]
350
+ datalayer.settings["filters"] = [
351
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
352
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
353
+ ]
354
+ datalayer.save()
355
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
356
+ page.get_by_role("button", name="Manage layers").click()
357
+ page.get_by_role("button", name="Edit", exact=True).click()
358
+ page.get_by_text("Manage Fields").click()
359
+ page.get_by_role("button", name="Delete this field").nth(1).click()
360
+ page.get_by_role("button", name="OK").click()
361
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
362
+ page.get_by_role("button", name="Save").click()
363
+ saved = DataLayer.objects.first()
364
+ assert saved.settings["fields"] == [
365
+ {"key": "name", "type": "String"},
366
+ {"key": "description", "type": "Text"},
367
+ ]
368
+ saved.settings["filters"] == [
369
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
370
+ ]
371
+ page.locator(".edit-undo").click()
372
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
373
+ page.get_by_role("button", name="Save").click()
374
+ saved = DataLayer.objects.first()
375
+ assert saved.settings["fields"] == [
376
+ {"key": "name", "type": "String"},
377
+ {"key": "foobar", "type": "Number"},
378
+ {"key": "description", "type": "Text"},
379
+ ]
380
+ saved.settings["filters"] == [
381
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
382
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
383
+ ]
384
+
385
+
386
+ def test_deleting_field_from_map_should_delete_filter(live_server, page, openmap):
387
+ openmap.settings["properties"]["fields"] = [
388
+ {"key": "name", "type": "String"},
389
+ {"key": "foobar", "type": "Number"},
390
+ {"key": "description", "type": "Text"},
391
+ ]
392
+ openmap.settings["properties"]["filters"] = [
393
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
394
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
395
+ ]
396
+ openmap.save()
397
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
398
+ page.get_by_role("button", name="Map advanced properties").click()
399
+ page.get_by_text("Manage Fields").click()
400
+ page.get_by_role("button", name="Delete this field").nth(1).click()
401
+ page.get_by_role("button", name="OK").click()
402
+ with page.expect_response(re.compile(r"./update/settings/.*")):
403
+ page.get_by_role("button", name="Save").click()
404
+ saved = Map.objects.first()
405
+ assert saved.settings["properties"]["fields"] == [
406
+ {"key": "name", "type": "String"},
407
+ {"key": "description", "type": "Text"},
408
+ ]
409
+ saved.settings["properties"]["filters"] == [
410
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
411
+ ]
412
+ page.locator(".edit-undo").click()
413
+ with page.expect_response(re.compile(r"./update/settings/.*")):
414
+ page.get_by_role("button", name="Save").click()
415
+ saved = Map.objects.first()
416
+ assert saved.settings["properties"]["fields"] == [
417
+ {"key": "name", "type": "String"},
418
+ {"key": "foobar", "type": "Number"},
419
+ {"key": "description", "type": "Text"},
420
+ ]
421
+ saved.settings["properties"]["filters"] == [
422
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
423
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
424
+ ]
425
+
426
+
427
+ def test_can_create_filter_from_new_field(live_server, page, openmap):
428
+ openmap.settings["properties"]["onLoadPanel"] = "datafilters"
429
+ openmap.save()
430
+ DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
431
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
432
+ page.get_by_role("button", name="Manage layers").click()
433
+ page.get_by_role("button", name="Edit", exact=True).nth(1).click()
434
+ page.get_by_role("heading", name="Manage Fields").click()
435
+ page.get_by_role("button", name="Add a new field").click()
436
+ page.get_by_role("textbox", name="Field Name ✔").fill("foobar")
437
+ page.get_by_label("Field Type").select_option("Boolean")
438
+ page.get_by_role("button", name="Add filter for this field").click()
439
+ page.get_by_role("textbox", name="Human readable name of the").fill("Foo Bar")
440
+ expect(page.get_by_text("Yes/No")).to_be_checked()
441
+ page.wait_for_timeout(300) # Input throttling.
442
+ page.get_by_text("Edit this field").click()
443
+ expect(page.locator(".umap-filter span").filter(has_text="Foo Bar")).to_be_visible()
444
+ with page.expect_response(re.compile(r".*/datalayer/update/")):
445
+ page.get_by_role("button", name="Save").click()
446
+ saved = DataLayer.objects.first()
447
+ assert saved.settings["fields"] == [
448
+ {
449
+ "key": "mytype",
450
+ "type": "String",
451
+ },
452
+ {
453
+ "key": "name",
454
+ "type": "String",
455
+ },
456
+ {
457
+ "key": "mynumber",
458
+ "type": "String",
459
+ },
460
+ {
461
+ "key": "mydate",
462
+ "type": "String",
463
+ },
464
+ {
465
+ "key": "foobar",
466
+ "type": "Boolean",
467
+ },
468
+ ]
469
+
470
+ assert saved.settings["filters"] == [
471
+ {
472
+ "fieldKey": "foobar",
473
+ "label": "Foo Bar",
474
+ "widget": "Switch",
475
+ },
476
+ ]
477
+
478
+
479
+ def test_can_create_new_filter_on_map_from_panel(live_server, page, openmap):
480
+ openmap.settings["properties"]["onLoadPanel"] = "datafilters"
481
+ openmap.settings["properties"]["fields"] = [
482
+ {"key": "name", "type": "String"},
483
+ {"key": "foobar", "type": "Number"},
484
+ {"key": "description", "type": "Text"},
485
+ ]
486
+ openmap.save()
487
+ DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
488
+ page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit")
489
+ page.get_by_role("button", name="Manage filters").click()
490
+ page.get_by_role("button", name="Add filter").first.click()
491
+ page.get_by_label("Filter on").select_option("foobar")
492
+ page.get_by_role("textbox", name="Human readable name").fill("Foo Bar")
493
+ page.wait_for_timeout(300)
494
+ page.get_by_text("Exclusive choice").click()
495
+ page.get_by_role("button", name="OK").click()
496
+ expect(
497
+ page.locator(".panel.left").get_by_role("group", name="Foo Bar")
498
+ ).to_be_visible()
499
+ with page.expect_response(re.compile("./update/settings/.*")):
500
+ page.get_by_role("button", name="Save", exact=True).click()
501
+ saved = Map.objects.first()
502
+ assert saved.settings["properties"]["filters"] == [
503
+ {"fieldKey": "foobar", "label": "Foo Bar", "widget": "Radio"}
504
+ ]
505
+
506
+
507
+ def test_can_create_new_filter_on_datalayer_from_panel(live_server, page, openmap):
508
+ openmap.settings["properties"]["fields"] = [
509
+ {"key": "name", "type": "String"},
510
+ {"key": "foobar", "type": "Number"},
511
+ {"key": "description", "type": "Text"},
512
+ ]
513
+ openmap.save()
514
+ datalayer = DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
515
+ page.goto(
516
+ f"{live_server.url}{openmap.get_absolute_url()}?edit&onLoadPanel=datafilters"
517
+ )
518
+ page.get_by_role("button", name="Manage filters").click()
519
+ page.get_by_role("button", name="Add filter").nth(1).click()
520
+ expect(page.get_by_label("Apply filter to")).to_have_value(f"layer:{datalayer.pk}")
521
+ page.get_by_label("Filter on").select_option("mynumber")
522
+ page.get_by_role("textbox", name="Human readable name of the").fill("Foo Bar")
523
+ page.wait_for_timeout(300)
524
+ page.get_by_text("Exclusive choice").click()
525
+ page.get_by_role("button", name="OK").click()
526
+ expect(
527
+ page.locator(".panel.left").get_by_role("group", name="Foo Bar")
528
+ ).to_be_visible()
529
+ with page.expect_response(re.compile("./datalayer/update/.*")):
530
+ page.get_by_role("button", name="Save", exact=True).click()
531
+ saved = DataLayer.objects.first()
532
+ assert saved.settings["filters"] == [
533
+ {"fieldKey": "mynumber", "label": "Foo Bar", "widget": "Radio"}
534
+ ]
535
+
536
+
537
+ def test_can_edit_filter_from_panel(live_server, page, openmap):
538
+ openmap.settings["properties"]["fields"] = [
539
+ {"key": "name", "type": "String"},
540
+ {"key": "foobar", "type": "Number"},
541
+ {"key": "description", "type": "Text"},
542
+ ]
543
+ openmap.settings["properties"]["filters"] = [
544
+ {"fieldKey": "foobar", "widget": "MinMax", "label": "Foo Bar"},
545
+ {"fieldKey": "name", "widget": "Checkbox", "label": "Bar Foo"},
546
+ ]
547
+ openmap.save()
548
+ page.goto(
549
+ f"{live_server.url}{openmap.get_absolute_url()}?edit&onLoadPanel=datafilters"
550
+ )
551
+ page.get_by_role("group", name="Foo Bar").get_by_role("button").click()
552
+ expect(page.get_by_label("Apply filter to")).to_have_value(f"map:{openmap.pk}")
553
+ expect(page.get_by_label("Apply filter to")).to_be_disabled()
554
+ page.get_by_role("textbox", name="Human readable name of the").fill("Foo Bar Baz")
555
+ page.wait_for_timeout(300) # Input throttling.
556
+ page.get_by_role("button", name="OK").click()
557
+ expect(
558
+ page.get_by_role("group", name="Foo Bar Baz").locator("legend")
559
+ ).to_be_visible()
560
+
561
+
562
+ def test_filter_with_enum(live_server, openmap, page):
563
+ data = {
564
+ "type": "FeatureCollection",
565
+ "features": [
566
+ {
567
+ "type": "Feature",
568
+ "properties": {
569
+ "name": "Point 2",
570
+ "products": "flour,vegetable,bread",
571
+ },
572
+ "geometry": {"type": "Point", "coordinates": [0.065918, 48.385442]},
573
+ },
574
+ {
575
+ "type": "Feature",
576
+ "properties": {
577
+ "name": "Point 1",
578
+ "products": "vegetable,bread",
579
+ },
580
+ "geometry": {"type": "Point", "coordinates": [3.55957, 49.767074]},
581
+ },
582
+ {
583
+ "type": "Feature",
584
+ "properties": {
585
+ "name": "Point 3",
586
+ "products": "flour,vegetable,milk,eggs",
587
+ },
588
+ "geometry": {"type": "Point", "coordinates": [0.856934, 45.290347]},
589
+ },
590
+ ],
591
+ "_umap_options": {
592
+ "name": "Calque 1",
593
+ "fields": [{"key": "products", "type": "Enum"}],
594
+ },
595
+ }
596
+ DataLayerFactory(map=openmap, data=data)
597
+ page.goto(
598
+ f"{live_server.url}{openmap.get_absolute_url()}?edit&onLoadPanel=datafilters#6/48.948/1.670"
599
+ )
600
+ page.get_by_role("button", name="Manage filters").click()
601
+ page.get_by_role("button", name="Add filter").click()
602
+ page.get_by_label("Filter on").select_option("products")
603
+ page.get_by_role("button", name="OK").click()
604
+ expect(
605
+ page.locator(".umap-browser .filters span").filter(has_text="products")
606
+ ).to_be_visible()
607
+ markers = page.locator(".leaflet-marker-icon")
608
+ expect(markers).to_have_count(3)
609
+ page.get_by_role("checkbox", name="bread").check()
610
+ expect(markers).to_have_count(2)
611
+ page.get_by_role("checkbox", name="bread").uncheck()
612
+ expect(markers).to_have_count(3)
613
+ page.get_by_role("checkbox", name="vegetable").check()
614
+ expect(markers).to_have_count(3)
615
+ page.get_by_role("checkbox", name="milk").check()
616
+ expect(markers).to_have_count(1)
@@ -6,7 +6,7 @@ def test_home_control_is_hidden(live_server, map, tilelayer, page):
6
6
  <html>
7
7
  <head></head>
8
8
  <body>
9
- <iframe width="100%" height="300px" frameborder="0" allowfullscreen allow="geolocation"
9
+ <iframe style="width: 100%; height: 300px; border: 0;" allowfullscreen allow="geolocation"
10
10
  src="{map.get_absolute_url()}?scaleControl=false&miniMap=false&scrollWheelZoom=false&zoomControl=true&editMode=disabled&moreControl=true&searchControl=null&tilelayersControl=null&embedControl=null&datalayersControl=true&onLoadPanel=caption&captionBar=false&captionMenus=true"></iframe>
11
11
  </body>
12
12
  </html>