Commit 80ac24d7 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch...

Merge branch '235603-convert-group-members-list-view-from-haml-to-vue-wire-up-role-dropdown' into 'master'

Group members Vue conversion - Wire up role dropdown with Vuex action

See merge request gitlab-org/gitlab!45227
parents 417c3e11 7402c825
import Vue from 'vue';
import Vuex from 'vuex';
import { GlToast } from '@gitlab/ui';
import { parseDataAttributes } from 'ee_else_ce/groups/members/utils';
import App from './components/app.vue';
import membersModule from '~/vuex_shared/modules/members';
......@@ -10,6 +11,7 @@ export const initGroupMembersApp = (el, tableFields, requestFormatter) => {
}
Vue.use(Vuex);
Vue.use(GlToast);
const store = new Vuex.Store({
...membersModule({
......
<script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { mapActions } from 'vuex';
import { s__ } from '~/locale';
export default {
name: 'RoleDropdown',
......@@ -17,14 +19,32 @@ export default {
data() {
return {
isDesktop: false,
busy: false,
};
},
mounted() {
this.isDesktop = bp.isDesktop();
},
methods: {
handleSelect() {
// Vuex action will be called here to make API request and update `member.accessLevel`
...mapActions(['updateMemberRole']),
handleSelect(value, name) {
if (value === this.member.accessLevel.integerValue) {
return;
}
this.busy = true;
this.updateMemberRole({
memberId: this.member.id,
accessLevel: { integerValue: value, stringValue: name },
})
.then(() => {
this.$toast.show(s__('Members|Role updated successfully.'));
this.busy = false;
})
.catch(() => {
this.busy = false;
});
},
},
};
......@@ -35,13 +55,14 @@ export default {
:right="!isDesktop"
:text="member.accessLevel.stringValue"
:header-text="__('Change permissions')"
:disabled="busy"
>
<gl-dropdown-item
v-for="(value, name) in member.validRoles"
:key="value"
is-check-item
:is-checked="value === member.accessLevel.integerValue"
@click="handleSelect"
@click="handleSelect(value, name)"
>
{{ name }}
</gl-dropdown-item>
......
......@@ -16193,6 +16193,9 @@ msgstr ""
msgid "Members|No expiration set"
msgstr ""
msgid "Members|Role updated successfully."
msgstr ""
msgid "Members|in %{time}"
msgstr ""
......
import { mount, createWrapper } from '@vue/test-utils';
import { mount, createWrapper, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { nextTick } from 'vue';
import { within } from '@testing-library/dom';
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import waitForPromises from 'helpers/wait_for_promises';
import RoleDropdown from '~/vue_shared/components/members/table/role_dropdown.vue';
import { member } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('RoleDropdown', () => {
let wrapper;
let actions;
const $toast = {
show: jest.fn(),
};
const createStore = () => {
actions = {
updateMemberRole: jest.fn(() => Promise.resolve()),
};
return new Vuex.Store({ actions });
};
const createComponent = (propsData = {}) => {
wrapper = mount(RoleDropdown, {
......@@ -15,6 +32,11 @@ describe('RoleDropdown', () => {
member,
...propsData,
},
localVue,
store: createStore(),
mocks: {
$toast,
},
});
};
......@@ -22,7 +44,11 @@ describe('RoleDropdown', () => {
const getByTextInDropdownMenu = (text, options = {}) =>
createWrapper(within(getDropdownMenu()).getByText(text, options));
const getDropdownItemByText = text =>
getByTextInDropdownMenu(text, { selector: '[role="menuitem"] p' });
createWrapper(
within(getDropdownMenu())
.getByText(text, { selector: '[role="menuitem"] p' })
.closest('[role="menuitem"]'),
);
const getCheckedDropdownItem = () =>
wrapper
.findAll(GlDropdownItem)
......@@ -55,10 +81,47 @@ describe('RoleDropdown', () => {
expect(getByTextInDropdownMenu('Change permissions').exists()).toBe(true);
});
it('sets dropdown toggle and checks selected role', async () => {
it('sets dropdown toggle and checks selected role', () => {
expect(findDropdownToggle().text()).toBe('Owner');
expect(getCheckedDropdownItem().text()).toBe('Owner');
});
describe('when dropdown item is selected', () => {
it('does nothing if the item selected was already selected', () => {
getDropdownItemByText('Owner').trigger('click');
expect(actions.updateMemberRole).not.toHaveBeenCalled();
});
it('calls `updateMemberRole` Vuex action', () => {
getDropdownItemByText('Developer').trigger('click');
expect(actions.updateMemberRole).toHaveBeenCalledWith(expect.any(Object), {
memberId: member.id,
accessLevel: { integerValue: 30, stringValue: 'Developer' },
});
});
it('displays toast when successful', async () => {
getDropdownItemByText('Developer').trigger('click');
await waitForPromises();
expect($toast.show).toHaveBeenCalledWith('Role updated successfully.');
});
it('disables dropdown while waiting for `updateMemberRole` to resolve', async () => {
getDropdownItemByText('Developer').trigger('click');
await nextTick();
expect(findDropdown().attributes('disabled')).toBe('disabled');
await waitForPromises();
expect(findDropdown().attributes('disabled')).toBeUndefined();
});
});
});
it("sets initial dropdown toggle value to member's role", () => {
......
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