Commit 5547886a authored by peterhegman's avatar peterhegman

Improve consistency of admin user dropdown actions

Update button variants, add modals, and improve semantics of the markup

Changelog: changed
parent a1082b70
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
const messageHtml = `
<p>${s__('AdminUsers|Reactivating a user will:')}</p>
<ul>
<li>${s__('AdminUsers|Restore user access to the account, including web, Git and API.')}</li>
</ul>
<p>${s__('AdminUsers|You can always deactivate their account again if needed.')}</p>
`;
export default {
components: {
......@@ -25,9 +35,14 @@ export default {
title: sprintf(s__('AdminUsers|Activate user %{username}?'), {
username: this.username,
}),
message: s__('AdminUsers|You can always deactivate their account again if needed.'),
okVariant: 'confirm',
okTitle: s__('AdminUsers|Activate'),
messageHtml,
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.activate,
attributes: [{ variant: 'confirm' }],
},
}),
};
},
......@@ -36,9 +51,7 @@ export default {
</script>
<template>
<div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<gl-dropdown-item>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<slot></slot>
</gl-dropdown-item>
</div>
</template>
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
const messageHtml = `
<p>${s__('AdminUsers|Approved users can:')}</p>
<ul>
<li>${s__('AdminUsers|Log in')}</li>
<li>${s__('AdminUsers|Access Git repositories')}</li>
<li>${s__('AdminUsers|Access the API')}</li>
<li>${s__('AdminUsers|Be added to groups and projects')}</li>
</ul>
`;
export default {
components: {
GlDropdownItem,
},
props: {
username: {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
},
computed: {
attributes() {
return {
'data-path': this.path,
'data-method': 'put',
'data-modal-attributes': JSON.stringify({
title: sprintf(s__('AdminUsers|Approve user %{username}?'), {
username: this.username,
}),
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.approve,
attributes: [{ variant: 'confirm', 'data-qa-selector': 'approve_user_confirm_button' }],
},
messageHtml,
}),
'data-qa-selector': 'approve_user_button',
};
},
},
};
</script>
<template>
<gl-dropdown-item :href="path" data-method="put">
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...attributes }">
<slot></slot>
</gl-dropdown-item>
</template>
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { sprintf, s__ } from '~/locale';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
const messageHtml = `
......@@ -46,8 +47,13 @@ export default {
title: sprintf(s__('AdminUsers|Ban user %{username}?'), {
username: this.username,
}),
okVariant: 'warning',
okTitle: s__('AdminUsers|Ban user'),
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.ban,
attributes: [{ variant: 'confirm' }],
},
messageHtml,
}),
};
......@@ -57,9 +63,7 @@ export default {
</script>
<template>
<div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<gl-dropdown-item>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<slot></slot>
</gl-dropdown-item>
</div>
</template>
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
const messageHtml = `
......@@ -11,6 +12,7 @@ const messageHtml = `
<li>${s__('AdminUsers|Personal projects will be left')}</li>
<li>${s__('AdminUsers|Owned groups will be left')}</li>
</ul>
<p>${s__('AdminUsers|You can always unblock their account, their data will remain intact.')}</p>
`;
export default {
......@@ -34,8 +36,13 @@ export default {
'data-method': 'put',
'data-modal-attributes': JSON.stringify({
title: sprintf(s__('AdminUsers|Block user %{username}?'), { username: this.username }),
okVariant: 'confirm',
okTitle: s__('AdminUsers|Block'),
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.block,
attributes: [{ variant: 'confirm' }],
},
messageHtml,
}),
};
......@@ -45,9 +52,7 @@ export default {
</script>
<template>
<div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<gl-dropdown-item>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<slot></slot>
</gl-dropdown-item>
</div>
</template>
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
const messageHtml = `
......@@ -16,6 +17,9 @@ const messageHtml = `
)}</li>
<li>${s__('AdminUsers|Personal projects, group and user history will be left intact')}</li>
</ul>
<p>${s__(
'AdminUsers|You can always re-activate their account, their data will remain intact.',
)}</p>
`;
export default {
......@@ -41,8 +45,13 @@ export default {
title: sprintf(s__('AdminUsers|Deactivate user %{username}?'), {
username: this.username,
}),
okVariant: 'confirm',
okTitle: s__('AdminUsers|Deactivate'),
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.deactivate,
attributes: [{ variant: 'confirm' }],
},
messageHtml,
}),
};
......@@ -52,9 +61,7 @@ export default {
</script>
<template>
<div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<gl-dropdown-item>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<slot></slot>
</gl-dropdown-item>
</div>
</template>
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
const messageHtml = `
<p>${s__('AdminUsers|Rejected users:')}</p>
<ul>
<li>${s__('AdminUsers|Cannot sign in or access instance information')}</li>
<li>${s__('AdminUsers|Will be deleted')}</li>
</ul>
<p>${sprintf(
s__(
'AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}',
),
{
link_start: `<a href="${helpPagePath('user/profile/account/delete_account', {
anchor: 'associated-records',
})}" target="_blank">`,
link_end: '</a>',
},
false,
)}</p>
`;
export default {
components: {
GlDropdownItem,
},
props: {
username: {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
},
computed: {
modalAttributes() {
return {
'data-path': this.path,
'data-method': 'delete',
'data-modal-attributes': JSON.stringify({
title: sprintf(s__('AdminUsers|Reject user %{username}?'), {
username: this.username,
}),
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.reject,
attributes: [{ variant: 'danger' }],
},
messageHtml,
}),
};
},
},
};
</script>
<template>
<gl-dropdown-item :href="path" data-method="delete">
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<slot></slot>
</gl-dropdown-item>
</template>
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
const messageHtml = `<p>${s__(
......@@ -30,8 +31,13 @@ export default {
title: sprintf(s__('AdminUsers|Unban user %{username}?'), {
username: this.username,
}),
okVariant: 'info',
okTitle: s__('AdminUsers|Unban user'),
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.unban,
attributes: [{ variant: 'confirm' }],
},
messageHtml,
}),
};
......@@ -41,9 +47,7 @@ export default {
</script>
<template>
<div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<gl-dropdown-item>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<slot></slot>
</gl-dropdown-item>
</div>
</template>
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
export default {
components: {
......@@ -24,8 +25,13 @@ export default {
'data-modal-attributes': JSON.stringify({
title: sprintf(s__('AdminUsers|Unblock user %{username}?'), { username: this.username }),
message: s__('AdminUsers|You can always block their account again if needed.'),
okVariant: 'confirm',
okTitle: s__('AdminUsers|Unblock'),
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.unblock,
attributes: [{ variant: 'confirm' }],
},
}),
};
},
......@@ -34,9 +40,7 @@ export default {
</script>
<template>
<div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<gl-dropdown-item>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<slot></slot>
</gl-dropdown-item>
</div>
</template>
<script>
import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale';
import { I18N_USER_ACTIONS } from '../../constants';
export default {
components: {
......@@ -24,8 +25,13 @@ export default {
'data-modal-attributes': JSON.stringify({
title: sprintf(s__('AdminUsers|Unlock user %{username}?'), { username: this.username }),
message: __('Are you sure?'),
okVariant: 'confirm',
okTitle: s__('AdminUsers|Unlock'),
actionCancel: {
text: __('Cancel'),
},
actionPrimary: {
text: I18N_USER_ACTIONS.unlock,
attributes: [{ variant: 'confirm' }],
},
}),
};
},
......@@ -34,9 +40,7 @@ export default {
</script>
<template>
<div class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<gl-dropdown-item>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }">
<slot></slot>
</gl-dropdown-item>
</div>
</template>
......@@ -2529,6 +2529,9 @@ msgstr ""
msgid "AdminUsers|Approve user"
msgstr ""
msgid "AdminUsers|Approve user %{username}?"
msgstr ""
msgid "AdminUsers|Approved users can:"
msgstr ""
......@@ -2694,6 +2697,9 @@ msgstr ""
msgid "AdminUsers|Reject request"
msgstr ""
msgid "AdminUsers|Reject user %{username}?"
msgstr ""
msgid "AdminUsers|Rejected users:"
msgstr ""
......@@ -2757,9 +2763,6 @@ msgstr ""
msgid "AdminUsers|Unblock user %{username}?"
msgstr ""
msgid "AdminUsers|Unlock"
msgstr ""
msgid "AdminUsers|Unlock user %{username}?"
msgstr ""
......
......@@ -39,37 +39,12 @@ describe('Action components', () => {
await nextTick();
const div = wrapper.find('div');
expect(div.attributes('data-path')).toBe('/test');
expect(div.attributes('data-modal-attributes')).toContain('John Doe');
expect(wrapper.attributes('data-path')).toBe('/test');
expect(wrapper.attributes('data-modal-attributes')).toContain('John Doe');
expect(findDropdownItem().exists()).toBe(true);
});
});
describe('LINK_ACTIONS', () => {
it.each`
action | method
${'Approve'} | ${'put'}
${'Reject'} | ${'delete'}
`(
'renders a dropdown item link with method "$method" for "$action"',
async ({ action, method }) => {
initComponent({
component: Actions[action],
props: {
path: '/test',
},
});
await nextTick();
const item = wrapper.find(GlDropdownItem);
expect(item.attributes('href')).toBe('/test');
expect(item.attributes('data-method')).toContain(method);
},
);
});
describe('DELETE_ACTION_COMPONENTS', () => {
const oncallSchedules = [{ name: 'schedule1' }, { name: 'schedule2' }];
it.each(DELETE_ACTIONS)('renders a dropdown item for "%s"', async (action) => {
......
......@@ -6,7 +6,7 @@ import { I18N_USER_ACTIONS } from '~/admin/users/constants';
import { generateUserPaths } from '~/admin/users/utils';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { CONFIRMATION_ACTIONS, DELETE_ACTIONS, LINK_ACTIONS, LDAP, EDIT } from '../constants';
import { CONFIRMATION_ACTIONS, DELETE_ACTIONS, LDAP, EDIT } from '../constants';
import { users, paths } from '../mock_data';
describe('AdminUserActions component', () => {
......@@ -62,7 +62,7 @@ describe('AdminUserActions component', () => {
describe('actions dropdown', () => {
describe('when there are actions', () => {
const actions = [EDIT, ...LINK_ACTIONS];
const actions = [EDIT, ...CONFIRMATION_ACTIONS];
beforeEach(() => {
initComponent({ actions });
......@@ -72,19 +72,6 @@ describe('AdminUserActions component', () => {
expect(findActionsDropdown().exists()).toBe(true);
});
describe('when there are actions that should render as links', () => {
beforeEach(() => {
initComponent({ actions: LINK_ACTIONS });
});
it.each(LINK_ACTIONS)('renders an action component item for "%s"', (action) => {
const component = wrapper.find(Actions[capitalizeFirstCharacter(action)]);
expect(component.props('path')).toBe(userPaths[action]);
expect(component.text()).toBe(I18N_USER_ACTIONS[action]);
});
});
describe('when there are actions that require confirmation', () => {
beforeEach(() => {
initComponent({ actions: CONFIRMATION_ACTIONS });
......
......@@ -14,8 +14,16 @@ export const EDIT = 'edit';
export const LDAP = 'ldapBlocked';
export const LINK_ACTIONS = [APPROVE, REJECT];
export const CONFIRMATION_ACTIONS = [ACTIVATE, BLOCK, DEACTIVATE, UNLOCK, UNBLOCK, BAN, UNBAN];
export const CONFIRMATION_ACTIONS = [
ACTIVATE,
BLOCK,
DEACTIVATE,
UNLOCK,
UNBLOCK,
BAN,
UNBAN,
APPROVE,
REJECT,
];
export const DELETE_ACTIONS = [DELETE, DELETE_WITH_CONTRIBUTIONS];
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment