diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2018-02-16 09:12:23 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-16 09:12:23 +0100 |
commit | 6dabceb176660fbed4195214d39c8f348fbe2c32 (patch) | |
tree | 21f322f558b69319ab7c5b2d679dbddb8d32c3c2 /server/sonar-web | |
parent | 11bd2fcdf399d62b277e9a69b7a6bc4f3ad696a8 (diff) | |
download | sonarqube-6dabceb176660fbed4195214d39c8f348fbe2c32.tar.gz sonarqube-6dabceb176660fbed4195214d39c8f348fbe2c32.zip |
rewrite parts of permission templates app in react (#3070)
Diffstat (limited to 'server/sonar-web')
25 files changed, 622 insertions, 553 deletions
diff --git a/server/sonar-web/src/main/js/api/permissions.ts b/server/sonar-web/src/main/js/api/permissions.ts index b87dbc9956d..465d4fedea3 100644 --- a/server/sonar-web/src/main/js/api/permissions.ts +++ b/server/sonar-web/src/main/js/api/permissions.ts @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { BaseSearchProjectsParameters } from './components'; +import { PermissionTemplate } from '../app/types'; +import throwGlobalError from '../app/utils/throwGlobalError'; import { getJSON, post, postJSON, RequestData } from '../helpers/request'; const PAGE_SIZE = 100; @@ -86,21 +88,6 @@ export function revokePermissionFromGroup( return post('/api/permissions/remove_group', data); } -export interface PermissionTemplate { - id: string; - name: string; - description?: string; - projectKeyPattern?: string; - createdAt: string; - updatedAt?: string; - permissions: Array<{ - key: string; - usersCount: number; - groupsCount: number; - withProjectCreator?: boolean; - }>; -} - interface GetPermissionTemplatesResponse { permissionTemplates: PermissionTemplate[]; defaultTemplates: Array<{ templateId: string; qualifier: string }>; @@ -122,8 +109,8 @@ export function updatePermissionTemplate(data: RequestData): Promise<void> { return post('/api/permissions/update_template', data); } -export function deletePermissionTemplate(data: RequestData): Promise<void> { - return post('/api/permissions/delete_template', data); +export function deletePermissionTemplate(data: RequestData) { + return post('/api/permissions/delete_template', data).catch(throwGlobalError); } /** @@ -133,8 +120,8 @@ export function setDefaultPermissionTemplate(templateId: string, qualifier: stri return post('/api/permissions/set_default_template', { templateId, qualifier }); } -export function applyTemplateToProject(data: RequestData): Promise<void> { - return post('/api/permissions/apply_template', data); +export function applyTemplateToProject(data: RequestData) { + return post('/api/permissions/apply_template', data).catch(throwGlobalError); } export function bulkApplyTemplate(data: BaseSearchProjectsParameters): Promise<void> { diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index 504e86547be..4ec38c6b961 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -99,6 +99,7 @@ export interface Component extends LightComponent { } interface ComponentConfiguration { + canApplyPermissionTemplate?: boolean; extensions?: Extension[]; showBackgroundTasks?: boolean; showLinks?: boolean; @@ -314,3 +315,19 @@ export interface CustomMeasure { value: string; updatedAt?: string; } + +export interface PermissionTemplate { + defaultFor: string[]; + id: string; + name: string; + description?: string; + projectKeyPattern?: string; + createdAt: string; + updatedAt?: string; + permissions: Array<{ + key: string; + usersCount: number; + groupsCount: number; + withProjectCreator?: boolean; + }>; +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx index e01fe8c35cd..121c008ce9a 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx @@ -17,58 +17,81 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router'; +import * as React from 'react'; +import * as PropTypes from 'prop-types'; import { difference } from 'lodash'; -import Backbone from 'backbone'; -import { PermissionTemplateType, CallbackType } from '../propTypes'; +import Form from './Form'; +import { + setDefaultPermissionTemplate, + deletePermissionTemplate, + updatePermissionTemplate +} from '../../../api/permissions'; +import { PermissionTemplate } from '../../../app/types'; import ActionsDropdown, { ActionsDropdownItem } from '../../../components/controls/ActionsDropdown'; +import ConfirmButton from '../../../components/controls/ConfirmButton'; import QualifierIcon from '../../../components/shared/QualifierIcon'; -import UpdateView from '../views/UpdateView'; -import DeleteView from '../views/DeleteView'; -import { translate } from '../../../helpers/l10n'; -import { setDefaultPermissionTemplate } from '../../../api/permissions'; - -export default class ActionsCell extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - permissionTemplate: PermissionTemplateType.isRequired, - topQualifiers: PropTypes.array.isRequired, - refresh: CallbackType, - fromDetails: PropTypes.bool - }; +import { translate, translateWithParameters } from '../../../helpers/l10n'; + +interface Props { + fromDetails?: boolean; + organization?: { isDefault?: boolean; key: string }; + permissionTemplate: PermissionTemplate; + refresh: () => void; + topQualifiers: string[]; +} - static defaultProps = { - fromDetails: false - }; +interface State { + updateModal: boolean; +} + +export default class ActionsCell extends React.PureComponent<Props, State> { + mounted = false; static contextTypes = { router: PropTypes.object }; + state: State = { updateModal: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + handleUpdateClick = () => { - new UpdateView({ - model: new Backbone.Model(this.props.permissionTemplate), - refresh: this.props.refresh - }).render(); + this.setState({ updateModal: true }); }; - handleDeleteClick = () => { - new DeleteView({ - model: new Backbone.Model(this.props.permissionTemplate) - }) - .on('done', () => { - const pathname = this.props.organization - ? `/organizations/${this.props.organization.key}/permission_templates` - : '/permission_templates'; - this.context.router.replace(pathname); - this.props.refresh(); - }) - .render(); + handleCloseUpdateModal = () => { + if (this.mounted) { + this.setState({ updateModal: false }); + } + }; + + handleSubmitUpdateModal = (data: { + description: string; + name: string; + projectKeyPattern: string; + }) => { + return updatePermissionTemplate({ id: this.props.permissionTemplate.id, ...data }).then( + this.props.refresh + ); + }; + + handleDelete = (templateId: string) => { + return deletePermissionTemplate({ templateId }).then(() => { + const pathname = this.props.organization + ? `/organizations/${this.props.organization.key}/permission_templates` + : '/permission_templates'; + this.context.router.replace(pathname); + this.props.refresh(); + }); }; - setDefault = qualifier => () => { + setDefault = (qualifier: string) => () => { setDefaultPermissionTemplate(this.props.permissionTemplate.id, qualifier).then( this.props.refresh, () => {} @@ -95,19 +118,19 @@ export default class ActionsCell extends React.PureComponent { : this.renderIfMultipleTopQualifiers(availableQualifiers); } - renderSetDefaultLink(qualifier, child) { + renderSetDefaultLink(qualifier: string, child: React.ReactNode) { return ( <ActionsDropdownItem - key={qualifier} className="js-set-default" data-qualifier={qualifier} + key={qualifier} onClick={this.setDefault(qualifier)}> {child} </ActionsDropdownItem> ); } - renderIfSingleTopQualifier(availableQualifiers) { + renderIfSingleTopQualifier(availableQualifiers: string[]) { return availableQualifiers.map(qualifier => this.renderSetDefaultLink( qualifier, @@ -116,7 +139,7 @@ export default class ActionsCell extends React.PureComponent { ); } - renderIfMultipleTopQualifiers(availableQualifiers) { + renderIfMultipleTopQualifiers(availableQualifiers: string[]) { return availableQualifiers.map(qualifier => this.renderSetDefaultLink( qualifier, @@ -148,11 +171,33 @@ export default class ActionsCell extends React.PureComponent { <ActionsDropdownItem className="js-update" onClick={this.handleUpdateClick}> {translate('update_details')} </ActionsDropdownItem> + {this.state.updateModal && ( + <Form + confirmButtonText={translate('update_verb')} + header={translate('permission_template.edit_template')} + onClose={this.handleCloseUpdateModal} + onSubmit={this.handleSubmitUpdateModal} + permissionTemplate={t} + /> + )} {t.defaultFor.length === 0 && ( - <ActionsDropdownItem className="js-delete" onClick={this.handleDeleteClick}> - {translate('delete')} - </ActionsDropdownItem> + <ConfirmButton + confirmButtonText={translate('delete')} + confirmData={t.id} + isDestructive={true} + modalBody={translateWithParameters( + 'permission_template.do_you_want_to_delete_template_xxx', + t.name + )} + modalHeader={translate('permission_template.delete_confirm_title')} + onConfirm={this.handleDelete}> + {({ onClick }) => ( + <ActionsDropdownItem className="js-delete" destructive={true} onClick={onClick}> + {translate('delete')} + </ActionsDropdownItem> + )} + </ConfirmButton> )} </ActionsDropdown> ); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx new file mode 100644 index 00000000000..3bf36d12b1d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Form.tsx @@ -0,0 +1,154 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; +import SimpleModal from '../../../components/controls/SimpleModal'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + confirmButtonText: string; + header: string; + permissionTemplate?: { description?: string; name: string; projectKeyPattern?: string }; + onClose: () => void; + onSubmit: ( + data: { description: string; name: string; projectKeyPattern: string } + ) => Promise<void>; +} + +interface State { + description: string; + name: string; + projectKeyPattern: string; +} + +export default class Form extends React.PureComponent<Props, State> { + mounted = false; + + constructor(props: Props) { + super(props); + this.state = { + description: (props.permissionTemplate && props.permissionTemplate.description) || '', + name: (props.permissionTemplate && props.permissionTemplate.name) || '', + projectKeyPattern: + (props.permissionTemplate && props.permissionTemplate.projectKeyPattern) || '' + }; + } + + handleSubmit = () => { + return this.props + .onSubmit({ + description: this.state.description, + name: this.state.name, + projectKeyPattern: this.state.projectKeyPattern + }) + .then(this.props.onClose); + }; + + handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => { + this.setState({ name: event.currentTarget.value }); + }; + + handleDescriptionChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => { + this.setState({ description: event.currentTarget.value }); + }; + + handleProjectKeyPatternChange = (event: React.ChangeEvent<HTMLInputElement>) => { + this.setState({ projectKeyPattern: event.currentTarget.value }); + }; + + render() { + return ( + <SimpleModal + header={this.props.header} + onClose={this.props.onClose} + onSubmit={this.handleSubmit}> + {({ onCloseClick, onFormSubmit, submitting }) => ( + <form id="permission-template-form" onSubmit={onFormSubmit}> + <header className="modal-head"> + <h2>{this.props.header}</h2> + </header> + + <div className="modal-body"> + <div className="modal-field"> + <label htmlFor="permission-template-name"> + {translate('name')} + <em className="mandatory">*</em> + </label> + <input + autoFocus={true} + id="permission-template-name" + maxLength={256} + name="name" + onChange={this.handleNameChange} + required={true} + type="text" + value={this.state.name} + /> + <div className="modal-field-description">{translate('should_be_unique')}</div> + </div> + + <div className="modal-field"> + <label htmlFor="permission-template-description">{translate('description')}</label> + <textarea + id="permission-template-description" + name="description" + onChange={this.handleDescriptionChange} + value={this.state.description} + /> + </div> + + <div className="modal-field"> + <label htmlFor="permission-template-project-key-pattern"> + {translate('permission_template.key_pattern')} + </label> + <input + id="permission-template-project-key-pattern" + maxLength={500} + name="projectKeyPattern" + onChange={this.handleProjectKeyPatternChange} + type="text" + value={this.state.projectKeyPattern} + /> + <div className="modal-field-description"> + {translate('permission_template.key_pattern.description')} + </div> + </div> + </div> + + <footer className="modal-foot"> + <DeferredSpinner className="spacer-right" loading={submitting} /> + <button disabled={submitting} id="permission-template-submit" type="submit"> + {this.props.confirmButtonText} + </button> + <button + className="button-link" + disabled={submitting} + id="permission-template-cancel" + onClick={onCloseClick} + type="reset"> + {translate('cancel')} + </button> + </footer> + </form> + )} + </SimpleModal> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Header.js b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.js deleted file mode 100644 index 2635bbbe8a8..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Header.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import CreateView from '../views/CreateView'; -import { translate } from '../../../helpers/l10n'; -import { CallbackType } from '../propTypes'; - -export default class Header extends React.PureComponent { - static propTypes = { - organization: PropTypes.object, - ready: PropTypes.bool.isRequired, - refresh: CallbackType - }; - - static contextTypes = { - router: PropTypes.object - }; - - componentWillMount() { - this.handleCreateClick = this.handleCreateClick.bind(this); - } - - handleCreateClick(e) { - e.preventDefault(); - const { organization } = this.props; - - new CreateView({ organization }) - .on('done', r => { - this.props.refresh().then(() => { - const pathname = organization - ? `/organizations/${organization.key}/permission_templates` - : '/permission_templates'; - this.context.router.push({ - pathname, - query: { id: r.permissionTemplate.id } - }); - }); - }) - .render(); - } - - render() { - return ( - <header id="project-permissions-header" className="page-header"> - <h1 className="page-title">{translate('permission_templates.page')}</h1> - - {!this.props.ready && <i className="spinner" />} - - <div className="page-actions"> - <button onClick={this.handleCreateClick}>{translate('create')}</button> - </div> - - <p className="page-description">{translate('permission_templates.page.description')}</p> - </header> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx new file mode 100644 index 00000000000..1db801795fe --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx @@ -0,0 +1,107 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import Form from './Form'; +import { createPermissionTemplate } from '../../../api/permissions'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + organization?: { key: string }; + ready?: boolean; + refresh: () => Promise<void>; +} + +interface State { + createModal: boolean; +} + +export default class Header extends React.PureComponent<Props, State> { + mounted = false; + + static contextTypes = { + router: PropTypes.object + }; + + state: State = { createModal: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + handleCreateClick = (event: React.SyntheticEvent<HTMLButtonElement>) => { + event.preventDefault(); + event.currentTarget.blur(); + this.setState({ createModal: true }); + }; + + handleCreateModalClose = () => { + if (this.mounted) { + this.setState({ createModal: false }); + } + }; + + handleCreateModalSubmit = (data: { + description: string; + name: string; + projectKeyPattern: string; + }) => { + const organization = this.props.organization && this.props.organization.key; + return createPermissionTemplate({ ...data, organization }).then(response => { + this.props.refresh().then(() => { + const pathname = organization + ? `/organizations/${organization}/permission_templates` + : '/permission_templates'; + this.context.router.push({ pathname, query: { id: response.permissionTemplate.id } }); + }); + }); + }; + + render() { + return ( + <header className="page-header" id="project-permissions-header"> + <h1 className="page-title">{translate('permission_templates.page')}</h1> + + {!this.props.ready && <i className="spinner" />} + + <div className="page-actions"> + <button onClick={this.handleCreateClick} type="button"> + {translate('create')} + </button> + + {this.state.createModal && ( + <Form + confirmButtonText={translate('create')} + header={translate('permission_template.new_template')} + onClose={this.handleCreateModalClose} + onSubmit={this.handleCreateModalSubmit} + /> + )} + </div> + + <p className="page-description">{translate('permission_templates.page.description')}</p> + </header> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-delete.hbs b/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-delete.hbs deleted file mode 100644 index 6cdf78a14a0..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-delete.hbs +++ /dev/null @@ -1,13 +0,0 @@ -<form id="delete-permission-template-form"> - <div class="modal-head"> - <h2>{{t 'permission_template.delete_confirm_title'}}</h2> - </div> - <div class="modal-body"> - <div class="js-modal-messages"></div> - {{tp 'permission_template.do_you_want_to_delete_template_xxx' name}} - </div> - <div class="modal-foot"> - <button id="delete-permission-template-submit" class="button-red">{{t 'delete'}}</button> - <a href="#" class="js-modal-close" id="delete-permission-template-cancel">{{t 'cancel'}}</a> - </div> -</form> diff --git a/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-form.hbs b/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-form.hbs deleted file mode 100644 index b88decef2e4..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/templates/permission-templates-form.hbs +++ /dev/null @@ -1,34 +0,0 @@ -<form id="permission-template-form" autocomplete="off"> - <div class="modal-head"> - <h2>{{#if id}}{{t 'permission_template.edit_template'}}{{else}}{{t 'permission_template.new_template'}}{{/if}}</h2> - </div> - <div class="modal-body"> - <div class="js-modal-messages"></div> - - <div class="modal-field"> - <label for="permission-template-name">{{t 'name'}}<em class="mandatory">*</em></label> - <input id="permission-template-name" name="name" type="text" maxlength="256" required value="{{name}}"> - <div class="modal-field-description"> - {{t 'should_be_unique'}} - </div> - </div> - - <div class="modal-field"> - <label for="permission-template-description">{{t 'description'}}</label> - <textarea id="permission-template-description" name="description" maxlength="4000" rows="5">{{description}}</textarea> - </div> - - <div class="modal-field"> - <label for="permission-template-project-key-pattern">{{t 'permission_template.key_pattern'}}</label> - <input id="permission-template-project-key-pattern" name="keyPattern" type="text" maxlength="500" - value="{{projectKeyPattern}}"> - <div class="modal-field-description"> - {{t 'permission_template.key_pattern.description'}} - </div> - </div> - </div> - <div class="modal-foot"> - <button id="permission-template-submit">{{#if id}}{{t 'update_verb'}}{{else}}{{t 'create'}}{{/if}}</button> - <a href="#" class="js-modal-close" id="permission-template-cancel">{{t 'cancel'}}</a> - </div> -</form> diff --git a/server/sonar-web/src/main/js/apps/permission-templates/views/CreateView.js b/server/sonar-web/src/main/js/apps/permission-templates/views/CreateView.js deleted file mode 100644 index 0e788921b2f..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/views/CreateView.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import FormView from './FormView'; -import { createPermissionTemplate } from '../../../api/permissions'; -import { parseError } from '../../../helpers/request'; - -export default FormView.extend({ - sendRequest() { - this.disableForm(); - const data = { - name: this.$('#permission-template-name').val(), - description: this.$('#permission-template-description').val(), - projectKeyPattern: this.$('#permission-template-project-key-pattern').val() - }; - if (this.options.organization) { - Object.assign(data, { organization: this.options.organization.key }); - } - createPermissionTemplate(data).then( - r => { - this.trigger('done', r); - this.destroy(); - }, - e => { - this.enableForm(); - parseError(e).then(message => this.showSingleError(message)); - } - ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/views/DeleteView.js b/server/sonar-web/src/main/js/apps/permission-templates/views/DeleteView.js deleted file mode 100644 index ea780ddf33a..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/views/DeleteView.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import ModalForm from '../../../components/common/modal-form'; -import { deletePermissionTemplate } from '../../../api/permissions'; -import Template from '../templates/permission-templates-delete.hbs'; -import { parseError } from '../../../helpers/request'; - -export default ModalForm.extend({ - template: Template, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - this.sendRequest(); - }, - - sendRequest() { - deletePermissionTemplate({ templateId: this.model.id }).then( - () => { - this.trigger('done'); - this.destroy(); - }, - e => { - this.enableForm(); - parseError(e).then(message => this.showSingleError(message)); - } - ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/views/FormView.js b/server/sonar-web/src/main/js/apps/permission-templates/views/FormView.js deleted file mode 100644 index 0c9e5d40c4d..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/views/FormView.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import ModalForm from '../../../components/common/modal-form'; -import Template from '../templates/permission-templates-form.hbs'; - -export default ModalForm.extend({ - template: Template, - - onRender() { - ModalForm.prototype.onRender.apply(this, arguments); - this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); - this.$('#create-custom-measure-metric').select2({ - width: '250px', - minimumResultsForSearch: 20 - }); - }, - - onDestroy() { - ModalForm.prototype.onDestroy.apply(this, arguments); - this.$('[data-toggle="tooltip"]').tooltip('destroy'); - }, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - this.sendRequest(); - } -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/views/UpdateView.js b/server/sonar-web/src/main/js/apps/permission-templates/views/UpdateView.js deleted file mode 100644 index 5284685848f..00000000000 --- a/server/sonar-web/src/main/js/apps/permission-templates/views/UpdateView.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import FormView from './FormView'; -import { updatePermissionTemplate } from '../../../api/permissions'; -import { parseError } from '../../../helpers/request'; - -export default FormView.extend({ - sendRequest() { - this.disableForm(); - updatePermissionTemplate({ - id: this.model.id, - name: this.$('#permission-template-name').val(), - description: this.$('#permission-template-description').val(), - projectKeyPattern: this.$('#permission-template-project-key-pattern').val() - }).then( - () => { - this.options.refresh(); - this.destroy(); - }, - e => { - this.enableForm(); - parseError(e).then(message => this.showSingleError(message)); - } - ); - } -}); diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx b/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx new file mode 100644 index 00000000000..e8550cef79a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx @@ -0,0 +1,155 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { getPermissionTemplates, applyTemplateToProject } from '../../../../api/permissions'; +import { PermissionTemplate } from '../../../../app/types'; +import DeferredSpinner from '../../../../components/common/DeferredSpinner'; +import SimpleModal from '../../../../components/controls/SimpleModal'; +import Select from '../../../../components/controls/Select'; +import { translateWithParameters, translate } from '../../../../helpers/l10n'; + +interface Props { + onApply?: () => void; + onClose: () => void; + organization: string | undefined; + project: { key: string; name: string }; +} + +interface State { + done: boolean; + loading: boolean; + permissionTemplate?: string; + permissionTemplates?: PermissionTemplate[]; +} + +export default class ApplyTemplate extends React.PureComponent<Props, State> { + mounted = false; + state: State = { done: false, loading: true }; + + componentDidMount() { + this.mounted = true; + this.fetchPermissionTemplates(); + } + + componentWillUnmount() { + this.mounted = false; + } + + fetchPermissionTemplates = () => { + getPermissionTemplates(this.props.organization).then( + ({ permissionTemplates }) => { + if (this.mounted) { + this.setState({ loading: false, permissionTemplates }); + } + }, + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } + ); + }; + + handleSubmit = () => { + if (this.state.permissionTemplate) { + return applyTemplateToProject({ + organization: this.props.organization, + projectKey: this.props.project.key, + templateId: this.state.permissionTemplate + }).then(() => { + if (this.mounted) { + if (this.props.onApply) { + this.props.onApply(); + } + this.setState({ done: true }); + } + }); + } else { + return Promise.reject(undefined); + } + }; + + handlePermissionTemplateChange = ({ value }: { value: string }) => { + this.setState({ permissionTemplate: value }); + }; + + render() { + const header = translateWithParameters( + 'projects_role.apply_template_to_xxx', + this.props.project.name + ); + + return ( + <SimpleModal header={header} onClose={this.props.onClose} onSubmit={this.handleSubmit}> + {({ onCloseClick, onFormSubmit, submitting }) => ( + <form id="project-permissions-apply-template-form" onSubmit={onFormSubmit}> + <header className="modal-head"> + <h2>{header}</h2> + </header> + + <div className="modal-body"> + {this.state.done ? ( + <div className="alert alert-success"> + {translate('projects_role.apply_template.success')} + </div> + ) : ( + <> + {this.state.loading ? ( + <i className="spinner" /> + ) : ( + <div className="modal-field"> + <label htmlFor="project-permissions-template"> + {translate('template')} + <em className="mandatory">*</em> + </label> + {this.state.permissionTemplates && ( + <Select + clearable={false} + onChange={this.handlePermissionTemplateChange} + options={this.state.permissionTemplates.map(permissionTemplate => ({ + label: permissionTemplate.name, + value: permissionTemplate.id + }))} + value={this.state.permissionTemplate} + /> + )} + </div> + )} + </> + )} + </div> + + <footer className="modal-foot"> + <DeferredSpinner className="spacer-right" loading={submitting} /> + {!this.state.done && ( + <button disabled={submitting || !this.state.permissionTemplate} type="submit"> + {translate('apply')} + </button> + )} + <button className="button-link" onClick={onCloseClick} type="reset"> + {translate(this.state.done ? 'close' : 'cancel')} + </button> + </footer> + </form> + )} + </SimpleModal> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.js b/server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.tsx index 02eed6c9310..bab1ba6490d 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.js +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/PageHeader.tsx @@ -17,43 +17,48 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; +import ApplyTemplate from './ApplyTemplate'; +import { Component } from '../../../../app/types'; import { translate } from '../../../../helpers/l10n'; -import ApplyTemplateView from '../views/ApplyTemplateView'; -/*:: -type Props = {| - component: { - configuration?: { - canApplyPermissionTemplate: boolean, - canUpdateProjectVisibilityToPrivate: boolean - }, - key: string, - qualifier: string, - visibility: string - }, - loadHolders: () => void, - loading: boolean -|}; -*/ +interface Props { + component: Component; + loadHolders: () => void; + loading: boolean; +} + +interface State { + applyTemplateModal: boolean; +} + +export default class PageHeader extends React.PureComponent<Props, State> { + mounted = false; + state: State = { applyTemplateModal: false }; + + componentDidMount() { + this.mounted = true; + } -export default class PageHeader extends React.PureComponent { - /*:: props: Props; */ + componentWillUnmount() { + this.mounted = false; + } - handleApplyTemplate = (e /*: Event & { target: HTMLButtonElement } */) => { - e.preventDefault(); - e.target.blur(); - const { component, loadHolders } = this.props; - const organization = component.organization ? { key: component.organization } : null; - new ApplyTemplateView({ project: component, organization }) - .on('done', () => loadHolders()) - .render(); + handleApplyTemplate = (event: React.SyntheticEvent<HTMLButtonElement>) => { + event.preventDefault(); + event.currentTarget.blur(); + this.setState({ applyTemplateModal: true }); + }; + + handleApplyTemplateClose = () => { + if (this.mounted) { + this.setState({ applyTemplateModal: false }); + } }; render() { const { component } = this.props; - const configuration = component.configuration; + const { configuration } = component; const canApplyPermissionTemplate = configuration != null && configuration.canApplyPermissionTemplate; @@ -62,7 +67,7 @@ export default class PageHeader extends React.PureComponent { : translate('roles.page.description2'); const visibilityDescription = - component.qualifier === 'TRK' + component.qualifier === 'TRK' && component.visibility ? translate('visibility', component.visibility, 'description') : null; @@ -74,9 +79,18 @@ export default class PageHeader extends React.PureComponent { {canApplyPermissionTemplate && ( <div className="page-actions"> - <button className="js-apply-template" onClick={this.handleApplyTemplate}> + <button className="js-apply-template" onClick={this.handleApplyTemplate} type="button"> {translate('projects_role.apply_template')} </button> + + {this.state.applyTemplateModal && ( + <ApplyTemplate + onApply={this.props.loadHolders} + onClose={this.handleApplyTemplateClose} + organization={component.organization} + project={component} + /> + )} </div> )} diff --git a/server/sonar-web/src/main/js/apps/permissions/project/templates/ApplyTemplateTemplate.hbs b/server/sonar-web/src/main/js/apps/permissions/project/templates/ApplyTemplateTemplate.hbs deleted file mode 100644 index 37e8ff307e5..00000000000 --- a/server/sonar-web/src/main/js/apps/permissions/project/templates/ApplyTemplateTemplate.hbs +++ /dev/null @@ -1,41 +0,0 @@ -<form id="project-permissions-apply-template-form" autocomplete="off"> - <div class="modal-head"> - <h2>{{tp 'projects_role.apply_template_to_xxx' project.name}}</h2> - </div> - - <div class="modal-body"> - <div class="js-modal-messages"></div> - - {{#if done}} - <div class="alert alert-success"> - {{t 'projects_role.apply_template.success'}} - </div> - {{/if}} - - {{#unless done}} - {{#notNull permissionTemplates}} - <div class="modal-field"> - <label for="project-permissions-template"> - {{t 'template'}}<em class="mandatory">*</em> - </label> - <select id="project-permissions-template"> - {{#each permissionTemplates}} - <option value="{{id}}">{{name}}</option> - {{/each}} - </select> - </div> - {{else}} - <i class="spinner"></i> - {{/notNull}} - {{/unless}} - </div> - - <div class="modal-foot"> - {{#unless done}} - {{#notNull permissionTemplates}} - <button id="project-permissions-apply-template">{{t 'apply'}}</button> - {{/notNull}} - {{/unless}} - <a href="#" class="js-modal-close">{{t 'close'}}</a> - </div> -</form> diff --git a/server/sonar-web/src/main/js/apps/permissions/project/views/ApplyTemplateView.js b/server/sonar-web/src/main/js/apps/permissions/project/views/ApplyTemplateView.js deleted file mode 100644 index 6bba0c09b5b..00000000000 --- a/server/sonar-web/src/main/js/apps/permissions/project/views/ApplyTemplateView.js +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import ModalForm from '../../../../components/common/modal-form'; -import { applyTemplateToProject, getPermissionTemplates } from '../../../../api/permissions'; -import Template from '../templates/ApplyTemplateTemplate.hbs'; - -export default ModalForm.extend({ - template: Template, - - initialize() { - this.loadPermissionTemplates(); - this.done = false; - }, - - loadPermissionTemplates() { - return getPermissionTemplates(this.options.organization.key).then(r => { - this.permissionTemplates = r.permissionTemplates; - this.render(); - }); - }, - - onRender() { - ModalForm.prototype.onRender.apply(this, arguments); - this.$('#project-permissions-template').select2({ - width: '250px', - minimumResultsForSearch: 20 - }); - }, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - const permissionTemplate = this.$('#project-permissions-template').val(); - this.disableForm(); - - const data = { - organization: this.options.organization.key, - projectKey: this.options.project.key, - templateId: permissionTemplate - }; - applyTemplateToProject(data) - .then(() => { - this.trigger('done'); - this.done = true; - this.render(); - }) - .catch(function(e) { - e.response.json().then(r => { - this.showErrors(r.errors, r.warnings); - this.enableForm(); - }); - }); - }, - - serializeData() { - return { - permissionTemplates: this.permissionTemplates, - project: this.options.project, - done: this.done - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx index 726b4f69709..69bb55161c7 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx @@ -18,11 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { - getPermissionTemplates, - bulkApplyTemplate, - PermissionTemplate -} from '../../api/permissions'; +import { getPermissionTemplates, bulkApplyTemplate } from '../../api/permissions'; +import { PermissionTemplate } from '../../app/types'; import { translate, translateWithParameters } from '../../helpers/l10n'; import AlertWarnIcon from '../../components/icons-components/AlertWarnIcon'; import Modal from '../../components/controls/Modal'; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx index cea505f986d..72e658c8727 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx @@ -29,8 +29,8 @@ import DateTooltipFormatter from '../../components/intl/DateTooltipFormatter'; interface Props { currentUser: { login: string }; - onApplyTemplate: (project: Project) => void; onProjectCheck: (project: Project, checked: boolean) => void; + organization: string | undefined; project: Project; selected: boolean; } @@ -51,8 +51,8 @@ export default class ProjectRow extends React.PureComponent<Props> { <td className="nowrap"> <Link - to={{ pathname: '/dashboard', query: { id: project.key } }} - className="link-with-icon"> + className="link-with-icon" + to={{ pathname: '/dashboard', query: { id: project.key } }}> <QualifierIcon qualifier={project.qualifier} /> <span>{project.name}</span> </Link> </td> @@ -78,7 +78,7 @@ export default class ProjectRow extends React.PureComponent<Props> { <td className="thin nowrap"> <ProjectRowActions currentUser={this.props.currentUser} - onApplyTemplate={this.props.onApplyTemplate} + organization={this.props.organization} project={project} /> </td> diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx index 7f188592892..b43fd2d96fa 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx @@ -20,6 +20,7 @@ import * as React from 'react'; import RestoreAccessModal from './RestoreAccessModal'; import { Project } from './utils'; +import ApplyTemplate from '../permissions/project/components/ApplyTemplate'; import { getComponentShow } from '../../api/components'; import { getComponentNavigation } from '../../api/nav'; import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/ActionsDropdown'; @@ -28,11 +29,12 @@ import { getComponentPermissionsUrl } from '../../helpers/urls'; export interface Props { currentUser: { login: string }; - onApplyTemplate: (project: Project) => void; + organization: string | undefined; project: Project; } interface State { + applyTemplateModal: boolean; hasAccess?: boolean; loading: boolean; restoreAccessModal: boolean; @@ -40,7 +42,7 @@ interface State { export default class ProjectRowActions extends React.PureComponent<Props, State> { mounted = false; - state: State = { loading: false, restoreAccessModal: false }; + state: State = { applyTemplateModal: false, loading: false, restoreAccessModal: false }; componentDidMount() { this.mounted = true; @@ -81,7 +83,13 @@ export default class ProjectRowActions extends React.PureComponent<Props, State> }; handleApplyTemplateClick = () => { - this.props.onApplyTemplate(this.props.project); + this.setState({ applyTemplateModal: true }); + }; + + handleApplyTemplateClose = () => { + if (this.mounted) { + this.setState({ applyTemplateModal: false }); + } }; handleRestoreAccessClick = () => { @@ -125,6 +133,14 @@ export default class ProjectRowActions extends React.PureComponent<Props, State> project={this.props.project} /> )} + + {this.state.applyTemplateModal && ( + <ApplyTemplate + onClose={this.handleApplyTemplateClose} + organization={this.props.organization} + project={this.props.project} + /> + )} </ActionsDropdown> ); } diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx index b0872d7532b..7e72fb9b057 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/Projects.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import * as classNames from 'classnames'; import ProjectRow from './ProjectRow'; import { Project } from './utils'; -import ApplyTemplateView from '../permissions/project/views/ApplyTemplateView'; import { Organization } from '../../app/types'; import { translate } from '../../helpers/l10n'; @@ -44,10 +43,6 @@ export default class Projects extends React.PureComponent<Props> { } }; - handleApplyTemplate = (project: Project) => { - new ApplyTemplateView({ project, organization: this.props.organization }).render(); - }; - render() { return ( <div className="boxed-group boxed-group-inner"> @@ -69,8 +64,8 @@ export default class Projects extends React.PureComponent<Props> { <ProjectRow currentUser={this.props.currentUser} key={project.key} - onApplyTemplate={this.handleApplyTemplate} onProjectCheck={this.onProjectCheck} + organization={this.props.organization && this.props.organization.key} project={project} selected={this.props.selection.includes(project.key)} /> diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx index f9a5eee8406..4c9e74dc1b1 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx @@ -54,17 +54,16 @@ it('restores access', async () => { }); it('applies permission template', () => { - const onApplyTemplate = jest.fn(); - const wrapper = shallowRender({ onApplyTemplate }); + const wrapper = shallowRender(); click(wrapper.find('.js-apply-template')); - expect(onApplyTemplate).toBeCalledWith(project); + expect(wrapper.find('ApplyTemplate')).toMatchSnapshot(); }); function shallowRender(props: Partial<Props> = {}) { const wrapper = shallow( <ProjectRowActions currentUser={{ login: 'admin' }} - onApplyTemplate={jest.fn()} + organization="org" project={project} {...props} /> diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx index 76c6be42713..25b2a6ac78d 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Projects-test.tsx @@ -17,13 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -/* eslint-disable import/first */ -jest.mock('../../permissions/project/views/ApplyTemplateView'); - import * as React from 'react'; import { shallow } from 'enzyme'; import Projects from '../Projects'; -import ApplyTemplateView from '../../permissions/project/views/ApplyTemplateView'; import { Visibility } from '../../../app/types'; const organization = { key: 'org', name: 'org', projectVisibility: 'public' }; @@ -55,15 +51,6 @@ it('selects and deselects project', () => { expect(onProjectDeselected).toBeCalledWith('a'); }); -it('opens modal to apply permission template', () => { - const wrapper = shallowRender({ projects }); - wrapper - .find('ProjectRow') - .first() - .prop<Function>('onApplyTemplate')(projects[0]); - expect(ApplyTemplateView).toBeCalledWith({ organization, project: projects[0] }); -}); - function shallowRender(props?: any) { return shallow( <Projects diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap index 7c62905a04c..6d05b0406f7 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap @@ -70,7 +70,6 @@ exports[`renders 1`] = ` "login": "foo", } } - onApplyTemplate={[MockFunction]} project={ Object { "key": "project", @@ -152,7 +151,6 @@ exports[`renders 2`] = ` "login": "foo", } } - onApplyTemplate={[MockFunction]} project={ Object { "key": "project", diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap index d2737d58194..b824855ce96 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRowActions-test.tsx.snap @@ -1,5 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`applies permission template 1`] = ` +<ApplyTemplate + onClose={[Function]} + organization="org" + project={ + Object { + "id": "", + "key": "project", + "name": "Project", + "organization": "org", + "qualifier": "TRK", + "visibility": "private", + } + } +/> +`; + exports[`restores access 1`] = ` <ActionsDropdown onToggleClick={[Function]} diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap index 1503fed2c4e..678979cf56a 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Projects-test.tsx.snap @@ -34,8 +34,8 @@ exports[`renders list of projects 1`] = ` } } key="a" - onApplyTemplate={[Function]} onProjectCheck={[Function]} + organization="org" project={ Object { "key": "a", @@ -53,8 +53,8 @@ exports[`renders list of projects 1`] = ` } } key="b" - onApplyTemplate={[Function]} onProjectCheck={[Function]} + organization="org" project={ Object { "key": "b", |