Merge branch '18203-Support-setting-multi-properties-at-once' into main
authorDaniel Kutyła <daniel.kutyla@contractors.roche.com>
Tue, 21 Jun 2022 09:16:57 +0000 (11:16 +0200)
committerDaniel Kutyła <daniel.kutyla@contractors.roche.com>
Tue, 21 Jun 2022 09:17:19 +0000 (11:17 +0200)
closes #18203

Arvados-DCO-1.1-Signed-off-by: Daniel Kutyła <daniel.kutyla@contractors.roche.com>

cypress/integration/collection.spec.js
cypress/integration/project.spec.js
src/views-components/collection-properties/create-collection-properties-form.tsx
src/views-components/collection-properties/update-collection-properties-form.tsx
src/views-components/project-properties/create-project-properties-form.tsx
src/views-components/project-properties/update-project-properties-form.tsx
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/resource-properties-form.tsx

index 1329191dad906b825c13a271227fe8b6d12f4944..fd8e65b26f5f9c60cf54edc2b06ba5553f39731f 100644 (file)
@@ -425,6 +425,20 @@ describe('Collection panel tests', function () {
             });
     });
 
+    it('shows collection owner', () => {
+        cy.createCollection(adminUser.token, {
+            name: `Test collection ${Math.floor(Math.random() * 999999)}`,
+            owner_uuid: activeUser.user.uuid,
+            manifest_text: ". 37b51d194a7513e45b56f6524f2d51f2+3 0:3:bar\n"
+        })
+            .as('testCollection').then((testCollection) => {
+                cy.loginAs(activeUser);
+                cy.goToPath(`/collections/${testCollection.uuid}`);
+                cy.wait(5000);
+                cy.get('[data-cy=collection-info-panel]').contains(`Collection User`);
+            });
+    });
+
     it('tries to rename a file with illegal names', function () {
         // Creates the collection using the admin token so we can set up
         // a bogus manifest text without block signatures.
index 0017e416c65e57627f4e0dc8c6a7d5b778428227..7371ed060bf79bded70edd01f3bd0d8ff18f1892 100644 (file)
@@ -28,7 +28,7 @@ describe('Project tests', function() {
         cy.clearLocalStorage();
     });
 
-    it('creates a new project with properties', function() {
+    it('creates a new project with multiple properties', function() {
         const projName = `Test project (${Math.floor(999999 * Math.random())})`;
         cy.loginAs(activeUser);
         cy.get('[data-cy=side-panel-button]').click();
@@ -51,9 +51,26 @@ describe('Project tests', function() {
                 cy.get('input').type('Magenta');
             });
             cy.root().submit();
+            cy.get('[data-cy=property-field-value]').within(() => {
+                cy.get('input').type('Pink');
+            });
+            cy.root().submit();
+            cy.get('[data-cy=property-field-value]').within(() => {
+                cy.get('input').type('Yellow');
+            });
+            cy.root().submit();
         });
         // Confirm proper vocabulary labels are displayed on the UI.
         cy.get('[data-cy=form-dialog]').should('contain', 'Color: Magenta');
+        cy.get('[data-cy=form-dialog]').should('contain', 'Color: Pink');
+        cy.get('[data-cy=form-dialog]').should('contain', 'Color: Yellow');
+
+        cy.get('[data-cy=resource-properties-form]').within(() => {
+            cy.get('[data-cy=property-field-key]').within(() => {
+                cy.get('input').focus();
+            });
+            cy.get('[data-cy=property-field-key]').should('not.contain', 'Color');
+        });
 
         // Create project and confirm the properties' real values.
         cy.get('[data-cy=form-submit-btn]').click();
index 3f19e158595ca84e6f109d448184d41f76c66850..fb18bb1ad818e362fb03028bdba204f285235aba 100644 (file)
@@ -2,7 +2,7 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { reduxForm, reset } from 'redux-form';
+import { reduxForm, change } from 'redux-form';
 import { withStyles } from '@material-ui/core';
 import {
     COLLECTION_CREATE_PROPERTIES_FORM_NAME,
@@ -13,6 +13,7 @@ import {
     ResourcePropertiesFormData
 } from 'views-components/resource-properties-form/resource-properties-form';
 import { addPropertyToResourceForm } from 'store/resources/resources-actions';
+import { PROPERTY_VALUE_FIELD_NAME } from 'views-components/resource-properties-form/property-value-field';
 
 const Form = withStyles(
     ({ spacing }) => (
@@ -27,6 +28,6 @@ export const CreateCollectionPropertiesForm = reduxForm<ResourcePropertiesFormDa
     form: COLLECTION_CREATE_PROPERTIES_FORM_NAME,
     onSubmit: (data, dispatch) => {
         dispatch(addPropertyToResourceForm(data, COLLECTION_CREATE_FORM_NAME));
-        dispatch(reset(COLLECTION_CREATE_PROPERTIES_FORM_NAME));
+        dispatch(change(COLLECTION_CREATE_PROPERTIES_FORM_NAME, PROPERTY_VALUE_FIELD_NAME, ''));
     }
 })(Form);
\ No newline at end of file
index 9092c7cce16396e05637858cff27f9877b90c02c..3ab425f16624bebe4874885ed645aca6324aff53 100644 (file)
@@ -2,7 +2,7 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { reduxForm, reset } from 'redux-form';
+import { reduxForm, change } from 'redux-form';
 import { withStyles } from '@material-ui/core';
 import {
     COLLECTION_UPDATE_FORM_NAME,
@@ -13,6 +13,7 @@ import {
     ResourcePropertiesFormData
 } from 'views-components/resource-properties-form/resource-properties-form';
 import { addPropertyToResourceForm } from 'store/resources/resources-actions';
+import { PROPERTY_VALUE_FIELD_NAME } from 'views-components/resource-properties-form/property-value-field';
 
 const Form = withStyles(
     ({ spacing }) => (
@@ -27,6 +28,6 @@ export const UpdateCollectionPropertiesForm = reduxForm<ResourcePropertiesFormDa
     form: COLLECTION_UPDATE_PROPERTIES_FORM_NAME,
     onSubmit: (data, dispatch) => {
         dispatch(addPropertyToResourceForm(data, COLLECTION_UPDATE_FORM_NAME));
-        dispatch(reset(COLLECTION_UPDATE_PROPERTIES_FORM_NAME));
+        dispatch(change(COLLECTION_UPDATE_PROPERTIES_FORM_NAME, PROPERTY_VALUE_FIELD_NAME, ''));
     }
 })(Form);
\ No newline at end of file
index 8c26523e8e79327ef0f53ac1bbe829e765031e20..5a6d9df6471374d176112a33aabd9e029b5c1ae1 100644 (file)
@@ -2,7 +2,7 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { reduxForm, reset } from 'redux-form';
+import { reduxForm, change } from 'redux-form';
 import { withStyles } from '@material-ui/core';
 import {
     PROJECT_CREATE_PROPERTIES_FORM_NAME,
@@ -13,6 +13,7 @@ import {
     ResourcePropertiesFormData
 } from 'views-components/resource-properties-form/resource-properties-form';
 import { addPropertyToResourceForm } from 'store/resources/resources-actions';
+import { PROPERTY_VALUE_FIELD_NAME } from 'views-components/resource-properties-form/property-value-field';
 
 const Form = withStyles(
     ({ spacing }) => (
@@ -27,6 +28,6 @@ export const CreateProjectPropertiesForm = reduxForm<ResourcePropertiesFormData>
     form: PROJECT_CREATE_PROPERTIES_FORM_NAME,
     onSubmit: (data, dispatch) => {
         dispatch(addPropertyToResourceForm(data, PROJECT_CREATE_FORM_NAME));
-        dispatch(reset(PROJECT_CREATE_PROPERTIES_FORM_NAME));
+        dispatch(change(PROJECT_CREATE_PROPERTIES_FORM_NAME, PROPERTY_VALUE_FIELD_NAME, ''));
     }
 })(Form);
\ No newline at end of file
index 0b5554bc52aedee200e9973031ff4fe5a39bf0b7..9bce50ab8a539c162e5d13b61f22725489176ba4 100644 (file)
@@ -2,7 +2,7 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { reduxForm, reset } from 'redux-form';
+import { reduxForm, change } from 'redux-form';
 import { withStyles } from '@material-ui/core';
 import {
     PROJECT_UPDATE_PROPERTIES_FORM_NAME,
@@ -13,6 +13,7 @@ import {
     ResourcePropertiesFormData
 } from 'views-components/resource-properties-form/resource-properties-form';
 import { addPropertyToResourceForm } from 'store/resources/resources-actions';
+import { PROPERTY_VALUE_FIELD_NAME } from 'views-components/resource-properties-form/property-value-field';
 
 const Form = withStyles(
     ({ spacing }) => (
@@ -27,6 +28,6 @@ export const UpdateProjectPropertiesForm = reduxForm<ResourcePropertiesFormData>
     form: PROJECT_UPDATE_PROPERTIES_FORM_NAME,
     onSubmit: (data, dispatch) => {
         dispatch(addPropertyToResourceForm(data, PROJECT_UPDATE_FORM_NAME));
-        dispatch(reset(PROJECT_UPDATE_PROPERTIES_FORM_NAME));
+        dispatch(change(PROJECT_UPDATE_PROPERTIES_FORM_NAME, PROPERTY_VALUE_FIELD_NAME, ''));
     }
 })(Form);
\ No newline at end of file
index c4dc494b3967558179ba662df5272164d8593013..dad172840bfce6905635fc86e86c6a376ddaa94b 100644 (file)
@@ -14,6 +14,7 @@ export interface VocabularyProp {
 
 export interface ValidationProp {
     skipValidation?: boolean;
+    clearPropertyKeyOnSelect?: boolean;
 }
 
 export const mapStateToProps = (state: RootState, ownProps: ValidationProp): VocabularyProp & ValidationProp => ({
index 0be4527ac36d8370ee678b56710dab5d6dbbcb2b..0b08ad6d7d2e295f06ca14fa6933ea38d01ebf10 100644 (file)
@@ -30,9 +30,10 @@ export const PROPERTY_KEY_FIELD_NAME = 'key';
 export const PROPERTY_KEY_FIELD_ID = 'keyID';
 
 export const PropertyKeyField = connectVocabulary(
-    ({ vocabulary, skipValidation }: VocabularyProp & ValidationProp) =>
+    ({ vocabulary, skipValidation, clearPropertyKeyOnSelect }: VocabularyProp & ValidationProp) =>
         <span data-cy='property-field-key'>
         <Field
+            clearPropertyKeyOnSelect
             name={PROPERTY_KEY_FIELD_NAME}
             component={PropertyKeyInput}
             vocabulary={vocabulary}
@@ -40,7 +41,7 @@ export const PropertyKeyField = connectVocabulary(
         </span>
 );
 
-const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & VocabularyProp) =>
+const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & VocabularyProp & { clearPropertyKeyOnSelect?: boolean }) =>
     <FormName children={data => (
         <Autocomplete
             {...buildProps(props)}
@@ -51,6 +52,11 @@ const PropertyKeyInput = ({ vocabulary, ...props }: WrappedFieldProps & Vocabula
                     ? `${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
index 979d772ea5807cb0646fb934ec9b9c824fc559e2..25d0f2bb377e8a9bcb567838c212558a542ff4e8 100644 (file)
@@ -16,16 +16,17 @@ export interface ResourcePropertiesFormData {
     [PROPERTY_KEY_FIELD_ID]: string;
     [PROPERTY_VALUE_FIELD_NAME]: string;
     [PROPERTY_VALUE_FIELD_ID]: string;
+    clearPropertyKeyOnSelect?: boolean;
 }
 
-export type ResourcePropertiesFormProps = {uuid: string; } & InjectedFormProps<ResourcePropertiesFormData, {uuid: string; }> & WithStyles<GridClassKey>;
+export type ResourcePropertiesFormProps = {uuid: string; clearPropertyKeyOnSelect?: boolean } & InjectedFormProps<ResourcePropertiesFormData, {uuid: string; }> & WithStyles<GridClassKey>;
 
-export const ResourcePropertiesForm = ({ handleSubmit, change, submitting, invalid, classes, uuid }: ResourcePropertiesFormProps ) => {
+export const ResourcePropertiesForm = ({ handleSubmit, change, submitting, invalid, classes, uuid, clearPropertyKeyOnSelect }: ResourcePropertiesFormProps ) => {
     change('uuid', uuid); // Sets the uuid field to the uuid of the resource.
     return <form data-cy='resource-properties-form' onSubmit={handleSubmit}>
         <Grid container spacing={16} classes={classes}>
             <Grid item xs>
-                <PropertyKeyField />
+                <PropertyKeyField clearPropertyKeyOnSelect />
             </Grid>
             <Grid item xs>
                 <PropertyValueField />