1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React from 'react';
6 import { WrappedFieldProps, Field, FormName, reset, change, WrappedFieldInputProps, WrappedFieldMetaProps } from 'redux-form';
7 import { memoize } from 'lodash';
8 import { Autocomplete } from 'components/autocomplete/autocomplete';
16 } from 'models/vocabulary';
24 } from 'views-components/resource-properties-form/property-field-common';
25 import { TAG_KEY_VALIDATION } from 'validators/validators';
26 import { escapeRegExp } from 'common/regexp';
27 import { ChangeEvent } from 'react';
29 export const PROPERTY_KEY_FIELD_NAME = 'key';
30 export const PROPERTY_KEY_FIELD_ID = 'keyID';
32 export const PropertyKeyField = connectVocabulary(
33 ({ vocabulary, skipValidation, clearPropertyKeyOnSelect }: VocabularyProp & ValidationProp) =>
34 <span data-cy='property-field-key'>
36 clearPropertyKeyOnSelect
37 name={PROPERTY_KEY_FIELD_NAME}
38 component={PropertyKeyInput}
39 vocabulary={vocabulary}
40 validate={skipValidation ? undefined : getValidation(vocabulary)} />
44 const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & VocabularyProp & { clearPropertyKeyOnSelect?: boolean }) =>
45 <FormName children={data => (
47 {...buildProps(props)}
49 suggestions={getSuggestions(props.input.value, vocabulary)}
51 (s: PropFieldSuggestion) => s.synonyms && s.synonyms.length > 0
52 ? `${s.label} (${s.synonyms.join('; ')})`
56 if (props.clearPropertyKeyOnSelect && props.input.value) {
57 props.meta.dispatch(reset(props.meta.form));
60 onSelect={handleSelect(PROPERTY_KEY_FIELD_ID, data.form, props.input, props.meta)}
62 // Case-insensitive search for the key in the vocabulary
63 const foundKeyID = getTagKeyID(props.input.value, vocabulary);
64 if (foundKeyID !== '') {
65 props.input.value = getTagKeyLabel(foundKeyID, vocabulary);
67 handleBlur(PROPERTY_KEY_FIELD_ID, data.form, props.meta, props.input, foundKeyID)();
69 onChange={(e: ChangeEvent<HTMLInputElement>) => {
70 const newValue = e.currentTarget.value;
71 handleChange(data.form, props.input, props.meta, newValue);
76 const getValidation = memoize(
77 (vocabulary: Vocabulary) =>
78 vocabulary.strict_tags
79 ? [...TAG_KEY_VALIDATION, matchTags(vocabulary)]
80 : TAG_KEY_VALIDATION);
82 const matchTags = (vocabulary: Vocabulary) =>
84 getTags(vocabulary).find(tag => tag.label === value)
88 const getSuggestions = (value: string, vocabulary: Vocabulary): PropFieldSuggestion[] => {
89 const re = new RegExp(escapeRegExp(value), "i");
90 return getPreferredTags(vocabulary, value).filter(
91 tag => (tag.label !== value && re.test(tag.label)) ||
92 (tag.synonyms && tag.synonyms.some(s => re.test(s))));
95 const handleChange = (
97 { onChange }: WrappedFieldInputProps,
98 { dispatch }: WrappedFieldMetaProps,
100 // Properties' values are dependant on the keys, if any value is
101 // pre-existant, a change on the property key should mean that the
102 // previous value is invalid, so we better reset the whole form before
103 // setting the new tag key.
104 dispatch(reset(formName));
107 dispatch(change(formName, PROPERTY_KEY_FIELD_NAME, value));