15067: Tag key/value suggestions show all available labels.
authorLucas Di Pentima <lucas@di-pentima.com.ar>
Tue, 12 Nov 2019 12:31:59 +0000 (09:31 -0300)
committerLucas Di Pentima <lucas@di-pentima.com.ar>
Tue, 12 Nov 2019 12:31:59 +0000 (09:31 -0300)
Also:
* Moved all vocabulary related functions on the model file.
* Return an alphabetic ordered list of suggestions.

Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima@veritasgenetics.com>

public/vocabulary-example.json
src/models/vocabulary.ts
src/views-components/resource-properties-form/property-field-common.tsx
src/views-components/resource-properties-form/property-key-field.tsx
src/views-components/resource-properties-form/property-value-field.tsx

index 4859b5d69141a8d006e56f6e7ce7ce0db03604e9..59d4de7aab3f9a69b1bac6af67225b01c107083e 100644 (file)
         "IDTAGANIMALS": {
             "strict": false,
             "labels": [
-                {"label": "Animal" }
+                {"label": "Animal" },
+                {"label": "Creature"}
             ],
             "values": {
                 "IDVALANIMALS1": {
                     "labels": [
-                        {"label": "Human"}
+                        {"label": "Human"},
+                        {"label": "Homo sapiens"}
                     ]
                 },
                 "IDVALANIMALS2": {
                     "labels": [
-                        {"label": "Dog"}
+                        {"label": "Dog"},
+                        {"label": "Canis lupus familiaris"}
                     ]
                 },
                 "IDVALANIMALS3": {
                     "labels": [
-                        {"label": "Elephant"}
+                        {"label": "Elephant"},
+                        {"label": "Loxodonta"}
                     ]
                 },
                 "IDVALANIMALS4": {
                     "labels": [
-                        {"label": "Eagle"}
+                        {"label": "Eagle"},
+                        {"label": "Haliaeetus leucocephalus"}
                     ]
                 }
             }
index 173c8371f3c778a2f42d8febf2685b513c676e49..158d8058899943d4b5447afc3bc8ab1d86bc21b3 100644 (file)
@@ -24,6 +24,11 @@ export interface Tag {
     values?: Record<string, TagValue>;
 }
 
+export interface PropFieldSuggestion {
+    "id": string;
+    "label": string;
+}
+
 const VOCABULARY_VALIDATORS = [
     isObject,
     has('strict_tags'),
@@ -31,4 +36,54 @@ const VOCABULARY_VALIDATORS = [
 ];
 
 export const isVocabulary = (value: any) =>
-    every(validator => validator(value), VOCABULARY_VALIDATORS);
\ No newline at end of file
+    every(validator => validator(value), VOCABULARY_VALIDATORS);
+
+export const isStrictTag = (tagKeyID: string, vocabulary: Vocabulary) => {
+    const tag = vocabulary.tags[tagKeyID];
+    return tag ? tag.strict : false;
+};
+
+export const getTagValueID = (tagKeyID:string, tagValueLabel:string, vocabulary: Vocabulary) =>
+    (tagKeyID && vocabulary.tags[tagKeyID] && vocabulary.tags[tagKeyID].values)
+    ? Object.keys(vocabulary.tags[tagKeyID].values!).find(
+        k => vocabulary.tags[tagKeyID].values![k].labels.find(
+            l => l.label === tagValueLabel) !== undefined) || ''
+    : '';
+
+const compare = (a: PropFieldSuggestion, b: PropFieldSuggestion) => {
+    if (a.label < b.label) {return -1;}
+    if (a.label > b.label) {return 1;}
+    return 0;
+};
+
+export const getTagValues = (tagKeyID: string, vocabulary: Vocabulary) => {
+    const tag = vocabulary.tags[tagKeyID];
+    const ret = tag && tag.values
+        ? Object.keys(tag.values).map(
+            tagValueID => tag.values![tagValueID].labels
+                ? tag.values![tagValueID].labels.map(
+                    lbl => Object.assign({}, {"id": tagValueID, "label": lbl.label}))
+                : [{"id": tagValueID, "label": tagValueID}])
+            .reduce((prev, curr) => [...prev, ...curr], [])
+            .sort(compare)
+        : [];
+    return ret;
+};
+
+export const getTags = ({ tags }: Vocabulary) => {
+    const ret = tags && Object.keys(tags)
+        ? Object.keys(tags).map(
+            tagID => tags[tagID].labels
+                ? tags[tagID].labels.map(
+                    lbl => Object.assign({}, {"id": tagID, "label": lbl.label}))
+                : [{"id": tagID, "label": tagID}])
+            .reduce((prev, curr) => [...prev, ...curr], [])
+            .sort(compare)
+        : [];
+    return ret;
+};
+
+export const getTagKeyID = (tagKeyLabel:string, vocabulary: Vocabulary) =>
+    Object.keys(vocabulary.tags).find(
+        k => vocabulary.tags[k].labels.find(
+            l => l.label === tagKeyLabel) !== undefined) || '';
index a0f5800f17e8952f4615890e24d91ae6f3351fae..a90ce923265b779758b64987c68dd3070009ff06 100644 (file)
@@ -4,7 +4,7 @@
 
 import { connect } from 'react-redux';
 import { WrappedFieldMetaProps, WrappedFieldInputProps, WrappedFieldProps } from 'redux-form';
-import { Vocabulary } from '~/models/vocabulary';
+import { Vocabulary, PropFieldSuggestion } from '~/models/vocabulary';
 import { RootState } from '~/store/store';
 import { getVocabulary } from '~/store/vocabulary/vocabulary-selectors';
 
@@ -43,8 +43,3 @@ export const buildProps = ({ input, meta }: WrappedFieldProps) => {
         helperText: getErrorMsg(meta),
     };
 };
-
-export interface PropFieldSuggestion {
-    "id": string;
-    "label": string;
-}
index 5c8c875b6fb54d2d6edf5c8f8ceecde36c6d25e6..ff665df231a96b81a9fb853deabb0d45ace4704b 100644 (file)
@@ -6,8 +6,8 @@ import * as React from 'react';
 import { change, WrappedFieldProps, WrappedFieldMetaProps, WrappedFieldInputProps, Field } from 'redux-form';
 import { memoize } from 'lodash';
 import { Autocomplete } from '~/components/autocomplete/autocomplete';
-import { Vocabulary } from '~/models/vocabulary';
-import { connectVocabulary, VocabularyProp, buildProps, PropFieldSuggestion } from '~/views-components/resource-properties-form/property-field-common';
+import { Vocabulary, getTags, getTagKeyID, PropFieldSuggestion } from '~/models/vocabulary';
+import { connectVocabulary, VocabularyProp, buildProps } from '~/views-components/resource-properties-form/property-field-common';
 import { TAG_KEY_VALIDATION } from '~/validators/validators';
 import { COLLECTION_TAG_FORM_NAME } from '~/store/collection-panel/collection-panel-action';
 
@@ -46,27 +46,12 @@ const getValidation = memoize(
 
 const matchTags = (vocabulary: Vocabulary) =>
     (value: string) =>
-        getTagsList(vocabulary).find(tag => tag.label === value)
+        getTags(vocabulary).find(tag => tag.label === value)
             ? undefined
             : 'Incorrect key';
 
 const getSuggestions = (value: string, vocabulary: Vocabulary) =>
-    getTagsList(vocabulary).filter(tag => tag.label.toLowerCase().includes(value.toLowerCase()));
-
-const getTagsList = ({ tags }: Vocabulary) => {
-    const ret = tags && Object.keys(tags)
-        ? Object.keys(tags).map(
-            tagID => tags[tagID].labels
-                ? {"id": tagID, "label": tags[tagID].labels[0].label}
-                : {"id": tagID, "label": tagID})
-        : [];
-    return ret;
-};
-
-const getTagKeyID = (tagKeyLabel:string, vocabulary: Vocabulary) =>
-    Object.keys(vocabulary.tags).find(
-        k => vocabulary.tags[k].labels.find(
-            l => l.label === tagKeyLabel) !== undefined) || '';
+    getTags(vocabulary).filter(tag => tag.label.toLowerCase().includes(value.toLowerCase()));
 
 // Attempts to match a manually typed key label with a key ID, when the user
 // doesn't select the key from the suggestions list.
index 910a095ad0b6f5d29a1b10cc1f9fdd4215f5b8ae..32109ef441b0b74d8fa50a7e3afd13055a4baeef 100644 (file)
@@ -6,9 +6,9 @@ import * as React from 'react';
 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 { 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, PropFieldSuggestion } from '~/views-components/resource-properties-form/property-field-common';
+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';
 
@@ -62,29 +62,6 @@ const matchTagValues = ({ vocabulary, propertyKey }: PropertyValueFieldProps) =>
 const getSuggestions = (value: string, tagKey: string, vocabulary: Vocabulary) =>
     getTagValues(tagKey, vocabulary).filter(v => v.label.toLowerCase().includes(value.toLowerCase()));
 
-const isStrictTag = (tagKey: string, vocabulary: Vocabulary) => {
-    const tag = vocabulary.tags[tagKey];
-    return tag ? tag.strict : false;
-};
-
-const getTagValues = (tagKey: string, vocabulary: Vocabulary) => {
-    const tag = vocabulary.tags[tagKey];
-    const ret = tag && tag.values
-        ? Object.keys(tag.values).map(
-            tagValueID => tag.values![tagValueID].labels
-                ? {"id": tagValueID, "label": tag.values![tagValueID].labels[0].label}
-                : {"id": tagValueID, "label": tagValueID})
-        : [];
-    return ret;
-};
-
-const getTagValueID = (tagKeyID:string, tagValueLabel:string, vocabulary: Vocabulary) =>
-    (tagKeyID && vocabulary.tags[tagKeyID] && vocabulary.tags[tagKeyID].values)
-    ? Object.keys(vocabulary.tags[tagKeyID].values!).find(
-        k => vocabulary.tags[tagKeyID].values![k].labels.find(
-            l => l.label === tagValueLabel) !== undefined) || ''
-    : '';
-
 // 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 = (
@@ -92,10 +69,10 @@ const handleBlur = (
     { onBlur, value }: WrappedFieldInputProps,
     vocabulary: Vocabulary,
     tagKeyID: string) =>
-    () => {
-        dispatch(change(COLLECTION_TAG_FORM_NAME, PROPERTY_VALUE_FIELD_ID, getTagValueID(tagKeyID, value, vocabulary)));
-        onBlur(value);
-    };
+        () => {
+            dispatch(change(COLLECTION_TAG_FORM_NAME, PROPERTY_VALUE_FIELD_ID, getTagValueID(tagKeyID, value, vocabulary)));
+            onBlur(value);
+        };
 
 // When selecting a property value, save its ID for later usage.
 const handleSelect = (