From: Stephen Smith Date: Wed, 15 Dec 2021 05:35:25 +0000 (-0500) Subject: 18123: Use update project dialog for creating groups and remove create group dialog. X-Git-Tag: 2.4.0~22^2~2 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/df9197982583c61e6f142d37ca8e01a8277027d3 18123: Use update project dialog for creating groups and remove create group dialog. Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- diff --git a/cypress/integration/group-manage.spec.js b/cypress/integration/group-manage.spec.js index cae69efb..c98c2201 100644 --- a/cypress/integration/group-manage.spec.js +++ b/cypress/integration/group-manage.spec.js @@ -6,6 +6,7 @@ describe('Group manage tests', function() { let activeUser; let adminUser; let otherUser; + let userThree; const groupName = `Test group (${Math.floor(999999 * Math.random())})`; before(function() { @@ -28,6 +29,11 @@ describe('Group manage tests', function() { otherUser = this.otherUser; } ); + cy.getUser('userThree', 'User', 'Three', false, true) + .as('userThree').then(function() { + userThree = this.userThree; + } + ); }); beforeEach(function() { @@ -44,15 +50,18 @@ describe('Group manage tests', function() { // Create new group cy.get('[data-cy=groups-panel-new-group]').click(); cy.get('[data-cy=form-dialog]') - .should('contain', 'Create a group') + .should('contain', 'Create Group') .within(() => { cy.get('input[name=name]').type(groupName); - cy.get('button[type=submit]').click(); + cy.get('[data-cy=users-field] input').type("three"); }); + cy.get('[role=tooltip]').click(); + cy.get('[data-cy=form-dialog] button[type=submit]').click(); // Check that the group was created cy.get('[data-cy=groups-panel-data-explorer]').contains(groupName).click(); - cy.get('[data-cy=group-members-data-explorer]').contains('Active User'); + cy.get('[data-cy=group-members-data-explorer]').contains(activeUser.user.full_name); + cy.get('[data-cy=group-members-data-explorer]').contains(userThree.user.full_name); }); it('adds users to the group', function() { @@ -68,13 +77,13 @@ describe('Group manage tests', function() { // Check that both users are present with appropriate permissions cy.get('[data-cy=group-members-data-explorer]') - .contains('Other User') + .contains(otherUser.user.full_name) .parents('tr') .within(() => { cy.contains('Read'); }); cy.get('[data-cy=group-members-data-explorer] tr') - .contains('Active User') + .contains(activeUser.user.full_name) .parents('tr') .within(() => { cy.contains('Manage'); @@ -84,7 +93,7 @@ describe('Group manage tests', function() { it('changes permission level of a member', function() { // Test change permission level cy.get('[data-cy=group-members-data-explorer]') - .contains('Other User') + .contains(otherUser.user.full_name) .parents('tr') .within(() => { cy.contains('Read') @@ -97,7 +106,7 @@ describe('Group manage tests', function() { .contains('Write') .click(); cy.get('[data-cy=group-members-data-explorer]') - .contains('Other User') + .contains(otherUser.user.full_name) .parents('tr') .within(() => { cy.contains('Write'); @@ -113,12 +122,12 @@ describe('Group manage tests', function() { // Check that other user is hidden cy.get('[data-cy=group-details-permissions-tab]').click(); cy.get('[data-cy=group-permissions-data-explorer]') - .should('not.contain', 'Other User') + .should('not.contain', otherUser.user.full_name) cy.get('[data-cy=group-details-members-tab]').click(); // Test unhide cy.get('[data-cy=group-members-data-explorer]') - .contains('Other User') + .contains(otherUser.user.full_name) .parents('tr') .within(() => { cy.get('[data-cy=user-visible-checkbox]').click(); @@ -126,7 +135,7 @@ describe('Group manage tests', function() { // Check that other user is visible cy.get('[data-cy=group-details-permissions-tab]').click(); cy.get('[data-cy=group-permissions-data-explorer]') - .contains('Other User') + .contains(otherUser.user.full_name) .parents('tr') .within(() => { cy.contains('Read'); @@ -134,7 +143,7 @@ describe('Group manage tests', function() { // Test re-hide cy.get('[data-cy=group-details-members-tab]').click(); cy.get('[data-cy=group-members-data-explorer]') - .contains('Other User') + .contains(otherUser.user.full_name) .parents('tr') .within(() => { cy.get('[data-cy=user-visible-checkbox]').click(); @@ -142,7 +151,7 @@ describe('Group manage tests', function() { // Check that other user is hidden cy.get('[data-cy=group-details-permissions-tab]').click(); cy.get('[data-cy=group-permissions-data-explorer]') - .should('not.contain', 'Other User') + .should('not.contain', otherUser.user.full_name) }); it('displays resources shared with the group', function() { @@ -183,14 +192,25 @@ describe('Group manage tests', function() { // Remove other user cy.get('[data-cy=group-members-data-explorer]') - .contains('Other User') + .contains(otherUser.user.full_name) + .parents('tr') + .within(() => { + cy.get('[data-cy=resource-delete-button]').click(); + }); + cy.get('[data-cy=confirmation-dialog-ok-btn]').click(); + cy.get('[data-cy=group-members-data-explorer]') + .should('not.contain', otherUser.user.full_name); + + // Remove user three + cy.get('[data-cy=group-members-data-explorer]') + .contains(userThree.user.full_name) .parents('tr') .within(() => { cy.get('[data-cy=resource-delete-button]').click(); }); cy.get('[data-cy=confirmation-dialog-ok-btn]').click(); cy.get('[data-cy=group-members-data-explorer]') - .should('not.contain', 'Other User'); + .should('not.contain', userThree.user.full_name); }); it('renames the group', function() { @@ -207,7 +227,7 @@ describe('Group manage tests', function() { // Rename the group cy.get('[data-cy=form-dialog]') - .should('contain', 'Edit Project') + .should('contain', 'Edit Group') .within(() => { cy.get('input[name=name]').clear().type(groupName + ' (renamed)'); cy.get('button[type=submit]').click(); diff --git a/src/store/groups-panel/groups-panel-actions.ts b/src/store/groups-panel/groups-panel-actions.ts index 6d17db19..be4e5d72 100644 --- a/src/store/groups-panel/groups-panel-actions.ts +++ b/src/store/groups-panel/groups-panel-actions.ts @@ -20,12 +20,6 @@ import { ProjectUpdateFormDialogData, PROJECT_UPDATE_FORM_NAME } from 'store/pro export const GROUPS_PANEL_ID = "groupsPanel"; -// Create group dialog -export const CREATE_GROUP_DIALOG = "createGroupDialog"; -export const CREATE_GROUP_FORM = "createGroupForm"; -export const CREATE_GROUP_NAME_FIELD_NAME = 'name'; -export const CREATE_GROUP_USERS_FIELD_NAME = 'users'; - export const GROUP_ATTRIBUTES_DIALOG = 'groupAttributesDialog'; export const GROUP_REMOVE_DIALOG = 'groupRemoveDialog'; @@ -34,9 +28,9 @@ export const GroupsPanelActions = bindDataExplorerActions(GROUPS_PANEL_ID); export const loadGroupsPanel = () => GroupsPanelActions.REQUEST_ITEMS(); export const openCreateGroupDialog = () => - (dispatch: Dispatch) => { - dispatch(dialogActions.OPEN_DIALOG({ id: CREATE_GROUP_DIALOG, data: {} })); - dispatch(reset(CREATE_GROUP_FORM)); + (dispatch: Dispatch, getState: () => RootState) => { + dispatch(initialize(PROJECT_UPDATE_FORM_NAME, {})); + dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_UPDATE_FORM_NAME, data: {sourcePanel: GroupClass.ROLE, showUsersField: true} })); }; export const openGroupAttributes = (uuid: string) => @@ -94,16 +88,11 @@ export const updateGroup = (project: ProjectUpdateFormDialogData) => } }; -export interface CreateGroupFormData { - [CREATE_GROUP_NAME_FIELD_NAME]: string; - [CREATE_GROUP_USERS_FIELD_NAME]?: Participant[]; -} - -export const createGroup = ({ name, users = [] }: CreateGroupFormData) => +export const createGroup = ({ name, users = [], description }: ProjectUpdateFormDialogData) => async (dispatch: Dispatch, _: {}, { groupsService, permissionService }: ServiceRepository) => { - dispatch(startSubmit(CREATE_GROUP_FORM)); + dispatch(startSubmit(PROJECT_UPDATE_FORM_NAME)); try { - const newGroup = await groupsService.create({ name, groupClass: GroupClass.ROLE }); + const newGroup = await groupsService.create({ name, description, groupClass: GroupClass.ROLE }); for (const user of users) { await addGroupMember({ user, @@ -112,8 +101,8 @@ export const createGroup = ({ name, users = [] }: CreateGroupFormData) => permissionService, }); } - dispatch(dialogActions.CLOSE_DIALOG({ id: CREATE_GROUP_DIALOG })); - dispatch(reset(CREATE_GROUP_FORM)); + dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_UPDATE_FORM_NAME })); + dispatch(reset(PROJECT_UPDATE_FORM_NAME)); dispatch(loadGroupsPanel()); dispatch(snackbarActions.OPEN_SNACKBAR({ message: `${newGroup.name} group has been created`, @@ -123,7 +112,7 @@ export const createGroup = ({ name, users = [] }: CreateGroupFormData) => } catch (e) { const error = getCommonResourceServiceError(e); if (error === CommonResourceServiceError.UNIQUE_NAME_VIOLATION) { - dispatch(stopSubmit(CREATE_GROUP_FORM, { name: 'Group with the same name already exists.' } as FormErrors)); + dispatch(stopSubmit(PROJECT_UPDATE_FORM_NAME, { name: 'Group with the same name already exists.' } as FormErrors)); } return; } diff --git a/src/store/projects/project-update-actions.ts b/src/store/projects/project-update-actions.ts index 45065b62..ba176753 100644 --- a/src/store/projects/project-update-actions.ts +++ b/src/store/projects/project-update-actions.ts @@ -10,10 +10,12 @@ import { getCommonResourceServiceError, CommonResourceServiceError } from "servi import { ServiceRepository } from "services/services"; import { projectPanelActions } from 'store/project-panel/project-panel-action'; import { GroupClass } from "models/group"; +import { Participant } from "views-components/sharing-dialog/participant-select"; export interface ProjectUpdateFormDialogData { uuid: string; name: string; + users?: Participant[]; description?: string; } diff --git a/src/views-components/dialog-forms/create-group-dialog.tsx b/src/views-components/dialog-forms/create-group-dialog.tsx deleted file mode 100644 index fceea262..00000000 --- a/src/views-components/dialog-forms/create-group-dialog.tsx +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) The Arvados Authors. All rights reserved. -// -// SPDX-License-Identifier: AGPL-3.0 - -import React from 'react'; -import { compose } from "redux"; -import { reduxForm, InjectedFormProps, Field, WrappedFieldArrayProps, FieldArray } from 'redux-form'; -import { withDialog, WithDialogProps } from "store/dialog/with-dialog"; -import { FormDialog } from 'components/form-dialog/form-dialog'; -import { CREATE_GROUP_DIALOG, CREATE_GROUP_FORM, createGroup, CreateGroupFormData, CREATE_GROUP_NAME_FIELD_NAME, CREATE_GROUP_USERS_FIELD_NAME } from 'store/groups-panel/groups-panel-actions'; -import { TextField } from 'components/text-field/text-field'; -import { maxLength } from 'validators/max-length'; -import { require } from 'validators/require'; -import { ParticipantSelect, Participant } from 'views-components/sharing-dialog/participant-select'; - -export const CreateGroupDialog = compose( - withDialog(CREATE_GROUP_DIALOG), - reduxForm({ - form: CREATE_GROUP_FORM, - onSubmit: (data, dispatch) => { - dispatch(createGroup(data)); - } - }) -)( - (props: CreateGroupDialogComponentProps) => - -); - -type CreateGroupDialogComponentProps = WithDialogProps<{}> & InjectedFormProps; - -const CreateGroupFormFields = () => - <> - - - ; - -const GroupNameField = () => - ; - -const GROUP_NAME_VALIDATION = [require, maxLength(255)]; - -const UsersField = () => - ; - -const UsersSelect = ({ fields }: WrappedFieldArrayProps) => - ; diff --git a/src/views-components/dialog-forms/update-project-dialog.ts b/src/views-components/dialog-forms/update-project-dialog.ts index 119e9256..4ba03f2f 100644 --- a/src/views-components/dialog-forms/update-project-dialog.ts +++ b/src/views-components/dialog-forms/update-project-dialog.ts @@ -9,19 +9,23 @@ import { DialogProjectUpdate } from 'views-components/dialog-update/dialog-proje import { PROJECT_UPDATE_FORM_NAME, ProjectUpdateFormDialogData } from 'store/projects/project-update-actions'; import { updateProject, updateGroup } from 'store/workbench/workbench-actions'; import { GroupClass } from "models/group"; +import { createGroup } from "store/groups-panel/groups-panel-actions"; export const UpdateProjectDialog = compose( withDialog(PROJECT_UPDATE_FORM_NAME), reduxForm({ form: PROJECT_UPDATE_FORM_NAME, onSubmit: (data, dispatch, props) => { - console.log(props); switch (props.data.sourcePanel) { case GroupClass.PROJECT: dispatch(updateProject(data)); break; case GroupClass.ROLE: - dispatch(updateGroup(data)); + if (data.uuid) { + dispatch(updateGroup(data)); + } else { + dispatch(createGroup(data)); + } break; default: break; diff --git a/src/views-components/dialog-update/dialog-project-update.tsx b/src/views-components/dialog-update/dialog-project-update.tsx index ac14e5dc..64f75433 100644 --- a/src/views-components/dialog-update/dialog-project-update.tsx +++ b/src/views-components/dialog-update/dialog-project-update.tsx @@ -7,19 +7,38 @@ import { InjectedFormProps } from 'redux-form'; import { WithDialogProps } from 'store/dialog/with-dialog'; import { ProjectUpdateFormDialogData } from 'store/projects/project-update-actions'; import { FormDialog } from 'components/form-dialog/form-dialog'; -import { ProjectNameField, ProjectDescriptionField } from 'views-components/form-fields/project-form-fields'; +import { ProjectNameField, ProjectDescriptionField, UsersField } from 'views-components/form-fields/project-form-fields'; +import { GroupClass } from 'models/group'; -type DialogProjectProps = WithDialogProps<{}> & InjectedFormProps; +type DialogProjectProps = WithDialogProps<{sourcePanel: GroupClass, showUsersField?: boolean}> & InjectedFormProps; -export const DialogProjectUpdate = (props: DialogProjectProps) => - { + let title = 'Edit Project'; + let fields = ProjectEditFields; + const sourcePanel = props.data.sourcePanel || ''; + const showUsersField = !!props.data.showUsersField; + + if (sourcePanel === GroupClass.ROLE) { + title = showUsersField ? 'Create Group' : 'Edit Group'; + fields = showUsersField ? GroupAddFields : ProjectEditFields; + } + + return ; +}; +// Also used as "Group Edit Fields" const ProjectEditFields = () => ; + +const GroupAddFields = () => + + + +; diff --git a/src/views-components/form-fields/project-form-fields.tsx b/src/views-components/form-fields/project-form-fields.tsx index 34d7cef7..48348bab 100644 --- a/src/views-components/form-fields/project-form-fields.tsx +++ b/src/views-components/form-fields/project-form-fields.tsx @@ -3,11 +3,12 @@ // SPDX-License-Identifier: AGPL-3.0 import React from "react"; -import { Field, Validator } from "redux-form"; +import { Field, FieldArray, Validator, WrappedFieldArrayProps } from "redux-form"; import { TextField, RichEditorTextField } from "components/text-field/text-field"; import { PROJECT_NAME_VALIDATION, PROJECT_NAME_VALIDATION_ALLOW_SLASH } from "validators/validators"; import { connect } from "react-redux"; import { RootState } from "store/store"; +import { Participant, ParticipantSelect } from "views-components/sharing-dialog/participant-select"; interface ProjectNameFieldProps { validate: Validator[]; @@ -42,3 +43,16 @@ export const ProjectDescriptionField = () => name='description' component={RichEditorTextField as any} label="Description - optional" />; + +export const UsersField = () => + ; + +export const UsersSelect = ({ fields }: WrappedFieldArrayProps) => + ; diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index c756a7d6..64caa6ca 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -82,7 +82,6 @@ import { HelpApiClientAuthorizationDialog } from 'views-components/api-client-au import { UserManageDialog } from 'views-components/user-dialog/manage-dialog'; import { SetupShellAccountDialog } from 'views-components/dialog-forms/setup-shell-account-dialog'; import { GroupsPanel } from 'views/groups-panel/groups-panel'; -import { CreateGroupDialog } from 'views-components/dialog-forms/create-group-dialog'; import { RemoveGroupDialog } from 'views-components/groups-dialog/remove-dialog'; import { GroupAttributesDialog } from 'views-components/groups-dialog/attributes-dialog'; import { GroupDetailsPanel } from 'views/group-details-panel/group-details-panel'; @@ -221,7 +220,6 @@ export const WorkbenchPanel = -