From e7916d508b495a1fd2be51bd3da2125cc8eb7467 Mon Sep 17 00:00:00 2001 From: Lucas Di Pentima Date: Tue, 12 Nov 2019 09:31:59 -0300 Subject: [PATCH] 15067: Tag key/value suggestions show all available labels. 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 --- public/vocabulary-example.json | 15 +++-- src/models/vocabulary.ts | 57 ++++++++++++++++++- .../property-field-common.tsx | 7 +-- .../property-key-field.tsx | 23 ++------ .../property-value-field.tsx | 35 ++---------- 5 files changed, 77 insertions(+), 60 deletions(-) diff --git a/public/vocabulary-example.json b/public/vocabulary-example.json index 4859b5d6..59d4de7a 100644 --- a/public/vocabulary-example.json +++ b/public/vocabulary-example.json @@ -57,27 +57,32 @@ "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"} ] } } diff --git a/src/models/vocabulary.ts b/src/models/vocabulary.ts index 173c8371..158d8058 100644 --- a/src/models/vocabulary.ts +++ b/src/models/vocabulary.ts @@ -24,6 +24,11 @@ export interface Tag { values?: Record; } +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) || ''; diff --git a/src/views-components/resource-properties-form/property-field-common.tsx b/src/views-components/resource-properties-form/property-field-common.tsx index a0f5800f..a90ce923 100644 --- a/src/views-components/resource-properties-form/property-field-common.tsx +++ b/src/views-components/resource-properties-form/property-field-common.tsx @@ -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; -} 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 5c8c875b..ff665df2 100644 --- a/src/views-components/resource-properties-form/property-key-field.tsx +++ b/src/views-components/resource-properties-form/property-key-field.tsx @@ -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. 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 910a095a..32109ef4 100644 --- a/src/views-components/resource-properties-form/property-value-field.tsx +++ b/src/views-components/resource-properties-form/property-value-field.tsx @@ -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 = ( -- 2.30.2