From 44c1548e897f947f832e886d593da80abb082060 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 21 Oct 2023 09:35:54 +0200 Subject: [PATCH] Studio: UX-improvements after user testing --- Docs/Schemas/LayerConfigJson.schema.json | 2 +- Docs/Schemas/LayerConfigJsonJSC.ts | 2 +- Docs/Schemas/LayoutConfigJson.schema.json | 4 ++-- Docs/Schemas/LayoutConfigJsonJSC.ts | 4 ++-- .../2023-10-17 User Test Studio Thibault.md | 4 +++- ...2023-10-17 User Test Studio bxl-forever.md | 22 +++++++++++++++++++ scripts/fixSchemas.ts | 1 + src/Logic/Tags/TagUtils.ts | 3 ++- .../ThemeConfig/Json/LayerConfigJson.ts | 1 + .../Helpers/SimpleTagInput.svelte | 21 +++++++++++++----- src/UI/InputElement/InputHelper.svelte | 2 +- src/UI/InputElement/ValidatedInput.svelte | 1 - src/UI/Studio/EditLayer.svelte | 8 +++++-- src/UI/Studio/SchemaBasedField.svelte | 22 ++++++++++--------- src/UI/Studio/TagInput/BasicTagInput.svelte | 11 ++++++++-- src/UI/Studio/configMeta.ts | 4 ++++ src/UI/StudioGUI.svelte | 18 ++++++++++----- src/assets/schemas/layerconfigmeta.json | 1 + src/assets/schemas/layoutconfigmeta.json | 4 +++- 19 files changed, 100 insertions(+), 35 deletions(-) create mode 100644 Docs/UserTests/2023-10-17 User Test Studio bxl-forever.md diff --git a/Docs/Schemas/LayerConfigJson.schema.json b/Docs/Schemas/LayerConfigJson.schema.json index 35f7f9d71..8562b0b69 100644 --- a/Docs/Schemas/LayerConfigJson.schema.json +++ b/Docs/Schemas/LayerConfigJson.schema.json @@ -210,7 +210,7 @@ ] }, "tags": { - "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag", + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", "type": "array", "items": { "type": "string" diff --git a/Docs/Schemas/LayerConfigJsonJSC.ts b/Docs/Schemas/LayerConfigJsonJSC.ts index a1b23fb7d..2605d5f18 100644 --- a/Docs/Schemas/LayerConfigJsonJSC.ts +++ b/Docs/Schemas/LayerConfigJsonJSC.ts @@ -210,7 +210,7 @@ export default { ] }, "tags": { - "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag", + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", "type": "array", "items": { "type": "string" diff --git a/Docs/Schemas/LayoutConfigJson.schema.json b/Docs/Schemas/LayoutConfigJson.schema.json index 8facfa3b9..566c15e65 100644 --- a/Docs/Schemas/LayoutConfigJson.schema.json +++ b/Docs/Schemas/LayoutConfigJson.schema.json @@ -1992,7 +1992,7 @@ ] }, "tags": { - "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag", + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", "type": "array", "items": { "type": "string" @@ -2392,7 +2392,7 @@ ] }, "tags": { - "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag", + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", "type": "array", "items": { "type": "string" diff --git a/Docs/Schemas/LayoutConfigJsonJSC.ts b/Docs/Schemas/LayoutConfigJsonJSC.ts index d9fdee4ce..65ef95b89 100644 --- a/Docs/Schemas/LayoutConfigJsonJSC.ts +++ b/Docs/Schemas/LayoutConfigJsonJSC.ts @@ -1970,7 +1970,7 @@ export default { ] }, "tags": { - "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag", + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", "type": "array", "items": { "type": "string" @@ -2369,7 +2369,7 @@ export default { ] }, "tags": { - "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag", + "description": "A single tag (encoded as key=value) out of all the tags to add onto the newly created point.\nNote that the icon in the UI will be chosen automatically based on the tags provided here.\n\nquestion: What tag should be added to the new object?\ntype: simple_tag\ntypeHelper: uploadableOnly", "type": "array", "items": { "type": "string" diff --git a/Docs/UserTests/2023-10-17 User Test Studio Thibault.md b/Docs/UserTests/2023-10-17 User Test Studio Thibault.md index 34d220d0e..af8792e16 100644 --- a/Docs/UserTests/2023-10-17 User Test Studio Thibault.md +++ b/Docs/UserTests/2023-10-17 User Test Studio Thibault.md @@ -4,6 +4,8 @@ Create a simple layer specification using MapComplete studio with 'images' and a question. The actual _topic_ of the layer can be chosen by the participant +This participant wanted to create a layer about solar panels. + ## Background info Browser: Librewolf on a linux machine (actually: pietervdvn's dev machine) @@ -30,4 +32,4 @@ As such, many terms and the general structure of Studio were intuitively clear. ## Other misc issues -- The crosshair might be invisible if the aerial imagery is quite dark (fixed in 9dc222be433512d4d1ca530c1d09e28442d976ec) +- [x] The crosshair might be invisible if the aerial imagery is quite dark (fixed in 9dc222be433512d4d1ca530c1d09e28442d976ec) diff --git a/Docs/UserTests/2023-10-17 User Test Studio bxl-forever.md b/Docs/UserTests/2023-10-17 User Test Studio bxl-forever.md new file mode 100644 index 000000000..0c7d5822f --- /dev/null +++ b/Docs/UserTests/2023-10-17 User Test Studio bxl-forever.md @@ -0,0 +1,22 @@ +# User Test of the Studio + +## Task + +Create a simple layer specification using MapComplete studio with 'images' and a question. The actual _topic_ of the layer can be chosen by the participant + +This participant wanted to create a layer to park escooters + +## Background info + +Browser: Participants machine, browser unknown (but no browser-specific bugs were encountered) +Testurl: hosted.Mapcomplete.org/studio.html +The participant has extensive OpenStreetMap-knowledge but only used MapComplete a few times, long ago. + +## Surfaced issues + +- [x] In presets, all options can be chosen (e.g. regex, '<', ...). However, these should be uploadable tags +- [x] The 'try it out'-button should be a 'next'-button +- [x] Entering an incorrect ID and pressing enter still takes you to the layer editor with an incorrect ID +- [x] A name and description are obligatory to use the layer as single-layer-theme; but those error messages are unclear. +- [ ] +- [ ] diff --git a/scripts/fixSchemas.ts b/scripts/fixSchemas.ts index 5cb53d876..6da696ca1 100644 --- a/scripts/fixSchemas.ts +++ b/scripts/fixSchemas.ts @@ -9,6 +9,7 @@ import { AllSharedLayers } from "../src/Customizations/AllSharedLayers" const metainfo = { type: "One of the inputValidator types", + typeHelper: "Helper arguments for the type input, comma-separated. Same as 'args'", types: "Is multiple types are allowed for this field, then first show a mapping to pick the appropriate subtype. `Types` should be `;`-separated and contain precisely the same amount of subtypes", typesdefault: "Works in conjuction with `types`: this type will be selected by default", group: "A kind of label. Items with the same group name will be placed in the same region", diff --git a/src/Logic/Tags/TagUtils.ts b/src/Logic/Tags/TagUtils.ts index 5c73bd60c..dc35a2573 100644 --- a/src/Logic/Tags/TagUtils.ts +++ b/src/Logic/Tags/TagUtils.ts @@ -97,8 +97,9 @@ export class TagUtils { docs: "Both the `key` and `value` part of this specification are interpreted as regexes, both the key and value musth completely match their respective regexes", }, ":=": { - name: "Substitute `{some_key}` should match `key`", + name: "Substitute `... {some_key} ...` and match `key`", overpassSupport: false, + uploadable: true, docs: "**This is an advanced feature - use with caution**\n" + "\n" + diff --git a/src/Models/ThemeConfig/Json/LayerConfigJson.ts b/src/Models/ThemeConfig/Json/LayerConfigJson.ts index 40a3a2df4..422530cfe 100644 --- a/src/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/src/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -319,6 +319,7 @@ export interface LayerConfigJson { * * question: What tag should be added to the new object? * type: simple_tag + * typeHelper: uploadableOnly */ tags: string[] /** diff --git a/src/UI/InputElement/Helpers/SimpleTagInput.svelte b/src/UI/InputElement/Helpers/SimpleTagInput.svelte index 4e2747876..56ce35c86 100644 --- a/src/UI/InputElement/Helpers/SimpleTagInput.svelte +++ b/src/UI/InputElement/Helpers/SimpleTagInput.svelte @@ -3,18 +3,29 @@ */ import { UIEventSource } from "../../../Logic/UIEventSource"; import BasicTagInput from "../../Studio/TagInput/BasicTagInput.svelte"; - +import { TagUtils } from "../../../Logic/Tags/TagUtils"; +import * as nmd from "nano-markdown" +import FromHtml from "../../Base/FromHtml.svelte"; export let value: UIEventSource; -export let uploadableOnly: boolean; +export let args: string[] = []; +let uploadableOnly: boolean = args[0] === "uploadableOnly"; export let overpassSupportNeeded: boolean; /** * Only show the taginfo-statistics if they are suspicious (thus: less then 250 entries) */ export let silent: boolean = false; - - +let mode: string = "="; +let dropdownFocussed = new UIEventSource(false); +let documentation = TagUtils.modeDocumentation[mode]; +$: documentation = TagUtils.modeDocumentation[mode]; - + +{#if $dropdownFocussed} +
+ {documentation.name} + +
+{/if} diff --git a/src/UI/InputElement/InputHelper.svelte b/src/UI/InputElement/InputHelper.svelte index ed5776625..7386af12d 100644 --- a/src/UI/InputElement/InputHelper.svelte +++ b/src/UI/InputElement/InputHelper.svelte @@ -46,7 +46,7 @@ {:else if type === "tag"} {:else if type === "simple_tag"} - + {:else if type === "opening_hours"} {:else if type === "wikidata"} diff --git a/src/UI/InputElement/ValidatedInput.svelte b/src/UI/InputElement/ValidatedInput.svelte index dbee242cc..41f0d4961 100644 --- a/src/UI/InputElement/ValidatedInput.svelte +++ b/src/UI/InputElement/ValidatedInput.svelte @@ -17,7 +17,6 @@ export let getCountry: () => string | undefined export let placeholder: string | Translation | undefined export let unit: Unit = undefined - export let value: UIEventSource /** * Internal state bound to the input element. diff --git a/src/UI/Studio/EditLayer.svelte b/src/UI/Studio/EditLayer.svelte index 17c602e74..e14c0b3a1 100644 --- a/src/UI/Studio/EditLayer.svelte +++ b/src/UI/Studio/EditLayer.svelte @@ -10,6 +10,7 @@ import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"; import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion"; import ErrorIndicatorForRegion from "./ErrorIndicatorForRegion.svelte"; + import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid"; const layerSchema: ConfigMeta[] = layerSchemaRaw; @@ -57,12 +58,15 @@
+

Editing layer {$title}

{#if $hasErrors > 0}
{$hasErrors} errors detected
{:else} - Try it - out + + Try it out + + {/if}
diff --git a/src/UI/Studio/SchemaBasedField.svelte b/src/UI/Studio/SchemaBasedField.svelte index 371db7e2d..d93a6b906 100644 --- a/src/UI/Studio/SchemaBasedField.svelte +++ b/src/UI/Studio/SchemaBasedField.svelte @@ -20,17 +20,17 @@ const isTranslation = schema.hints.typehint === "translation" || schema.hints.typehint === "rendered" || ConfigMetaUtils.isTranslation(schema); let type = schema.hints.typehint ?? "string"; - - let rendervalue = schema.type === "boolean" ? undefined : ((schema.hints.inline ?? schema.path.join(".")) + " {translated(value)}") - let helperArgs = undefined - let inline = schema.hints.inline !== undefined + + let rendervalue = schema.type === "boolean" ? undefined : ((schema.hints.inline ?? schema.path.join(".")) + " {translated(value)}"); + let helperArgs = schema.hints.typehelper?.split(","); + let inline = schema.hints.inline !== undefined; if (isTranslation) { type = "translation"; - if(schema.hints.inline){ - const inlineValue = schema.hints.inline - rendervalue = inlineValue - inline = false - helperArgs = [inlineValue.substring(0, inlineValue.indexOf("{")), inlineValue.substring(inlineValue.indexOf("}") + 1)] + if (schema.hints.inline) { + const inlineValue = schema.hints.inline; + rendervalue = inlineValue; + inline = false; + helperArgs = [inlineValue.substring(0, inlineValue.indexOf("{")), inlineValue.substring(inlineValue.indexOf("}") + 1)]; } } if (type.endsWith("[]")) { @@ -164,6 +164,8 @@
{msg.message}
{/each} {/if} - {schema.path.join(".")} + {#if window.location.hostname === "127.0.0.1"} + {schema.path.join(".")} + {/if}
{/if} diff --git a/src/UI/Studio/TagInput/BasicTagInput.svelte b/src/UI/Studio/TagInput/BasicTagInput.svelte index bd67d9c63..28d26f565 100644 --- a/src/UI/Studio/TagInput/BasicTagInput.svelte +++ b/src/UI/Studio/TagInput/BasicTagInput.svelte @@ -11,9 +11,16 @@ export let tag: UIEventSource = new UIEventSource(undefined) export let uploadableOnly: boolean export let overpassSupportNeeded: boolean + + export let dropdownFocussed = new UIEventSource(false) + /** + * If set, do not show tagInfo if there are many features matching + */ export let silent : boolean = false + export let selected: UIEventSource = new UIEventSource(false) + let feedbackGlobal = tag.map(tag => { if (!tag) { return undefined @@ -38,7 +45,7 @@ let valueValue = new UIEventSource(undefined) - let mode: string = "=" + export let mode: string = "=" let modes: string[] = [] for (const k in TagUtils.modeDocumentation) { @@ -105,7 +112,7 @@ - dropdownFocussed.setData(true)} on:focusout={() => dropdownFocussed.setData(false)}> {#each modes as option}