diff --git a/package-lock.json b/package-lock.json
index 664a4b720..2c65db7f6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,7 @@
"hasInstallScript": true,
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
- "@defra/forms-model": "^3.0.667",
+ "@defra/forms-model": "^3.0.668",
"@defra/hapi-tracing": "^1.29.0",
"@defra/interactive-map": "^0.0.22-alpha",
"@elastic/ecs-pino-format": "^1.5.0",
@@ -1315,9 +1315,9 @@
}
},
"node_modules/@babel/plugin-transform-modules-systemjs": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz",
- "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==",
+ "version": "7.29.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz",
+ "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==",
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.28.6",
@@ -3513,9 +3513,9 @@
}
},
"node_modules/@defra/forms-model": {
- "version": "3.0.667",
- "resolved": "https://registry.npmjs.org/@defra/forms-model/-/forms-model-3.0.667.tgz",
- "integrity": "sha512-NLAj/qblpSZy3wR0VOfM8QDWqNj7qnfiZwj01cmfJLrpaVzpHJTkjyxO3Ki4JI8xAtk3p3knBWFNv3ayED56QQ==",
+ "version": "3.0.668",
+ "resolved": "https://registry.npmjs.org/@defra/forms-model/-/forms-model-3.0.668.tgz",
+ "integrity": "sha512-H0FBwHZu+joIlBXdGOiqDguZMtVT8WDxIFUUw737wJNdFrqj0PxlpVQn/hPHc5Juvt0s19JUZhrNlI0sXoucZw==",
"license": "OGL-UK-3.0",
"dependencies": {
"@joi/date": "^2.1.1",
@@ -11694,9 +11694,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
- "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -17596,9 +17596,9 @@
}
},
"node_modules/eslint-plugin-import-x/node_modules/brace-expansion": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
- "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -18337,9 +18337,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
- "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
+ "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
"funding": [
{
"type": "github",
@@ -20397,9 +20397,9 @@
}
},
"node_modules/ip-address": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
- "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
+ "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
"license": "MIT",
"engines": {
"node": ">= 12"
@@ -34474,9 +34474,9 @@
}
},
"node_modules/webpack-dev-server": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz",
- "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==",
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.4.tgz",
+ "integrity": "sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA==",
"license": "MIT",
"dependencies": {
"@types/bonjour": "^3.5.13",
@@ -34974,9 +34974,9 @@
}
},
"node_modules/ws": {
- "version": "8.19.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
- "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
+ "version": "8.20.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz",
+ "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
diff --git a/package.json b/package.json
index 441a71b0f..42a2515be 100644
--- a/package.json
+++ b/package.json
@@ -87,7 +87,7 @@
},
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
- "@defra/forms-model": "^3.0.667",
+ "@defra/forms-model": "^3.0.668",
"@defra/hapi-tracing": "^1.29.0",
"@defra/interactive-map": "^0.0.22-alpha",
"@elastic/ecs-pino-format": "^1.5.0",
diff --git a/src/client/javascripts/geospatial-map.js b/src/client/javascripts/geospatial-map.js
index 01c805a84..aec354bd3 100644
--- a/src/client/javascripts/geospatial-map.js
+++ b/src/client/javascripts/geospatial-map.js
@@ -7,6 +7,7 @@ import {
getCentroidGridRef,
getCoordinateGridRef
} from '~/src/client/javascripts/map.js'
+import { formatDelimtedList } from '~/src/client/javascripts/utils.js'
const helpPanelConfig = {
showLabel: true,
@@ -28,8 +29,63 @@ const helpPanelConfig = {
open: true,
dismissible: true,
modal: false
- },
- html: '
You can add points, shapes or lines to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
- Double‑click, or select \'Done\', when you have finished drawing a line or shape
- Give the location a name
'
+ }
+}
+
+/**
+ * @param {boolean} allowLine
+ * @param {boolean} allowShape
+ */
+function getLineOrShapeText(allowLine, allowShape) {
+ if (allowLine && allowShape) {
+ return 'a line or shape'
+ }
+ if (allowLine) {
+ return 'a line'
+ }
+ if (allowShape) {
+ return 'a shape'
+ }
+ return ''
+}
+
+/**
+ * @param {boolean} allowPoint
+ * @param {boolean} allowLine
+ * @param {boolean} allowShape
+ */
+function getAllowedTypesPhrase(allowPoint, allowLine, allowShape) {
+ const items = []
+
+ if (allowPoint) {
+ items.push('points')
+ }
+ if (allowLine) {
+ items.push('lines')
+ }
+ if (allowShape) {
+ items.push('shapes')
+ }
+
+ return formatDelimtedList(items, ',', 'or')
+}
+
+/**
+ * @param {boolean} allowPoint
+ * @param {boolean} allowLine
+ * @param {boolean} allowShape
+ */
+export function getHelpPanelHtml(allowPoint, allowLine, allowShape) {
+ const lineOrShapeText = getLineOrShapeText(allowLine, allowShape)
+ const doneExtra = lineOrShapeText
+ ? `Double‑click, or select 'Done', when you have finished drawing ${lineOrShapeText}`
+ : ''
+ const allowedTypesText = getAllowedTypesPhrase(
+ allowPoint,
+ allowLine,
+ allowShape
+ )
+ return `You can add ${allowedTypesText} to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
${doneExtra}- Give the location a name
`
}
const lineFeatureProperties = {
@@ -157,7 +213,18 @@ export function processGeospatial(config, geospatial, index) {
const { map, interactPlugin } = createMap(mapId, initConfig, config)
const featuresManager = getFeaturesManager(geojson)
const activeFeatureManager = getActiveFeatureManager()
- const uiManager = getUIManager(geojson, map, mapId, listEl, geospatialInput)
+ const geometryTypes = geospatial.dataset.geometrytypes ?? 'point,line,shape'
+ const options = {
+ geometryTypes
+ }
+ const uiManager = getUIManager(
+ geojson,
+ map,
+ mapId,
+ listEl,
+ geospatialInput,
+ options
+ )
/**
* @type {Context}
@@ -492,16 +559,32 @@ function getValueRenderer(geojson, geospatialInput) {
* @param {string} mapId - the ID of the map
* @param {HTMLDivElement} listEl - where to render the feature list
* @param {HTMLTextAreaElement} geospatialInput - the geospatial textarea
+ * @param {UIManagerOptions} options - extra options such as allowable geometry types
*/
-function getUIManager(geojson, map, mapId, listEl, geospatialInput) {
+function getUIManager(geojson, map, mapId, listEl, geospatialInput, options) {
+ /**
+ * Get a CSV list of geometry types the user can create
+ * @returns {string[]}
+ */
+ function getAllowableGeometryTypes() {
+ return options.geometryTypes ? options.geometryTypes.split(',') : []
+ }
+
/**
* Toggle the hidden state of the action buttons
* @type {ToggleActionButtons}
*/
function toggleActionButtons(hidden) {
- map.toggleButtonState('btnAddPoint', 'hidden', hidden)
- map.toggleButtonState('btnAddPolygon', 'hidden', hidden)
- map.toggleButtonState('btnAddLine', 'hidden', hidden)
+ const types = getAllowableGeometryTypes()
+ if (types.includes('point')) {
+ map.toggleButtonState('btnAddPoint', 'hidden', hidden)
+ }
+ if (types.includes('shape')) {
+ map.toggleButtonState('btnAddPolygon', 'hidden', hidden)
+ }
+ if (types.includes('line')) {
+ map.toggleButtonState('btnAddLine', 'hidden', hidden)
+ }
}
/**
@@ -528,7 +611,8 @@ function getUIManager(geojson, map, mapId, listEl, geospatialInput) {
renderValue,
listEl,
toggleActionButtons,
- focusDescriptionInput
+ focusDescriptionInput,
+ getAllowableGeometryTypes
}
}
@@ -572,7 +656,8 @@ function createContainers(geospatialInput, index) {
function onMapReadyFactory(context) {
const { map, activeFeatureManager, uiManager, interactPlugin, drawPlugin } =
context
- const { toggleActionButtons, renderList } = uiManager
+ const { toggleActionButtons, renderList, getAllowableGeometryTypes } =
+ uiManager
const { resetActiveFeature } = activeFeatureManager
/**
@@ -581,53 +666,67 @@ function onMapReadyFactory(context) {
* @param {MapLibreMap} e.map - the map provider instance
*/
return function onMapReady(e) {
+ const types = getAllowableGeometryTypes()
+ const allowPoint = types.includes('point')
+ const allowLine = types.includes('line')
+ const allowShape = types.includes('shape')
+
// Add info panel
- map.addPanel('info', helpPanelConfig)
-
- map.addButton('btnAddPoint', {
- variant: 'tertiary',
- label: 'Add point',
- iconSvgContent: POINT_SVG,
- onClick: () => {
- resetActiveFeature()
- toggleActionButtons(true)
- renderList(true)
- interactPlugin.enable()
- },
- mobile: { slot: 'actions' },
- tablet: { slot: 'actions' },
- desktop: { slot: 'actions' }
+ map.addPanel('info', {
+ ...helpPanelConfig,
+ html: getHelpPanelHtml(allowPoint, allowLine, allowShape)
})
- map.addButton('btnAddPolygon', {
- variant: 'tertiary',
- label: 'Add shape',
- iconSvgContent: POLYGON_SVG,
- onClick: () => {
- resetActiveFeature()
- toggleActionButtons(true)
- renderList(true)
- drawPlugin.newPolygon(generateID(), polygonFeatureProperties)
- },
- mobile: { slot: 'actions' },
- tablet: { slot: 'actions' },
- desktop: { slot: 'actions' }
- })
+ if (allowPoint) {
+ map.addButton('btnAddPoint', {
+ variant: 'tertiary',
+ label: 'Add point',
+ iconSvgContent: POINT_SVG,
+ onClick: () => {
+ resetActiveFeature()
+ toggleActionButtons(true)
+ renderList(true)
+ interactPlugin.enable()
+ },
+ mobile: { slot: 'actions' },
+ tablet: { slot: 'actions' },
+ desktop: { slot: 'actions' }
+ })
+ }
- map.addButton('btnAddLine', {
- variant: 'tertiary',
- label: 'Add line',
- iconSvgContent: LINE_SVG,
- onClick: () => {
- resetActiveFeature()
- toggleActionButtons(true)
- renderList(true)
- drawPlugin.newLine(generateID(), lineFeatureProperties)
- },
- mobile: { slot: 'actions' },
- tablet: { slot: 'actions' },
- desktop: { slot: 'actions' }
- })
+ if (allowShape) {
+ map.addButton('btnAddPolygon', {
+ variant: 'tertiary',
+ label: 'Add shape',
+ iconSvgContent: POLYGON_SVG,
+ onClick: () => {
+ resetActiveFeature()
+ toggleActionButtons(true)
+ renderList(true)
+ drawPlugin.newPolygon(generateID(), polygonFeatureProperties)
+ },
+ mobile: { slot: 'actions' },
+ tablet: { slot: 'actions' },
+ desktop: { slot: 'actions' }
+ })
+ }
+
+ if (allowLine) {
+ map.addButton('btnAddLine', {
+ variant: 'tertiary',
+ label: 'Add line',
+ iconSvgContent: LINE_SVG,
+ onClick: () => {
+ resetActiveFeature()
+ toggleActionButtons(true)
+ renderList(true)
+ drawPlugin.newLine(generateID(), lineFeatureProperties)
+ },
+ mobile: { slot: 'actions' },
+ tablet: { slot: 'actions' },
+ desktop: { slot: 'actions' }
+ })
+ }
// Set the map provider on the context
context.mapProvider = e.map
@@ -1055,6 +1154,12 @@ function onListElKeydownFactory() {
* @returns {void}
*/
+/**
+ * Returns the list of geometry types a user can create
+ * @callback GetAllowableGeometryTypes
+ * @returns {string[]}
+ */
+
/**
* Set focus to the last description input
* @callback FocusDescriptionInput
@@ -1084,6 +1189,7 @@ function onListElKeydownFactory() {
* @property {HTMLDivElement} listEl - the summary list of features
* @property {ToggleActionButtons} toggleActionButtons - function that toggles the action buttons
* @property {FocusDescriptionInput} focusDescriptionInput - function that sets focus to a description input element
+ * @property {GetAllowableGeometryTypes} getAllowableGeometryTypes - function that returns the array of geometry types a user can create
*/
/**
@@ -1098,5 +1204,5 @@ function onListElKeydownFactory() {
*/
/**
- * @import { MapLibreMap } from '~/src/client/javascripts/map.js'
+ * @import { MapLibreMap, UIManagerOptions } from '~/src/client/javascripts/map.js'
*/
diff --git a/src/client/javascripts/map.js b/src/client/javascripts/map.js
index a6852c120..07f69a5d6 100644
--- a/src/client/javascripts/map.js
+++ b/src/client/javascripts/map.js
@@ -395,6 +395,11 @@ export function centerMap(map, mapProvider, center) {
* @property {TileData} data - the tile data config
*/
+/**
+ * @typedef {object} UIManagerOptions
+ * @property {string} [geometryTypes] - the CSV list of geometry types that a user can create
+ */
+
/**
* @import { Feature } from '~/src/server/plugins/engine/types.js'
*/
diff --git a/src/client/javascripts/utils.js b/src/client/javascripts/utils.js
new file mode 100644
index 000000000..d7f5314d3
--- /dev/null
+++ b/src/client/javascripts/utils.js
@@ -0,0 +1,23 @@
+/**
+ * Builds a text representation of a list in the form 'a, b, c, d or e'
+ * @param {string[]} items
+ * @param {string} separator
+ * @param {string} lastSpearator
+ */
+export function formatDelimtedList(items, separator, lastSpearator) {
+ if (items.length === 0) {
+ return ''
+ }
+
+ if (items.length === 1) {
+ return items[0]
+ }
+
+ if (items.length === 2) {
+ return `${items[0]} ${lastSpearator} ${items[1]}`
+ }
+
+ const last = items.pop()
+ const separatorAndSpace = `${separator} `
+ return `${items.join(separatorAndSpace)} ${lastSpearator} ${last}`
+}
diff --git a/src/server/forms/simple-form.yaml b/src/server/forms/simple-form.yaml
index efa8f8215..e43cabd66 100644
--- a/src/server/forms/simple-form.yaml
+++ b/src/server/forms/simple-form.yaml
@@ -25,6 +25,15 @@ pages:
required: true
schema: {}
id: b68df7f1-d4f4-4c17-83c8-402f584906c9
+ - type: GeospatialField
+ title: Where do you live?
+ name: applicantLocation
+ shortDescription: Your location
+ hint: ''
+ options:
+ required: true
+ schema: {}
+ id: e18116e0-7c3e-416a-af42-6f229017c5b1
next: []
id: 622a35ec-3795-418a-81f3-a45746959045
- title: Upload a copy of your passport
diff --git a/src/server/plugins/engine/components/GeospatialField.ts b/src/server/plugins/engine/components/GeospatialField.ts
index f629f5010..b7ea33415 100644
--- a/src/server/plugins/engine/components/GeospatialField.ts
+++ b/src/server/plugins/engine/components/GeospatialField.ts
@@ -96,6 +96,7 @@ export class GeospatialField extends FormComponent {
return {
...viewModel,
country: this.options.countries?.at(0),
+ geometryTypes: this.options.geometryTypes,
value
}
}
diff --git a/src/server/plugins/engine/views/components/geospatialfield.html b/src/server/plugins/engine/views/components/geospatialfield.html
index 6548fc57d..36915ec7e 100644
--- a/src/server/plugins/engine/views/components/geospatialfield.html
+++ b/src/server/plugins/engine/views/components/geospatialfield.html
@@ -1,7 +1,7 @@
{% from "govuk/components/textarea/macro.njk" import govukTextarea %}
{% macro GeospatialField(component) %}
-
+
{{ govukTextarea(component.model) }}
{% endmacro %}
diff --git a/test/client/javascripts/map.test.js b/test/client/javascripts/map.test.js
index 8b3d6b733..1e0c1f7d5 100644
--- a/test/client/javascripts/map.test.js
+++ b/test/client/javascripts/map.test.js
@@ -1,6 +1,7 @@
import {
createFeatureHTML,
- createFeaturesHTML
+ createFeaturesHTML,
+ getHelpPanelHtml
} from '~/src/client/javascripts/geospatial-map.js'
import {
formSubmitFactory,
@@ -289,6 +290,44 @@ describe('Maps Client JS', () => {
})
})
+ describe('getHelpPanelHtml', () => {
+ it('should handle only point', () => {
+ expect(getHelpPanelHtml(true, false, false)).toBe(
+ '
You can add points to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
- Give the location a name
'
+ )
+ })
+ it('should handle only line', () => {
+ expect(getHelpPanelHtml(false, true, false)).toBe(
+ '
You can add lines to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
- Double‑click, or select \'Done\', when you have finished drawing a line
- Give the location a name
'
+ )
+ })
+ it('should handle only shape', () => {
+ expect(getHelpPanelHtml(false, false, true)).toBe(
+ '
You can add shapes to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
- Double‑click, or select \'Done\', when you have finished drawing a shape
- Give the location a name
'
+ )
+ })
+ it('should handle point and line', () => {
+ expect(getHelpPanelHtml(true, true, false)).toBe(
+ '
You can add points or lines to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
- Double‑click, or select \'Done\', when you have finished drawing a line
- Give the location a name
'
+ )
+ })
+ it('should handle point and shape', () => {
+ expect(getHelpPanelHtml(true, false, true)).toBe(
+ '
You can add points or shapes to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
- Double‑click, or select \'Done\', when you have finished drawing a shape
- Give the location a name
'
+ )
+ })
+ it('should handle line and shape', () => {
+ expect(getHelpPanelHtml(false, true, true)).toBe(
+ '
You can add lines or shapes to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
- Double‑click, or select \'Done\', when you have finished drawing a line or shape
- Give the location a name
'
+ )
+ })
+ it('should handle point, line and shape', () => {
+ expect(getHelpPanelHtml(true, true, true)).toBe(
+ '
You can add points, lines or shapes to the map.
- Search for a county, place or postcode
- Use the + and - icons to zoom in and out
- Double‑click, or select \'Done\', when you have finished drawing a line or shape
- Give the location a name
'
+ )
+ })
+ })
+
describe('Easting northing component', () => {
beforeEach(() => {
document.body.innerHTML = `
diff --git a/test/client/javascripts/utils.test.js b/test/client/javascripts/utils.test.js
new file mode 100644
index 000000000..eeeaf71dc
--- /dev/null
+++ b/test/client/javascripts/utils.test.js
@@ -0,0 +1,25 @@
+import { formatDelimtedList } from '~/src/client/javascripts/utils.js'
+
+describe('utils', () => {
+ describe('formatDelimitedList', () => {
+ it('should handle empty list', () => {
+ expect(formatDelimtedList([], ',', 'or')).toBe('')
+ })
+
+ it('should handle one item', () => {
+ expect(formatDelimtedList(['item1'], ',', 'or')).toBe('item1')
+ })
+
+ it('should handle two items', () => {
+ expect(formatDelimtedList(['item1', 'item2'], ',', 'or')).toBe(
+ 'item1 or item2'
+ )
+ })
+
+ it('should handle three items', () => {
+ expect(formatDelimtedList(['item1', 'item2', 'item3'], ',', 'or')).toBe(
+ 'item1, item2 or item3'
+ )
+ })
+ })
+})