diff --git a/src/Logic/Tags/TagTypes.ts b/src/Logic/Tags/TagTypes.ts index 9e19bc842..98730bb3b 100644 --- a/src/Logic/Tags/TagTypes.ts +++ b/src/Logic/Tags/TagTypes.ts @@ -13,7 +13,7 @@ type Brand = { [__is_optimized]: B } */ export type OptimizedTag = Brand -export type UploadableTag = Tag | SubstitutingTag | And +export type UploadableTag = Tag | SubstitutingTag | And /** * Not nested */ diff --git a/src/Logic/Tags/TagUtils.ts b/src/Logic/Tags/TagUtils.ts index 03f9a7f0a..55b142506 100644 --- a/src/Logic/Tags/TagUtils.ts +++ b/src/Logic/Tags/TagUtils.ts @@ -328,22 +328,6 @@ export class TagUtils { return keyValues } - /** - * Flattens an 'uploadableTag' and replaces all 'SubstitutingTags' into normal tags - */ - static FlattenAnd(tagFilters: UploadableTag, currentProperties: Record): Tag[] { - const tags: Tag[] = [] - tagFilters.visit((tf: UploadableTag) => { - if (tf instanceof Tag) { - tags.push(tf) - } - if (tf instanceof SubstitutingTag) { - tags.push(tf.asTag(currentProperties)) - } - }) - return tags - } - static optimzeJson(json: TagConfigJson): TagConfigJson | boolean { const optimized = TagUtils.Tag(json).optimize() if (optimized === true || optimized === false) { @@ -1024,4 +1008,14 @@ export class TagUtils { } return tag } + + static flattenAnd(tg: UploadableTag | UploadableTag[]): (SubstitutingTag | Tag)[] { + if (Array.isArray(tg)) { + return tg.flatMap(t => TagUtils.flattenAnd(t)) + } + if (tg["and"] || tg instanceof And) { + return tg["and"].flatMap(tg => TagUtils.flattenAnd(tg)) + } + return [tg] + } } diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index befa3ff70..29b342ed2 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -5,10 +5,7 @@ import { TagUtils } from "../../Logic/Tags/TagUtils" import { And } from "../../Logic/Tags/And" import { Utils } from "../../Utils" import { Tag } from "../../Logic/Tags/Tag" -import { - MappingConfigJson, - QuestionableTagRenderingConfigJson, -} from "./Json/QuestionableTagRenderingConfigJson" +import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRenderingConfigJson" import Validators, { ValidatorType } from "../../UI/InputElement/Validators" import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" import { RegexTag } from "../../Logic/Tags/RegexTag" @@ -23,6 +20,7 @@ import ComparingTag from "../../Logic/Tags/ComparingTag" import { Unit } from "../Unit" import { Lists } from "../../Utils/Lists" import { IsOnline } from "../../Logic/Web/IsOnline" +import SubstitutingTag from "../../Logic/Tags/SubstitutingTag" export interface Mapping { readonly if: UploadableTag @@ -803,7 +801,7 @@ export default class TagRenderingConfig { multiSelectedMapping: boolean[] | undefined, currentProperties: Record, unit?: Unit - ): UploadableTag[] { + ): (Tag | SubstitutingTag)[] { if (typeof freeformValue === "string") { freeformValue = freeformValue?.trim() } @@ -820,7 +818,8 @@ export default class TagRenderingConfig { valueNoUnit, () => currentProperties["_country"] ) - freeformValue = formatted + denom.canonical + // In general, we want a space between the amount and the unit + freeformValue = formatted + " " + denom.canonical } else { freeformValue = validator.reformat( freeformValue, @@ -865,7 +864,7 @@ export default class TagRenderingConfig { const freeformOnly = { [this.freeform.key]: freeformValue } const matchingMapping = this.mappings?.find((m) => m.if.matchesProperties(freeformOnly)) if (matchingMapping) { - return [matchingMapping.if, ...(matchingMapping.addExtraTags ?? [])] + return [...TagUtils.flattenAnd(matchingMapping.if), ...(matchingMapping.addExtraTags ?? [])] } // Either no mappings, or this is a radio-button selected freeform value const tag = [ @@ -877,7 +876,7 @@ export default class TagRenderingConfig { return undefined } - return tag + return TagUtils.flattenAnd(tag) } if (this.multiAnswer) { @@ -907,7 +906,7 @@ export default class TagRenderingConfig { ) { return undefined } - return and + return TagUtils.flattenAnd(and) } // Is at least one mapping shown in the answer? @@ -927,11 +926,11 @@ export default class TagRenderingConfig { if (useFreeform) { return [ new Tag(this.freeform.key, freeformValue), - ...(this.freeform.addExtraTags ?? []), + ...(TagUtils.flattenAnd(this.freeform.addExtraTags) ?? []) ] } else if (singleSelectedMapping !== undefined) { return [ - this.mappings[singleSelectedMapping].if, + ...TagUtils.flattenAnd(this.mappings[singleSelectedMapping].if), ...(this.mappings[singleSelectedMapping].addExtraTags ?? []), ] } else { @@ -1188,7 +1187,7 @@ export class TagRenderingConfigUtils { console.log("Could not download the NSI: ", extraMappingsErr["error"]) return config } - const extraMappings = extraMappingsErr?.success + const extraMappings = extraMappingsErr?.["success"] if(extraMappings === undefined){ if(!IsOnline.isOnline.data){ // The 'extraMappings' will still attempt to download the NSI - it might be in the service worker's cache diff --git a/test/integration/UnitHasSpace.spec.ts b/test/integration/UnitHasSpace.spec.ts new file mode 100644 index 000000000..91cb8b850 --- /dev/null +++ b/test/integration/UnitHasSpace.spec.ts @@ -0,0 +1,21 @@ +import { describe, it } from "vitest" +import LayerConfig from "../../src/Models/ThemeConfig/LayerConfig" +import maxspeed_layer from "../../public/assets/generated/layers/maxspeed.json" +import { LayerConfigJson } from "../../src/Models/ThemeConfig/Json/LayerConfigJson" +import { expect } from "chai" +import { Tag } from "../../src/Logic/Tags/Tag" + +describe("Integration_unit_has_space", () => { + it("maxspeed should produce '20 mph' and not '20mph'", () => { + const maxspeed = new LayerConfig( + maxspeed_layer, "integration_test" + ) + const maxspeedQuestion = maxspeed.tagRenderings.find(tr => tr.id === "maxspeed-maxspeed") + const unit = maxspeed.units.find(unit => unit.appliesToKeys.has("maxspeed")) + const spec = maxspeedQuestion.constructChangeSpecification("20", undefined, undefined, { + "_country": "gb" + }, unit).find(t => t["key"] === "maxspeed") + expect(spec.asJson()).to.eq(new Tag("maxspeed", "20 mph").asJson()) + }) + +})