aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorGuillaume Peoc'h <guillaume.peoch@sonarsource.com>2022-08-31 14:33:29 +0200
committersonartech <sonartech@sonarsource.com>2022-09-02 20:02:50 +0000
commit8d93c685bdcd3a867b12020e1ebc20f12ba3a3ac (patch)
tree5e6920afb634f5f9a50b0eea6ea14ec7b0ee3fda /server/sonar-web
parent687d0b26d6355627ce5704dbc3118b66ebad85c6 (diff)
downloadsonarqube-8d93c685bdcd3a867b12020e1ebc20f12ba3a3ac.tar.gz
sonarqube-8d93c685bdcd3a867b12020e1ebc20f12ba3a3ac.zip
SONAR-17242 Add anonymize checkbox to user deactivation form
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/api/users.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx43
-rw-r--r--server/sonar-web/src/main/js/apps/users/components/__tests__/DeactivateForm-test.tsx49
3 files changed, 89 insertions, 5 deletions
diff --git a/server/sonar-web/src/main/js/api/users.ts b/server/sonar-web/src/main/js/api/users.ts
index a5154d91839..423c3b67b80 100644
--- a/server/sonar-web/src/main/js/api/users.ts
+++ b/server/sonar-web/src/main/js/api/users.ts
@@ -92,7 +92,7 @@ export function updateUser(data: {
});
}
-export function deactivateUser(data: { login: string }): Promise<User> {
+export function deactivateUser(data: { login: string; anonymize?: boolean }): Promise<User> {
return postJSON('/api/users/deactivate', data).catch(throwGlobalError);
}
diff --git a/server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx b/server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx
index 8f4b6414e0f..f0705457e0b 100644
--- a/server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx
+++ b/server/sonar-web/src/main/js/apps/users/components/DeactivateForm.tsx
@@ -18,9 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
import { deactivateUser } from '../../../api/users';
import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons';
+import Checkbox from '../../../components/controls/Checkbox';
import Modal from '../../../components/controls/Modal';
+import { Alert } from '../../../components/ui/Alert';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { UserActive } from '../../../types/users';
@@ -32,11 +35,12 @@ export interface Props {
interface State {
submitting: boolean;
+ anonymize: boolean;
}
export default class DeactivateForm extends React.PureComponent<Props, State> {
mounted = false;
- state: State = { submitting: false };
+ state: State = { submitting: false, anonymize: false };
componentDidMount() {
this.mounted = true;
@@ -46,10 +50,14 @@ export default class DeactivateForm extends React.PureComponent<Props, State> {
this.mounted = false;
}
+ handleAnonymize = (checked: boolean) => {
+ this.setState({ anonymize: checked });
+ };
+
handleDeactivate = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
this.setState({ submitting: true });
- deactivateUser({ login: this.props.user.login }).then(
+ deactivateUser({ login: this.props.user.login, anonymize: this.state.anonymize }).then(
() => {
this.props.onUpdateUsers();
this.props.onClose();
@@ -64,7 +72,7 @@ export default class DeactivateForm extends React.PureComponent<Props, State> {
render() {
const { user } = this.props;
- const { submitting } = this.state;
+ const { submitting, anonymize } = this.state;
const header = translate('users.deactivate_user');
return (
@@ -73,8 +81,35 @@ export default class DeactivateForm extends React.PureComponent<Props, State> {
<header className="modal-head">
<h2>{header}</h2>
</header>
- <div className="modal-body">
+ <div className="modal-body display-flex-column">
{translateWithParameters('users.deactivate_user.confirmation', user.name, user.login)}
+ <Checkbox
+ id="delete-user"
+ className="big-spacer-top"
+ checked={anonymize}
+ onCheck={this.handleAnonymize}>
+ <label className="little-spacer-left" htmlFor="delete-user">
+ {translate('users.delete_user')}
+ </label>
+ </Checkbox>
+ {anonymize && (
+ <Alert variant="warning" className="big-spacer-top">
+ <FormattedMessage
+ defaultMessage={translate('users.delete_user.help')}
+ id="delete-user-warning"
+ values={{
+ link: (
+ <a
+ href="/documentation/instance-administration/authentication/overview/"
+ rel="noopener noreferrer"
+ target="_blank">
+ {translate('users.delete_user.help.link')}
+ </a>
+ )
+ }}
+ />
+ </Alert>
+ )}
</div>
<footer className="modal-foot">
{submitting && <i className="spinner spacer-right" />}
diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/DeactivateForm-test.tsx b/server/sonar-web/src/main/js/apps/users/components/__tests__/DeactivateForm-test.tsx
new file mode 100644
index 00000000000..ac27a479063
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/DeactivateForm-test.tsx
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 { render, screen } from '@testing-library/react';
+import * as React from 'react';
+import { IntlProvider } from 'react-intl';
+import { deactivateUser } from '../../../../api/users';
+import { mockUser } from '../../../../helpers/testMocks';
+import { UserActive } from '../../../../types/users';
+import DeactivateForm from '../DeactivateForm';
+
+jest.mock('../../../../api/users', () => ({
+ deactivateUser: jest.fn().mockResolvedValue({})
+}));
+
+it.each([true, false])('should deactivate user with anonymize set to %s', anonymize => {
+ const user = mockUser() as UserActive;
+ renderDeactivateForm(user);
+ if (anonymize) {
+ screen.getByRole('checkbox').click();
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ }
+ screen.getByRole('button', { name: 'users.deactivate' }).click();
+ expect(deactivateUser).toBeCalledWith({ login: user.login, anonymize });
+});
+
+function renderDeactivateForm(user: UserActive) {
+ return render(
+ <IntlProvider defaultLocale="en" locale="en">
+ <DeactivateForm onClose={jest.fn()} onUpdateUsers={jest.fn()} user={user} />
+ </IntlProvider>
+ );
+}