Merge branch 'master' into 15067-tag-editing-by-ids
[arvados-workbench2.git] / src / views-components / resource-properties-form / property-value-field.tsx
index c8634acf4e386c071f684064197a53521e389c7f..959b98ec87280ebc4d7bd496729ae051000ccdb5 100644 (file)
@@ -3,13 +3,14 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { WrappedFieldProps, Field, formValues } from 'redux-form';
+import { change, WrappedFieldProps, WrappedFieldMetaProps, WrappedFieldInputProps, Field, formValues } from 'redux-form';
 import { compose } from 'redux';
 import { Autocomplete } from '~/components/autocomplete/autocomplete';
-import { Vocabulary } from '~/models/vocabulary';
-import { PROPERTY_KEY_FIELD_NAME } from '~/views-components/resource-properties-form/property-key-field';
+import { Vocabulary, getTagValueID, isStrictTag, getTagValues, PropFieldSuggestion } from '~/models/vocabulary';
+import { PROPERTY_KEY_FIELD_ID } from '~/views-components/resource-properties-form/property-key-field';
 import { VocabularyProp, connectVocabulary, buildProps } from '~/views-components/resource-properties-form/property-field-common';
 import { TAG_VALUE_VALIDATION } from '~/validators/validators';
+import { COLLECTION_TAG_FORM_NAME } from '~/store/collection-panel/collection-panel-action';
 import { escapeRegExp } from '~/common/regexp.ts';
 
 interface PropertyKeyProp {
@@ -19,23 +20,33 @@ interface PropertyKeyProp {
 export type PropertyValueFieldProps = VocabularyProp & PropertyKeyProp;
 
 export const PROPERTY_VALUE_FIELD_NAME = 'value';
+export const PROPERTY_VALUE_FIELD_ID = 'valueID';
 
 export const PropertyValueField = compose(
     connectVocabulary,
-    formValues({ propertyKey: PROPERTY_KEY_FIELD_NAME })
+    formValues({ propertyKey: PROPERTY_KEY_FIELD_ID })
 )(
     (props: PropertyValueFieldProps) =>
-        <Field
-            name={PROPERTY_VALUE_FIELD_NAME}
-            component={PropertyValueInput}
-            validate={getValidation(props)}
-            {...props} />);
+        <div>
+            <Field
+                name={PROPERTY_VALUE_FIELD_NAME}
+                component={PropertyValueInput}
+                validate={getValidation(props)}
+                {...props} />
+            <Field
+                name={PROPERTY_VALUE_FIELD_ID}
+                type='hidden'
+                component='input' />
+        </div>
+);
 
 export const PropertyValueInput = ({ vocabulary, propertyKey, ...props }: WrappedFieldProps & PropertyValueFieldProps) =>
     <Autocomplete
         label='Value'
         suggestions={getSuggestions(props.input.value, propertyKey, vocabulary)}
+        onSelect={handleSelect(props.input, props.meta)}
         {...buildProps(props)}
+        onBlur={handleBlur(props.meta, props.input, vocabulary, propertyKey)}
     />;
 
 const getValidation = (props: PropertyValueFieldProps) =>
@@ -45,21 +56,33 @@ const getValidation = (props: PropertyValueFieldProps) =>
 
 const matchTagValues = ({ vocabulary, propertyKey }: PropertyValueFieldProps) =>
     (value: string) =>
-        getTagValues(propertyKey, vocabulary).find(v => v.includes(value))
+        getTagValues(propertyKey, vocabulary).find(v => v.label === value)
             ? undefined
             : 'Incorrect value';
 
 const getSuggestions = (value: string, tagName: string, vocabulary: Vocabulary) => {
     const re = new RegExp(escapeRegExp(value), "i");
-    return getTagValues(tagName, vocabulary).filter(v => re.test(v) && v !== value);
+    return getTagValues(tagName, vocabulary).filter(v => re.test(v.label) && v.label !== value);
 };
 
-const isStrictTag = (tagName: string, vocabulary: Vocabulary) => {
-    const tag = vocabulary.tags[tagName];
-    return tag ? tag.strict : false;
-};
+// Attempts to match a manually typed value label with a value ID, when the user
+// doesn't select the value from the suggestions list.
+const handleBlur = (
+    { dispatch }: WrappedFieldMetaProps,
+    { onBlur, value }: WrappedFieldInputProps,
+    vocabulary: Vocabulary,
+    tagKeyID: string) =>
+        () => {
+            dispatch(change(COLLECTION_TAG_FORM_NAME, PROPERTY_VALUE_FIELD_ID, getTagValueID(tagKeyID, value, vocabulary)));
+            onBlur(value);
+        };
 
-const getTagValues = (tagName: string, vocabulary: Vocabulary) => {
-    const tag = vocabulary.tags[tagName];
-    return tag && tag.values ? tag.values : [];
+// When selecting a property value, save its ID for later usage.
+const handleSelect = (
+    { onChange }: WrappedFieldInputProps,
+    { dispatch }: WrappedFieldMetaProps) => {
+        return (item:PropFieldSuggestion) => {
+            onChange(item.label);
+            dispatch(change(COLLECTION_TAG_FORM_NAME, PROPERTY_VALUE_FIELD_ID, item.id));
+    };
 };