//
// SPDX-License-Identifier: AGPL-3.0
-import * as React from 'react';
-import { change, WrappedFieldProps, WrappedFieldMetaProps, WrappedFieldInputProps, Field } from 'redux-form';
+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 } from '~/models/vocabulary';
-import { connectVocabulary, VocabularyProp, buildProps, PropFieldSuggestion } 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';
+import { Autocomplete } from 'components/autocomplete/autocomplete';
+import {
+ Vocabulary,
+ getTags,
+ getTagKeyID,
+ getTagKeyLabel,
+ getPreferredTags,
+ PropFieldSuggestion
+} from 'models/vocabulary';
+import {
+ handleSelect,
+ handleBlur,
+ connectVocabulary,
+ VocabularyProp,
+ ValidationProp,
+ buildProps
+} from 'views-components/resource-properties-form/property-field-common';
+import { TAG_KEY_VALIDATION } from 'validators/validators';
+import { escapeRegExp } from 'common/regexp';
+import { ChangeEvent } from 'react';
export const PROPERTY_KEY_FIELD_NAME = 'key';
export const PROPERTY_KEY_FIELD_ID = 'keyID';
export const PropertyKeyField = connectVocabulary(
- ({ vocabulary }: VocabularyProp) =>
- <div>
- <Field
- name={PROPERTY_KEY_FIELD_NAME}
- component={PropertyKeyInput}
- vocabulary={vocabulary}
- validate={getValidation(vocabulary)} />
- <Field
- name={PROPERTY_KEY_FIELD_ID}
- type='hidden'
- component='input' />
- </div>
+ ({ vocabulary, skipValidation, clearPropertyKeyOnSelect }: VocabularyProp & ValidationProp) =>
+ <span data-cy='property-field-key'>
+ <Field
+ clearPropertyKeyOnSelect
+ name={PROPERTY_KEY_FIELD_NAME}
+ component={PropertyKeyInput}
+ vocabulary={vocabulary}
+ validate={skipValidation ? undefined : getValidation(vocabulary)} />
+ </span>
);
-export const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & VocabularyProp) =>
- <Autocomplete
- label='Key'
- suggestions={getSuggestions(props.input.value, vocabulary)}
- onSelect={handleSelect(props.input, props.meta)}
- {...buildProps(props)}
- />;
+const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & VocabularyProp & { clearPropertyKeyOnSelect?: boolean }) =>
+ <FormName children={data => (
+ <Autocomplete
+ {...buildProps(props)}
+ label='Key'
+ suggestions={getSuggestions(props.input.value, vocabulary)}
+ renderSuggestion={
+ (s: PropFieldSuggestion) => s.synonyms && s.synonyms.length > 0
+ ? `${s.label} (${s.synonyms.join('; ')})`
+ : s.label
+ }
+ onFocus={() => {
+ if (props.clearPropertyKeyOnSelect && props.input.value) {
+ props.meta.dispatch(reset(props.meta.form));
+ }
+ }}
+ onSelect={handleSelect(PROPERTY_KEY_FIELD_ID, data.form, props.input, props.meta)}
+ onBlur={() => {
+ // Case-insensitive search for the key in the vocabulary
+ const foundKeyID = getTagKeyID(props.input.value, vocabulary);
+ if (foundKeyID !== '') {
+ props.input.value = getTagKeyLabel(foundKeyID, vocabulary);
+ }
+ handleBlur(PROPERTY_KEY_FIELD_ID, data.form, props.meta, props.input, foundKeyID)();
+ }}
+ onChange={(e: ChangeEvent<HTMLInputElement>) => {
+ const newValue = e.currentTarget.value;
+ handleChange(data.form, props.input, props.meta, newValue);
+ }}
+ />
+ )} />;
const getValidation = memoize(
(vocabulary: Vocabulary) =>
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 getSuggestions = (value: string, vocabulary: Vocabulary): PropFieldSuggestion[] => {
+ const re = new RegExp(escapeRegExp(value), "i");
+ return getPreferredTags(vocabulary, value).filter(
+ tag => (tag.label !== value && re.test(tag.label)) ||
+ (tag.synonyms && tag.synonyms.some(s => re.test(s))));
};
-const handleSelect = (
+const handleChange = (
+ formName: string,
{ onChange }: WrappedFieldInputProps,
- { dispatch }: WrappedFieldMetaProps) => {
- return (item:PropFieldSuggestion) => {
- onChange(item.label);
- dispatch(change(COLLECTION_TAG_FORM_NAME, PROPERTY_KEY_FIELD_ID, item.id));
- };
-};
+ { dispatch }: WrappedFieldMetaProps,
+ value: string) => {
+ // Properties' values are dependant on the keys, if any value is
+ // pre-existant, a change on the property key should mean that the
+ // previous value is invalid, so we better reset the whole form before
+ // setting the new tag key.
+ dispatch(reset(formName));
+
+ onChange(value);
+ dispatch(change(formName, PROPERTY_KEY_FIELD_NAME, value));
+ };
\ No newline at end of file