forked from MapComplete/MapComplete
		
	Better validated text fields with translations and human-friendly texts: first draft
This commit is contained in:
		
							parent
							
								
									695a0867c7
								
							
						
					
					
						commit
						9ab4fbd6f5
					
				
					 7 changed files with 581 additions and 528 deletions
				
			
		| 
						 | 
					@ -1,6 +0,0 @@
 | 
				
			||||||
import CompiledTranslations from "./assets/generated/CompiledTranslations";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default class AllTranslationAssets {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static t = CompiledTranslations.t;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -93,10 +93,22 @@ export default class TagRenderingConfig {
 | 
				
			||||||
            if (json.freeform.addExtraTags !== undefined && json.freeform.addExtraTags.map === undefined) {
 | 
					            if (json.freeform.addExtraTags !== undefined && json.freeform.addExtraTags.map === undefined) {
 | 
				
			||||||
                throw `Freeform.addExtraTags should be a list of strings - not a single string (at ${context})`
 | 
					                throw `Freeform.addExtraTags should be a list of strings - not a single string (at ${context})`
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					           const type = json.freeform.type ?? "string"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let placeholder = Translations.T(json.freeform.placeholder)
 | 
				
			||||||
 | 
					            if (placeholder === undefined) {
 | 
				
			||||||
 | 
					                const typeDescription = Translations.t.validation[type]?.description
 | 
				
			||||||
 | 
					                placeholder = Translations.T(json.freeform.key+" ("+type+")")
 | 
				
			||||||
 | 
					                if(typeDescription !== undefined){
 | 
				
			||||||
 | 
					                    console.log(typeDescription)
 | 
				
			||||||
 | 
					                    placeholder = placeholder.Fuse(typeDescription, type)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.freeform = {
 | 
					            this.freeform = {
 | 
				
			||||||
                key: json.freeform.key,
 | 
					                key: json.freeform.key,
 | 
				
			||||||
                type: json.freeform.type ?? "string",
 | 
					                type,
 | 
				
			||||||
                placeholder: Translations.T(json.freeform.placeholder ?? (json.freeform.key + "(" + (json.freeform.type ?? "string") + ")")),
 | 
					                placeholder,
 | 
				
			||||||
                addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
 | 
					                addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
 | 
				
			||||||
                    TagUtils.Tag(tg, `${context}.extratag[${i}]`)) ?? [],
 | 
					                    TagUtils.Tag(tg, `${context}.extratag[${i}]`)) ?? [],
 | 
				
			||||||
                inline: json.freeform.inline ?? false,
 | 
					                inline: json.freeform.inline ?? false,
 | 
				
			||||||
| 
						 | 
					@ -169,7 +181,7 @@ export default class TagRenderingConfig {
 | 
				
			||||||
                    hideInAnswer = TagUtils.Tag(mapping.hideInAnswer, `${context}.mapping[${i}].hideInAnswer`);
 | 
					                    hideInAnswer = TagUtils.Tag(mapping.hideInAnswer, `${context}.mapping[${i}].hideInAnswer`);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                let icon = undefined;
 | 
					                let icon = undefined;
 | 
				
			||||||
                if(mapping.icon !== ""){
 | 
					                if (mapping.icon !== "") {
 | 
				
			||||||
                    icon = mapping.icon
 | 
					                    icon = mapping.icon
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const mp = {
 | 
					                const mp = {
 | 
				
			||||||
| 
						 | 
					@ -339,7 +351,7 @@ export default class TagRenderingConfig {
 | 
				
			||||||
     * @param tags
 | 
					     * @param tags
 | 
				
			||||||
     * @constructor
 | 
					     * @constructor
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public GetRenderValues(tags: any): {then: Translation, icon?: string}[] {
 | 
					    public GetRenderValues(tags: any): { then: Translation, icon?: string }[] {
 | 
				
			||||||
        if (!this.multiAnswer) {
 | 
					        if (!this.multiAnswer) {
 | 
				
			||||||
            return [this.GetRenderValueWithImage(tags)]
 | 
					            return [this.GetRenderValueWithImage(tags)]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -348,7 +360,7 @@ export default class TagRenderingConfig {
 | 
				
			||||||
        // If it is undefined, it is "used" already, or at least we don't have to check for it anymore
 | 
					        // If it is undefined, it is "used" already, or at least we don't have to check for it anymore
 | 
				
			||||||
        let freeformKeyUsed = this.freeform?.key === undefined;
 | 
					        let freeformKeyUsed = this.freeform?.key === undefined;
 | 
				
			||||||
        // We run over all the mappings first, to check if the mapping matches
 | 
					        // We run over all the mappings first, to check if the mapping matches
 | 
				
			||||||
        const applicableMappings: {then: Translation, img?: string}[] = Utils.NoNull((this.mappings ?? [])?.map(mapping => {
 | 
					        const applicableMappings: { then: Translation, img?: string }[] = Utils.NoNull((this.mappings ?? [])?.map(mapping => {
 | 
				
			||||||
            if (mapping.if === undefined) {
 | 
					            if (mapping.if === undefined) {
 | 
				
			||||||
                return mapping;
 | 
					                return mapping;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -375,6 +387,7 @@ export default class TagRenderingConfig {
 | 
				
			||||||
    public GetRenderValue(tags: any, defltValue: any = undefined): Translation {
 | 
					    public GetRenderValue(tags: any, defltValue: any = undefined): Translation {
 | 
				
			||||||
        return this.GetRenderValueWithImage(tags, defltValue).then
 | 
					        return this.GetRenderValueWithImage(tags, defltValue).then
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Gets the correct rendering value (or undefined if not known)
 | 
					     * Gets the correct rendering value (or undefined if not known)
 | 
				
			||||||
     * Not compatible with multiAnswer - use GetRenderValueS instead in that case
 | 
					     * Not compatible with multiAnswer - use GetRenderValueS instead in that case
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ import Table from "../Base/Table";
 | 
				
			||||||
import Combine from "../Base/Combine";
 | 
					import Combine from "../Base/Combine";
 | 
				
			||||||
import Title from "../Base/Title";
 | 
					import Title from "../Base/Title";
 | 
				
			||||||
import InputElementMap from "./InputElementMap";
 | 
					import InputElementMap from "./InputElementMap";
 | 
				
			||||||
 | 
					import Translations from "../i18n/Translations";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TextFieldDef {
 | 
					interface TextFieldDef {
 | 
				
			||||||
    name: string,
 | 
					    name: string,
 | 
				
			||||||
| 
						 | 
					@ -84,7 +85,7 @@ class WikidataTextField implements TextFieldDef {
 | 
				
			||||||
        ]).AsMarkdown()
 | 
					        ]).AsMarkdown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public isValid(str) {
 | 
					    public isValid(str) : boolean{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (str === undefined) {
 | 
					        if (str === undefined) {
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
| 
						 | 
					@ -490,6 +491,7 @@ export default class ValidatedTextField {
 | 
				
			||||||
     * {string (typename) --> TextFieldDef}
 | 
					     * {string (typename) --> TextFieldDef}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static AllTypes: Map<string, TextFieldDef> = ValidatedTextField.allTypesDict();
 | 
					    public static AllTypes: Map<string, TextFieldDef> = ValidatedTextField.allTypesDict();
 | 
				
			||||||
 | 
					    private static Tranlations: string | BaseUIElement;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static InputForType(type: string, options?: {
 | 
					    public static InputForType(type: string, options?: {
 | 
				
			||||||
        placeholder?: string | BaseUIElement,
 | 
					        placeholder?: string | BaseUIElement,
 | 
				
			||||||
| 
						 | 
					@ -508,7 +510,9 @@ export default class ValidatedTextField {
 | 
				
			||||||
        inputStyle?: string
 | 
					        inputStyle?: string
 | 
				
			||||||
    }): InputElement<string> {
 | 
					    }): InputElement<string> {
 | 
				
			||||||
        options = options ?? {};
 | 
					        options = options ?? {};
 | 
				
			||||||
        options.placeholder = options.placeholder ?? type;
 | 
					        if(options.placeholder === undefined) {
 | 
				
			||||||
 | 
					            options.placeholder = Translations.t.validation[type]?.description  ?? type
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        const tp: TextFieldDef = ValidatedTextField.AllTypes.get(type)
 | 
					        const tp: TextFieldDef = ValidatedTextField.AllTypes.get(type)
 | 
				
			||||||
        const isValidTp = tp.isValid;
 | 
					        const isValidTp = tp.isValid;
 | 
				
			||||||
        let isValid;
 | 
					        let isValid;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,12 @@
 | 
				
			||||||
import {FixedUiElement} from "../Base/FixedUiElement";
 | 
					import {FixedUiElement} from "../Base/FixedUiElement";
 | 
				
			||||||
import AllTranslationAssets from "../../AllTranslationAssets";
 | 
					 | 
				
			||||||
import {Translation} from "./Translation";
 | 
					import {Translation} from "./Translation";
 | 
				
			||||||
import BaseUIElement from "../BaseUIElement";
 | 
					import BaseUIElement from "../BaseUIElement";
 | 
				
			||||||
import * as known_languages from "../../assets/generated/used_languages.json"
 | 
					import * as known_languages from "../../assets/generated/used_languages.json"
 | 
				
			||||||
 | 
					import CompiledTranslations from "../../assets/generated/CompiledTranslations";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Translations {
 | 
					export default class Translations {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static t = AllTranslationAssets.t;
 | 
					    static t = CompiledTranslations.t;
 | 
				
			||||||
    private static knownLanguages = new Set(known_languages.languages)
 | 
					    private static knownLanguages = new Set(known_languages.languages)
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        throw "Translations is static. If you want to intitialize a new translation, use the singular form"
 | 
					        throw "Translations is static. If you want to intitialize a new translation, use the singular form"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -517,5 +517,16 @@
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "importInspector": {
 | 
					  "importInspector": {
 | 
				
			||||||
    "title": "Inspect and manage import notes"
 | 
					    "title": "Inspect and manage import notes"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "validation": {
 | 
				
			||||||
 | 
					    "string": {
 | 
				
			||||||
 | 
					      "description": "a piece of text"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "pnat": {
 | 
				
			||||||
 | 
					      "description": "a positive number"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "nat": {
 | 
				
			||||||
 | 
					      "description": "a positive number or zero"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,8 @@ import ReplaceGeometrySpec from "./ReplaceGeometry.spec";
 | 
				
			||||||
import LegacyThemeLoaderSpec from "./LegacyThemeLoader.spec";
 | 
					import LegacyThemeLoaderSpec from "./LegacyThemeLoader.spec";
 | 
				
			||||||
import T from "./TestHelper";
 | 
					import T from "./TestHelper";
 | 
				
			||||||
import CreateNoteImportLayerSpec from "./CreateNoteImportLayer.spec";
 | 
					import CreateNoteImportLayerSpec from "./CreateNoteImportLayer.spec";
 | 
				
			||||||
 | 
					import ValidatedTextFieldTranslations from "./ValidatedTextFieldTranslations.spec";
 | 
				
			||||||
 | 
					import ValidatedTextFieldTranslationsSpec from "./ValidatedTextFieldTranslations.spec";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function main() {
 | 
					async function main() {
 | 
				
			||||||
| 
						 | 
					@ -38,7 +40,8 @@ async function main() {
 | 
				
			||||||
        new ActorsSpec(),
 | 
					        new ActorsSpec(),
 | 
				
			||||||
        new ReplaceGeometrySpec(),
 | 
					        new ReplaceGeometrySpec(),
 | 
				
			||||||
        new LegacyThemeLoaderSpec(),
 | 
					        new LegacyThemeLoaderSpec(),
 | 
				
			||||||
        new CreateNoteImportLayerSpec()
 | 
					        new CreateNoteImportLayerSpec(),
 | 
				
			||||||
 | 
					        new ValidatedTextFieldTranslationsSpec()
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Utils.externalDownloadFunction = async (url) => {
 | 
					    Utils.externalDownloadFunction = async (url) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										27
									
								
								test/ValidatedTextFieldTranslations.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								test/ValidatedTextFieldTranslations.spec.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					import T from "./TestHelper";
 | 
				
			||||||
 | 
					import ValidatedTextField from "../UI/Input/ValidatedTextField";
 | 
				
			||||||
 | 
					import Translations from "../UI/i18n/Translations";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class ValidatedTextFieldTranslationsSpec extends T {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        super([
 | 
				
			||||||
 | 
					            ["Test all", () => {
 | 
				
			||||||
 | 
					                const ts = Translations.t.validation;
 | 
				
			||||||
 | 
					                console.log("Hello world!")
 | 
				
			||||||
 | 
					                const allErrors = Array.from(ValidatedTextField.AllTypes.keys()).map(key => {
 | 
				
			||||||
 | 
					                    const errors = []
 | 
				
			||||||
 | 
					                    const t = ts[key]
 | 
				
			||||||
 | 
					                    if (t === undefined) {
 | 
				
			||||||
 | 
					                        errors.push("No tranlations at all for " + key)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return errors;
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                const errs = [].concat(...allErrors)
 | 
				
			||||||
 | 
					                if (errs.length > 0) {
 | 
				
			||||||
 | 
					                    errs.forEach(e => console.log(e))
 | 
				
			||||||
 | 
					                   // throw errs.join("\n")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }]
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue