16068: Merge branch 'main' of git.arvados.org:arvados-workbench2 into 16068
[arvados-workbench2.git] / src / views-components / resource-properties-form / property-key-field.tsx
index 2c047e5821a2ec06e919f9fa7e9db20a6bec92db..0be4527ac36d8370ee678b56710dab5d6dbbcb2b 100644 (file)
@@ -6,7 +6,14 @@ import React from 'react';
 import { WrappedFieldProps, Field, FormName, reset, change, WrappedFieldInputProps, WrappedFieldMetaProps } from 'redux-form';
 import { memoize } from 'lodash';
 import { Autocomplete } from 'components/autocomplete/autocomplete';
-import { Vocabulary, getTags, getTagKeyID } from 'models/vocabulary';
+import {
+    Vocabulary,
+    getTags,
+    getTagKeyID,
+    getTagKeyLabel,
+    getPreferredTags,
+    PropFieldSuggestion
+} from 'models/vocabulary';
 import {
     handleSelect,
     handleBlur,
@@ -16,7 +23,7 @@ import {
     buildProps
 } from 'views-components/resource-properties-form/property-field-common';
 import { TAG_KEY_VALIDATION } from 'validators/validators';
-import { escapeRegExp } from 'common/regexp.ts';
+import { escapeRegExp } from 'common/regexp';
 import { ChangeEvent } from 'react';
 
 export const PROPERTY_KEY_FIELD_NAME = 'key';
@@ -36,15 +43,27 @@ export const PropertyKeyField = connectVocabulary(
 const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & VocabularyProp) =>
     <FormName children={data => (
         <Autocomplete
+            {...buildProps(props)}
             label='Key'
             suggestions={getSuggestions(props.input.value, vocabulary)}
+            renderSuggestion={
+                (s: PropFieldSuggestion) => s.synonyms && s.synonyms.length > 0
+                    ? `${s.label} (${s.synonyms.join('; ')})`
+                    : s.label
+            }
             onSelect={handleSelect(PROPERTY_KEY_FIELD_ID, data.form, props.input, props.meta)}
-            onBlur={handleBlur(PROPERTY_KEY_FIELD_ID, data.form, props.meta, props.input, getTagKeyID(props.input.value, vocabulary))}
+            onBlur={() => {
+                // Case-insensitive search for the key in the vocabulary
+                const foundKeyID = getTagKeyID(props.input.value, vocabulary);
+                if (foundKeyID !== '') {
+                    props.input.value = getTagKeyLabel(foundKeyID, vocabulary);
+                }
+                handleBlur(PROPERTY_KEY_FIELD_ID, data.form, props.meta, props.input, foundKeyID)();
+            }}
             onChange={(e: ChangeEvent<HTMLInputElement>) => {
                 const newValue = e.currentTarget.value;
                 handleChange(data.form, props.input, props.meta, newValue);
             }}
-            {...buildProps(props)}
         />
     )} />;
 
@@ -60,9 +79,11 @@ const matchTags = (vocabulary: Vocabulary) =>
             ? undefined
             : 'Incorrect key';
 
-const getSuggestions = (value: string, vocabulary: Vocabulary) => {
+const getSuggestions = (value: string, vocabulary: Vocabulary): PropFieldSuggestion[] => {
     const re = new RegExp(escapeRegExp(value), "i");
-    return getTags(vocabulary).filter(tag => re.test(tag.label) && tag.label !== value);
+    return getPreferredTags(vocabulary, value).filter(
+        tag => (tag.label !== value && re.test(tag.label)) ||
+            (tag.synonyms && tag.synonyms.some(s => re.test(s))));
 };
 
 const handleChange = (