@geogirafe/lib-geoportal 1.2.0-dev.2669461096 → 1.2.0-dev.2675963665

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.
package/README.md CHANGED
@@ -163,6 +163,42 @@ This will automatically copy the `config.json` file in the right place, and down
163
163
  > Please note that today, the GeoGirafe Viewer is using a backend based on GeoMapFish.
164
164
  > The services you will have to use in your configuration must therefore be compliant with GeoMapFish.
165
165
 
166
+ ## Testing
167
+
168
+ There are 2 kinds of tests in the GeoGirafe project:
169
+
170
+ ### unitests (with vitest)
171
+
172
+ To run the unitests, execute the following command:
173
+
174
+ ```
175
+ npm run test
176
+ ```
177
+
178
+ And that's it.
179
+
180
+ ### End-to-end tests (with playwright)
181
+
182
+ To be able to run the playwright tests, first install the headless chrome and firefox binaries:
183
+
184
+ ```
185
+ npx playwright install
186
+ ```
187
+
188
+ Then you must start the application:
189
+
190
+ ```
191
+ npm start
192
+ ```
193
+
194
+ And then in another console you can run the tests:
195
+
196
+ ```
197
+ npx playwright test
198
+ ```
199
+
200
+ The result page will omen at the end of the tests.
201
+
166
202
  ## Build for Production
167
203
 
168
204
  ```bash
@@ -123,8 +123,10 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
123
123
  }
124
124
  manageUserInteraction() {
125
125
  // Deactivate the preview of search results
126
- this.context.configManager.Config.search.objectPreview = false;
127
- this.context.configManager.Config.search.layerPreview = false;
126
+ if (this.context.configManager.Config.search) {
127
+ this.context.configManager.Config.search.objectPreview = false;
128
+ this.context.configManager.Config.search.layerPreview = false;
129
+ }
128
130
  // Force window as selection component
129
131
  this.state.interface.selectionComponent = 'window';
130
132
  // Deactivate selection if the selectionbox is not active
@@ -284,7 +284,7 @@
284
284
  "Layers (from Capabilities)": "Layer (aus Capabilities)",
285
285
  "layers selected": "ausgewählte Ebene(n)",
286
286
  "Layout": "Layout",
287
- "layout-panel": "Schnittstellenlayout",
287
+ "layout-panel": "Ansichtslayout",
288
288
  "less than": "kleiner als",
289
289
  "less than or equal to": "kleiner oder gleich",
290
290
  "LiDAR profile service error": "Fehler beim LiDAR-Profil-Dienst",
@@ -47,9 +47,12 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
47
47
  if (!this.state.interface.basemapComponentVisible) {
48
48
  this.hideHelpFor('basemap');
49
49
  }
50
- if (!this.state.interface.searchComponentVisible) {
50
+ if (!this.state.interface.searchComponentVisible || !this.context.configManager.Config.search) {
51
51
  this.hideHelpFor('search');
52
52
  }
53
+ if (!this.context.configManager.Config.onboarding) {
54
+ this.hideHelpFor('onboarding');
55
+ }
53
56
  }
54
57
  hideHelpFor(elementId) {
55
58
  this.shadow.querySelector(`#${elementId}`).style.display = 'none';
@@ -75,7 +75,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
75
75
  }
76
76
  }
77
77
  createPreviewLayer() {
78
- this.paintSearchResults = this.context.configManager.Config.search.paintSearchResults;
78
+ this.paintSearchResults = this.context.configManager.Config.search?.paintSearchResults;
79
79
  this.previewGeoLayer = new VectorLayer({
80
80
  properties: {
81
81
  addToPrintedLayers: true
@@ -115,8 +115,8 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
115
115
  }
116
116
  connectedCallback() {
117
117
  super.connectedCallback();
118
- this.defaultSearchStrokeColor = this.context.configManager.Config.search.defaultStrokeColor;
119
- this.defaultSearchFillColor = this.context.configManager.Config.search.defaultFillColor;
118
+ this.defaultSearchStrokeColor = this.context.configManager.Config.search?.defaultStrokeColor;
119
+ this.defaultSearchFillColor = this.context.configManager.Config.search?.defaultFillColor;
120
120
  this.createPreviewLayer();
121
121
  this.render();
122
122
  super.girafeTranslate();
@@ -260,13 +260,13 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
260
260
  resultHtmlElement.scrollIntoView({ block: 'nearest' });
261
261
  }
262
262
  preview(result) {
263
- if (result.getGeometry() && this.context.configManager.Config.search.objectPreview) {
263
+ if (result.getGeometry() && this.context.configManager.Config.search?.objectPreview) {
264
264
  // Result with geometry
265
265
  this.addFeatureToPreview(result);
266
266
  this.updatePreviewLayerStyle();
267
267
  }
268
268
  const firstAction = result.get('actions')?.[0];
269
- if (firstAction?.action.startsWith('add_layer') && this.context.configManager.Config.search.layerPreview) {
269
+ if (firstAction?.action.startsWith('add_layer') && this.context.configManager.Config.search?.layerPreview) {
270
270
  const layer = this.context.themesHelper.findLayerByName(firstAction.data);
271
271
  if (layer) {
272
272
  const clonedTheme = this.context.themesHelper.getMinimalClonedThemeForLayer(layer);
@@ -348,7 +348,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
348
348
  }
349
349
  }
350
350
  zoomTo(extent) {
351
- const minResolution = this.context.configManager.Config.search.minResolution;
351
+ const minResolution = this.context.configManager.Config.search?.minResolution;
352
352
  const currentResolution = this.map.getView().getResolution();
353
353
  const currentExtent = this.map.getView().calculateExtent();
354
354
  if (minResolution) {
@@ -453,7 +453,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
453
453
  if (colorPicker) {
454
454
  this.girafeColorPicker = new GirafeColorPicker({
455
455
  parent: colorPicker,
456
- color: this.context.configManager.Config.search.defaultStrokeColor,
456
+ color: this.context.configManager.Config.search?.defaultStrokeColor,
457
457
  popup: 'right'
458
458
  });
459
459
  this.girafeColorPicker.onChange = (color) => {
@@ -465,7 +465,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
465
465
  };
466
466
  this.girafeColorPicker.onOpen = (_) => {
467
467
  if (this.defaultColorHasChanged()) {
468
- this.girafeColorPicker?.setColor(this.context.configManager.Config.search.defaultStrokeColor, true);
468
+ this.girafeColorPicker?.setColor(this.context.configManager.Config.search?.defaultStrokeColor, true);
469
469
  }
470
470
  };
471
471
  }
@@ -476,10 +476,10 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
476
476
  }
477
477
  // Only update style if new colors were provided via color picker or default colors have changed
478
478
  if (this.defaultColorHasChanged()) {
479
- const defaultFillColor = this.context.configManager.Config.search.defaultFillColor;
479
+ const defaultFillColor = this.context.configManager.Config.search?.defaultFillColor;
480
480
  this.defaultSearchFillColor = defaultFillColor;
481
481
  this.searchFillColor = defaultFillColor;
482
- const defaultStrokeColor = this.context.configManager.Config.search.defaultStrokeColor;
482
+ const defaultStrokeColor = this.context.configManager.Config.search?.defaultStrokeColor;
483
483
  this.defaultSearchStrokeColor = defaultStrokeColor;
484
484
  this.searchStrokeColor = defaultStrokeColor;
485
485
  }
@@ -488,7 +488,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
488
488
  this.previewGeoLayer.setStyle(new Style({
489
489
  stroke: new Stroke({
490
490
  color: strokeColorWithFallback,
491
- width: this.context.configManager.Config.search.defaultStrokeWidth
491
+ width: this.context.configManager.Config.search?.defaultStrokeWidth
492
492
  }),
493
493
  fill: new Fill({ color: fillColorWithFallback }),
494
494
  image: new Icon({
@@ -501,8 +501,8 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
501
501
  }));
502
502
  }
503
503
  defaultColorHasChanged() {
504
- return (this.defaultSearchFillColor !== this.context.configManager.Config.search.defaultFillColor ||
505
- this.defaultSearchStrokeColor !== this.context.configManager.Config.search.defaultStrokeColor);
504
+ return (this.defaultSearchFillColor !== this.context.configManager.Config.search?.defaultFillColor ||
505
+ this.defaultSearchStrokeColor !== this.context.configManager.Config.search?.defaultStrokeColor);
506
506
  }
507
507
  getColoredPinIcon(hexColor) {
508
508
  const pin = `<svg xmlns="http://www.w3.org/2000/svg"
@@ -65,6 +65,13 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
65
65
  drawingFillColor: new UserPreference('drawing.defaultFillColor', null, 'visual', 'color'),
66
66
  drawingStrokeColor: new UserPreference('drawing.defaultStrokeColor', null, 'visual', 'color')
67
67
  };
68
+ // Remove preferences where no config entry exists (e.g. when the component isn't configured for this app instance)
69
+ for (const key in this.preferences) {
70
+ const configEntry = getPropertyByPath(this.context.configManager.Config, this.preferences[key].configPath);
71
+ if (!configEntry.found) {
72
+ delete this.preferences[key];
73
+ }
74
+ }
68
75
  this.subscribe('themes.isLoaded', (_, isLoaded) => {
69
76
  if (isLoaded) {
70
77
  this.initPreferenceOptions();
@@ -120,42 +127,59 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
120
127
  For preferences that have options to choose from, readout all possible select options to show in the dropdowns
121
128
  */
122
129
  initPreferenceOptions() {
123
- this.preferences.language.options = Object.keys(this.context.configManager.Config.languages.translations).map((key) => {
124
- return { label: key, value: key };
125
- });
126
- this.preferences.logLevel.options = [
127
- { label: 'debug', value: 'debug' },
128
- { label: 'info', value: 'info' },
129
- { label: 'warn', value: 'warn' },
130
- { label: 'error', value: 'error' }
131
- ];
132
- this.preferences.projection.options = Object.keys(this.context.configManager.Config.projections).map((key) => {
133
- return { label: this.context.configManager.Config.projections[key], value: key };
134
- });
130
+ if (this.preferences.language) {
131
+ this.preferences.language.options = Object.keys(this.context.configManager.Config.languages.translations).map((key) => {
132
+ return { label: key, value: key };
133
+ });
134
+ }
135
+ if (this.preferences.logLevel) {
136
+ this.preferences.logLevel.options = [
137
+ { label: 'debug', value: 'debug' },
138
+ { label: 'info', value: 'info' },
139
+ { label: 'warn', value: 'warn' },
140
+ { label: 'error', value: 'error' }
141
+ ];
142
+ }
143
+ if (this.preferences.projection) {
144
+ this.preferences.projection.options = Object.keys(this.context.configManager.Config.projections).map((key) => {
145
+ return { label: this.context.configManager.Config.projections[key], value: key };
146
+ });
147
+ }
135
148
  this.refreshThemeOptions();
136
- this.preferences.basemap.options = Object.keys(this.state.basemaps).map((key) => {
137
- const baseMapName = this.state.basemaps[Number(key)].name;
138
- return { label: baseMapName, value: baseMapName };
139
- });
140
- this.preferences.selectionComponent.options = [
141
- { label: 'window', value: 'window' },
142
- { label: 'grid', value: 'grid' }
143
- ];
144
- this.preferences.darkFrontendMode.options = [
145
- { label: 'same as system', value: undefined },
146
- { label: 'dark', value: true },
147
- { label: 'light', value: false }
148
- ];
149
- this.preferences.selectHighlightMode.options = [
150
- { label: 'highlight-all', value: HighlightMode.HighlightAll },
151
- { label: 'highlight-per-layer', value: HighlightMode.HighlightPerLayer }
152
- ];
149
+ if (this.preferences.basemap) {
150
+ this.preferences.basemap.options = Object.keys(this.state.basemaps).map((key) => {
151
+ const baseMapName = this.state.basemaps[Number(key)].name;
152
+ return { label: baseMapName, value: baseMapName };
153
+ });
154
+ }
155
+ if (this.preferences.selectionComponent) {
156
+ this.preferences.selectionComponent.options = [
157
+ { label: 'window', value: 'window' },
158
+ { label: 'grid', value: 'grid' }
159
+ ];
160
+ }
161
+ if (this.preferences.darkFrontendMode) {
162
+ this.preferences.darkFrontendMode.options = [
163
+ { label: 'same as system', value: undefined },
164
+ { label: 'dark', value: true },
165
+ { label: 'light', value: false }
166
+ ];
167
+ }
168
+ if (this.preferences.selectHighlightMode) {
169
+ this.preferences.selectHighlightMode.options = [
170
+ { label: 'highlight-all', value: HighlightMode.HighlightAll },
171
+ { label: 'highlight-per-layer', value: HighlightMode.HighlightPerLayer }
172
+ ];
173
+ }
153
174
  }
154
175
  /**
155
176
  * Collect all possible options for the default theme preference. This has to be repeatable, since the user
156
177
  * can create new custom themes at any time.
157
178
  */
158
179
  refreshThemeOptions() {
180
+ if (!this.preferences.theme) {
181
+ return;
182
+ }
159
183
  let defaultThemes = [{ label: '-', value: '' }];
160
184
  Object.keys(this.state.themes._allThemes).forEach((key) => {
161
185
  const themeName = this.state.themes._allThemes[Number(key)].name;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "GeoGirafe PSC",
6
6
  "url": "https://doc.geogirafe.org"
7
7
  },
8
- "version": "1.2.0-dev.2669461096",
8
+ "version": "1.2.0-dev.2675963665",
9
9
  "type": "module",
10
10
  "engines": {
11
11
  "node": ">=20.19.0"
@@ -66,6 +66,7 @@
66
66
  "@eslint/js": "10.0.1",
67
67
  "@geoblocks/mapfishprint": "0.3.0",
68
68
  "@geoblocks/print": "0.7.9",
69
+ "@playwright/test": "1.61.0",
69
70
  "@types/babel__generator": "7.27.0",
70
71
  "@types/d3": "7.4.3",
71
72
  "@types/file-saver": "2.0.7",
@@ -92,6 +93,7 @@
92
93
  "lint-staged": "16.4.0",
93
94
  "magic-string": "0.30.21",
94
95
  "minify": "14.1.0",
96
+ "odiff-bin": "4.3.8",
95
97
  "ol": "10.8.0",
96
98
  "ol-ext": "4.0.38",
97
99
  "ol-mapbox-style": "13.4.0",
@@ -176,5 +178,12 @@
176
178
  "trust-default-dev-certs-win": "buildtools\\trust-dev-certs.cmd",
177
179
  "size-analysis": "node buildtools/app-clean.js && cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode analyze",
178
180
  "create-component": "tsx buildtools/create-component.ts"
181
+ },
182
+ "allowScripts": {
183
+ "@swc/core@1.15.24": true,
184
+ "esbuild@0.28.1": true,
185
+ "esbuild@0.25.12": true,
186
+ "odiff-bin@4.3.8": true,
187
+ "protobufjs@8.0.1": true
179
188
  }
180
189
  }
package/styles/index.css CHANGED
@@ -25,6 +25,11 @@ header {
25
25
  justify-content: space-between;
26
26
  }
27
27
 
28
+ .header-center-align {
29
+ margin-left: auto;
30
+ margin-right: auto;
31
+ }
32
+
28
33
  img {
29
34
  filter: var(--svg-filter);
30
35
  }
@@ -42,8 +47,6 @@ girafe-theme-select {
42
47
 
43
48
  girafe-search {
44
49
  width: 36rem;
45
- margin-left: auto;
46
- margin-right: auto;
47
50
  }
48
51
 
49
52
  #buttonbar {
@@ -115,13 +115,9 @@
115
115
  <header>
116
116
  <girafe-theme-select data-tour="theme-selection"></girafe-theme-select>
117
117
  <img class="logo" alt="geogirafe-logo" src="images/logo/horizontal_black.svg" />
118
- <girafe-search id="search">
119
- <!-- <div slot="search-tooltip">
120
- <p>
121
- Create a custom tooltip
122
- </p>
123
- </div> -->
124
- </girafe-search>
118
+ <div class="header-center-align">
119
+ <girafe-search id="search" plugin="config:search"></girafe-search>
120
+ </div>
125
121
  <girafe-news-button plugin="config:news"></girafe-news-button>
126
122
  <girafe-oauth></girafe-oauth>
127
123
  <girafe-add-bookmark></girafe-add-bookmark>
@@ -1 +1 @@
1
- {"version":"1.2.0-dev.2669461096", "build":"2669461096", "date":"11/07/2026"}
1
+ {"version":"1.2.0-dev.2675963665", "build":"2675963665", "date":"14/07/2026"}
@@ -15,9 +15,6 @@
15
15
  "url": "mock/themes.json",
16
16
  "defaultTheme": ""
17
17
  },
18
- "search": {
19
- "providers": []
20
- },
21
18
  "basemaps": {
22
19
  "defaultBasemap": "OpenStreetMap",
23
20
  "OSM": true
@@ -42,7 +42,7 @@ declare class GirafeConfig {
42
42
  height: number;
43
43
  };
44
44
  };
45
- search: {
45
+ search?: {
46
46
  providers: ({
47
47
  type: 'geomapfish';
48
48
  url: string;
@@ -175,6 +175,9 @@ class GirafeConfig {
175
175
  };
176
176
  }
177
177
  initConfigShare(config) {
178
+ if (!config.share) {
179
+ return undefined;
180
+ }
178
181
  if (!config.share?.createUrl) {
179
182
  throw new Error(`Configuration for share.createUrl is required. See https://doc.geogirafe.org/docs/configuration`);
180
183
  }
@@ -201,6 +204,9 @@ class GirafeConfig {
201
204
  };
202
205
  }
203
206
  initConfigPrint(config) {
207
+ if (!config.print) {
208
+ return undefined;
209
+ }
204
210
  if (!config.print?.url) {
205
211
  throw new Error(`Configuration for print.url is required. See https://doc.geogirafe.org/docs/configuration`);
206
212
  }
@@ -210,7 +216,7 @@ class GirafeConfig {
210
216
  }
211
217
  initConfigSearch(config) {
212
218
  if (!config.search) {
213
- throw new Error('Configuration for search is required. See https://doc.geogirafe.org/docs/configuration');
219
+ return undefined;
214
220
  }
215
221
  const providers = [];
216
222
  for (const provider of config.search.providers ?? []) {
@@ -237,15 +243,18 @@ class GirafeConfig {
237
243
  console.warn(`Provider type is missing or not valid: ${provider['type']}`);
238
244
  }
239
245
  }
246
+ if (providers.length === 0) {
247
+ console.warn('No search provider configured. Search will not be available.');
248
+ }
240
249
  return {
241
250
  providers: providers,
242
251
  objectPreview: config.search.objectPreview ?? false,
243
252
  layerPreview: config.search.layerPreview ?? false,
244
253
  minResolution: config.search.minResolution ?? 0.5,
245
- defaultFillColor: config.search?.defaultFillColor ?? '#3388ff7f',
246
- defaultStrokeColor: config.search?.defaultStrokeColor ?? '#3388ff',
247
- defaultStrokeWidth: config.search?.defaultStrokeWidth ?? 2,
248
- paintSearchResults: config.search?.paintSearchResults ?? true
254
+ defaultFillColor: config.search.defaultFillColor ?? '#3388ff7f',
255
+ defaultStrokeColor: config.search.defaultStrokeColor ?? '#3388ff',
256
+ defaultStrokeWidth: config.search.defaultStrokeWidth ?? 2,
257
+ paintSearchResults: config.search.paintSearchResults ?? true
249
258
  };
250
259
  }
251
260
  initConfigTreeview(config) {
@@ -271,18 +280,14 @@ class GirafeConfig {
271
280
  return config.lidar;
272
281
  }
273
282
  initConfigNews(config) {
274
- if (config.news?.sanitizeConfig) {
275
- return config.news;
283
+ if (!config.news) {
284
+ return undefined;
276
285
  }
277
- else if (config.news) {
278
- return {
279
- ...config.news,
280
- sanitizeConfig: defaultNewsSanitizeConfig
281
- };
286
+ if (config.news.sanitizeConfig) {
287
+ return config.news;
282
288
  }
283
289
  return {
284
- urls: [],
285
- autoDisplay: false,
290
+ ...config.news,
286
291
  sanitizeConfig: defaultNewsSanitizeConfig
287
292
  };
288
293
  }
@@ -639,7 +639,7 @@ export default class OlDrawing {
639
639
  const olFeature = this.getOlFeatureFromDrawingSource(drawingFeature.id);
640
640
  const extent = olFeature?.getGeometry()?.getExtent();
641
641
  if (extent) {
642
- const minResolution = this.config.search.minResolution;
642
+ const minResolution = this.config.search?.minResolution ?? 0.5;
643
643
  this.context.mapManager.zoomToExtent(extent, minResolution);
644
644
  }
645
645
  }
@@ -18,7 +18,7 @@ class SearchManager extends GirafeSingleton {
18
18
  }
19
19
  initializeSingleton() {
20
20
  super.initializeSingleton();
21
- for (const providerConfig of this.context.configManager.Config.search.providers ?? []) {
21
+ for (const providerConfig of this.context.configManager.Config.search?.providers ?? []) {
22
22
  this.createClient(providerConfig);
23
23
  }
24
24
  this.defaultSrid = this.context.configManager.getDefaultConfigValue('map.srid');