[ARVADOS-WORKBENCH2] created: 2.3.0-160-g1422847e

Git user git at public.arvados.org
Thu Feb 10 21:21:49 UTC 2022


        at  1422847e9da8c3d96cf777bac1742e300435657c (commit)


commit 1422847e9da8c3d96cf777bac1742e300435657c
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Thu Feb 10 18:18:48 2022 -0300

    18560: Improves the property form's autocomplete feature.
    
    Instead of presenting the whole list of terms and their synonymns to the user,
    only show the preferred term (the first defined label in the vocabulary) and
    if the user starts typing, also add the synonyms to every preferred term.
    This way the list gets a lot smalle and easier to browse.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/views-components/resource-properties-form/property-key-field.tsx b/src/views-components/resource-properties-form/property-key-field.tsx
index 791949f5..e566c708 100644
--- a/src/views-components/resource-properties-form/property-key-field.tsx
+++ b/src/views-components/resource-properties-form/property-key-field.tsx
@@ -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, getTagKeyLabel } from 'models/vocabulary';
+import {
+    Vocabulary,
+    getTags,
+    getTagKeyID,
+    getTagKeyLabel,
+    getPreferredTags,
+    PropFieldSuggestion
+} from 'models/vocabulary';
 import {
     handleSelect,
     handleBlur,
@@ -36,8 +43,10 @@ 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.description || s.label)}
             onSelect={handleSelect(PROPERTY_KEY_FIELD_ID, data.form, props.input, props.meta)}
             onBlur={() => {
                 // Case-insensitive search for the key in the vocabulary
@@ -51,7 +60,6 @@ const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & Vocabula
                 const newValue = e.currentTarget.value;
                 handleChange(data.form, props.input, props.meta, newValue);
             }}
-            {...buildProps(props)}
         />
     )} />;
 
@@ -67,9 +75,10 @@ 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 => re.test((tag.description || tag.label)) && tag.label !== value);
 };
 
 const handleChange = (
diff --git a/src/views-components/resource-properties-form/property-value-field.tsx b/src/views-components/resource-properties-form/property-value-field.tsx
index b023e412..56b7fe05 100644
--- a/src/views-components/resource-properties-form/property-value-field.tsx
+++ b/src/views-components/resource-properties-form/property-value-field.tsx
@@ -6,7 +6,7 @@ import React from 'react';
 import { WrappedFieldProps, Field, formValues, FormName, WrappedFieldInputProps, WrappedFieldMetaProps, change } from 'redux-form';
 import { compose } from 'redux';
 import { Autocomplete } from 'components/autocomplete/autocomplete';
-import { Vocabulary, isStrictTag, getTagValues, getTagValueID, getTagValueLabel } from 'models/vocabulary';
+import { Vocabulary, isStrictTag, getTagValues, getTagValueID, getTagValueLabel, PropFieldSuggestion, getPreferredTagValues } from 'models/vocabulary';
 import { PROPERTY_KEY_FIELD_ID, PROPERTY_KEY_FIELD_NAME } from 'views-components/resource-properties-form/property-key-field';
 import {
     handleSelect,
@@ -56,9 +56,11 @@ export const PropertyValueField = connectVocabularyAndPropertyKey(
 const PropertyValueInput = ({ vocabulary, propertyKeyId, propertyKeyName, ...props }: WrappedFieldProps & PropertyValueFieldProps) =>
     <FormName children={data => (
         <Autocomplete
+            {...buildProps(props)}
             label='Value'
             disabled={props.disabled}
             suggestions={getSuggestions(props.input.value, propertyKeyId, vocabulary)}
+            renderSuggestion={(s: PropFieldSuggestion) => (s.description || s.label)}
             onSelect={handleSelect(PROPERTY_VALUE_FIELD_ID, data.form, props.input, props.meta)}
             onBlur={() => {
                 // Case-insensitive search for the value in the vocabulary
@@ -73,7 +75,6 @@ const PropertyValueInput = ({ vocabulary, propertyKeyId, propertyKeyName, ...pro
                 const tagValueID = getTagValueID(propertyKeyId, newValue, vocabulary);
                 handleChange(data.form, tagValueID, props.input, props.meta, newValue);
             }}
-            {...buildProps(props)}
         />
     )} />;
 
@@ -90,7 +91,8 @@ const matchTagValues = ({ vocabulary, propertyKeyId }: PropertyValueFieldProps)
 
 const getSuggestions = (value: string, tagName: string, vocabulary: Vocabulary) => {
     const re = new RegExp(escapeRegExp(value), "i");
-    return getTagValues(tagName, vocabulary).filter(v => re.test(v.label) && v.label !== value);
+    return getPreferredTagValues(tagName, vocabulary, value !== '').filter(
+        v => re.test((v.description || v.label)) && v.label !== value);
 };
 
 const handleChange = (

commit 939593fbdd53bcca1c9c5e2247b3f279fdcf1523
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Thu Feb 10 18:17:56 2022 -0300

    18560: Adds vocabulary querying functions, with their tests.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/models/vocabulary.test.ts b/src/models/vocabulary.test.ts
index 18e2f19f..582af0ec 100644
--- a/src/models/vocabulary.test.ts
+++ b/src/models/vocabulary.test.ts
@@ -18,7 +18,8 @@ describe('Vocabulary', () => {
                     strict: false,
                     labels: [
                         {label: "Animal" },
-                        {label: "Creature"}
+                        {label: "Creature"},
+                        {label: "Beast"},
                     ],
                     values: {
                         IDVALANIMALS1: {
@@ -39,13 +40,13 @@ describe('Vocabulary', () => {
                     labels: [{label: "Sizes"}],
                     values: {
                         IDVALSIZES1: {
-                            labels: [{label: "Small"}]
+                            labels: [{label: "Small"}, {label: "S"}, {label: "Little"}]
                         },
                         IDVALSIZES2: {
-                            labels: [{label: "Medium"}]
+                            labels: [{label: "Medium"}, {label: "M"}]
                         },
                         IDVALSIZES3: {
-                            labels: [{label: "Large"}]
+                            labels: [{label: "Large"}, {label: "L"}]
                         },
                         IDVALSIZES4: {
                             labels: []
@@ -61,23 +62,70 @@ describe('Vocabulary', () => {
         // Alphabetically ordered by label
         expect(tagKeys).toEqual([
             {id: "IDKEYANIMALS", label: "Animal"},
+            {id: "IDKEYANIMALS", label: "Beast"},
             {id: "IDKEYANIMALS", label: "Creature"},
             {id: "IDKEYCOMMENT", label: "IDKEYCOMMENT"},
             {id: "IDKEYSIZES", label: "Sizes"},
         ]);
     });
 
+    it('returns the list of preferred tag keys', () => {
+        const preferredTagKeys = Vocabulary.getPreferredTags(vocabulary);
+        // Alphabetically ordered by label
+        expect(preferredTagKeys).toEqual([
+            {id: "IDKEYANIMALS", label: "Animal", description: "Animal"},
+            {id: "IDKEYCOMMENT", label: "IDKEYCOMMENT"},
+            {id: "IDKEYSIZES", label: "Sizes", description: "Sizes"},
+        ]);
+    });
+
+    it('returns the list of preferred tag keys with synonyms', () => {
+        const preferredTagKeys = Vocabulary.getPreferredTags(vocabulary, true);
+        // Alphabetically ordered by label
+        expect(preferredTagKeys).toEqual([
+            {id: "IDKEYANIMALS", label: "Animal", description: "Animal (Creature, Beast)"},
+            {id: "IDKEYCOMMENT", label: "IDKEYCOMMENT"},
+            {id: "IDKEYSIZES", label: "Sizes", description: "Sizes"},
+        ]);
+    });
+
     it('returns the tag values for a given key', () => {
         const tagValues = Vocabulary.getTagValues('IDKEYSIZES', vocabulary);
         // Alphabetically ordered by label
         expect(tagValues).toEqual([
             {id: "IDVALSIZES4", label: "IDVALSIZES4"},
+            {id: "IDVALSIZES3", label: "L"},
             {id: "IDVALSIZES3", label: "Large"},
+            {id: "IDVALSIZES1", label: "Little"},
+            {id: "IDVALSIZES2", label: "M"},
             {id: "IDVALSIZES2", label: "Medium"},
+            {id: "IDVALSIZES1", label: "S"},
             {id: "IDVALSIZES1", label: "Small"},
         ])
     });
 
+    it('returns the preferred tag values for a given key', () => {
+        const preferredTagValues = Vocabulary.getPreferredTagValues('IDKEYSIZES', vocabulary);
+        // Alphabetically ordered by label
+        expect(preferredTagValues).toEqual([
+            {id: "IDVALSIZES4", label: "IDVALSIZES4"},
+            {id: "IDVALSIZES3", label: "Large", description: "Large"},
+            {id: "IDVALSIZES2", label: "Medium", description: "Medium"},
+            {id: "IDVALSIZES1", label: "Small", description: "Small"},
+        ])
+    });
+
+    it('returns the preferred tag values with synonyms for a given key', () => {
+        const preferredTagValues = Vocabulary.getPreferredTagValues('IDKEYSIZES', vocabulary, true);
+        // Alphabetically ordered by label
+        expect(preferredTagValues).toEqual([
+            {id: "IDVALSIZES4", label: "IDVALSIZES4"},
+            {id: "IDVALSIZES3", label: "Large", description: "Large (L)"},
+            {id: "IDVALSIZES2", label: "Medium", description: "Medium (M)"},
+            {id: "IDVALSIZES1", label: "Small", description: "Small (S, Little)"},
+        ])
+    });
+
     it('returns an empty list of values for an non-existent key', () => {
         const tagValues = Vocabulary.getTagValues('IDNONSENSE', vocabulary);
         expect(tagValues).toEqual([]);
diff --git a/src/models/vocabulary.ts b/src/models/vocabulary.ts
index 3c542844..55525c22 100644
--- a/src/models/vocabulary.ts
+++ b/src/models/vocabulary.ts
@@ -27,6 +27,7 @@ export interface Tag {
 export interface PropFieldSuggestion {
     id: string;
     label: string;
+    description?: string;
 }
 
 const VOCABULARY_VALIDATORS = [
@@ -64,9 +65,9 @@ const compare = (a: PropFieldSuggestion, b: PropFieldSuggestion) => {
     return 0;
 };
 
-export const getTagValues = (tagKeyID: string, vocabulary: Vocabulary) => {
+export const getTagValues = (tagKeyID: string, vocabulary: Vocabulary): PropFieldSuggestion[] => {
     const tag = vocabulary.tags[tagKeyID];
-    const ret = tag && tag.values
+    return tag && tag.values
         ? Object.keys(tag.values).map(
             tagValueID => tag.values![tagValueID].labels && tag.values![tagValueID].labels.length > 0
                 ? tag.values![tagValueID].labels.map(
@@ -75,11 +76,27 @@ export const getTagValues = (tagKeyID: string, vocabulary: Vocabulary) => {
             .reduce((prev, curr) => [...prev, ...curr], [])
             .sort(compare)
         : [];
-    return ret;
 };
 
-export const getTags = ({ tags }: Vocabulary) => {
-    const ret = tags && Object.keys(tags)
+export const getPreferredTagValues = (tagKeyID: string, vocabulary: Vocabulary, withSynonyms?: boolean): PropFieldSuggestion[] => {
+    const tag = vocabulary.tags[tagKeyID];
+    return tag && tag.values
+        ? Object.keys(tag.values).map(
+            tagValueID => tag.values![tagValueID].labels && tag.values![tagValueID].labels.length > 0
+                ? {
+                    "id": tagValueID,
+                    "label": tag.values![tagValueID].labels[0].label,
+                    "description": tag.values![tagValueID].labels[0].label + (
+                        withSynonyms && tag.values![tagValueID].labels.length > 1
+                        ? ` (${tag.values![tagValueID].labels.slice(1).map(l => l.label).join(', ')})`
+                        : '')}
+                : {"id": tagValueID, "label": tagValueID})
+            .sort(compare)
+        : [];
+};
+
+export const getTags = ({ tags }: Vocabulary): PropFieldSuggestion[] => {
+    return tags && Object.keys(tags)
         ? Object.keys(tags).map(
             tagID => tags[tagID].labels && tags[tagID].labels.length > 0
                 ? tags[tagID].labels.map(
@@ -88,7 +105,23 @@ export const getTags = ({ tags }: Vocabulary) => {
             .reduce((prev, curr) => [...prev, ...curr], [])
             .sort(compare)
         : [];
-    return ret;
+};
+
+export const getPreferredTags = ({ tags }: Vocabulary, withSynonyms?: boolean): PropFieldSuggestion[] => {
+    return tags && Object.keys(tags)
+        ? Object.keys(tags).map(
+            tagID => tags[tagID].labels && tags[tagID].labels.length > 0
+                ? {
+                    "id": tagID,
+                    "label": tags[tagID].labels[0].label,
+                    "description": tags[tagID].labels[0].label + (
+                        withSynonyms && tags[tagID].labels.length > 1
+                        ? ` (${tags[tagID].labels.slice(1).map(lbl => lbl.label).join(', ')})`
+                        : ''
+                    )}
+                : {"id": tagID, "label": tagID})
+            .sort(compare)
+        : [];
 };
 
 export const getTagKeyID = (tagKeyLabel:string, vocabulary: Vocabulary) =>

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list