Commit 0b7277b6 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '235603-convert-group-members-list-view-from-haml-to-vue-resend-button' into 'master'

Group members Vue conversion - resend invite button

See merge request gitlab-org/gitlab!44520
parents 63d40920 814b0e86
<script>
import ActionButtonGroup from './action_button_group.vue';
import RemoveMemberButton from './remove_member_button.vue';
import ResendInviteButton from './resend_invite_button.vue';
import { s__, sprintf } from '~/locale';
export default {
name: 'InviteActionButtons',
components: { ActionButtonGroup, RemoveMemberButton },
components: { ActionButtonGroup, RemoveMemberButton, ResendInviteButton },
props: {
member: {
type: Object,
......@@ -33,7 +34,9 @@ export default {
<template>
<action-button-group>
<!-- Resend button will go here -->
<div v-if="permissions.canResend" class="gl-px-1">
<resend-invite-button :member-id="member.id" />
</div>
<div v-if="permissions.canRemove" class="gl-px-1">
<remove-member-button
:member-id="member.id"
......
<script>
import { mapState } from 'vuex';
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import csrf from '~/lib/utils/csrf';
import { __ } from '~/locale';
export default {
name: 'ResendInviteButton',
csrf,
title: __('Resend invite'),
components: { GlButton },
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
memberId: {
type: Number,
required: true,
},
},
computed: {
...mapState(['memberPath']),
resendPath() {
return this.memberPath.replace(/:id$/, `${this.memberId}/resend_invite`);
},
},
};
</script>
<template>
<form :action="resendPath" method="post">
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
<gl-button
v-gl-tooltip.hover
:title="$options.title"
:aria-label="$options.title"
icon="paper-airplane"
type="submit"
/>
</form>
</template>
......@@ -41,6 +41,9 @@ export default {
canRemove() {
return this.isDirectMember && this.member.canRemove;
},
canResend() {
return Boolean(this.member.invite?.canResend);
},
},
render() {
return this.$scopedSlots.default({
......@@ -49,6 +52,7 @@ export default {
isCurrentUser: this.isCurrentUser,
permissions: {
canRemove: this.canRemove,
canResend: this.canResend,
},
});
},
......
import { shallowMount } from '@vue/test-utils';
import InviteActionButtons from '~/vue_shared/components/members/action_buttons/invite_action_buttons.vue';
import RemoveMemberButton from '~/vue_shared/components/members/action_buttons/remove_member_button.vue';
import ResendInviteButton from '~/vue_shared/components/members/action_buttons/resend_invite_button.vue';
import { invite as member } from '../mock_data';
describe('InviteActionButtons', () => {
......@@ -16,6 +17,7 @@ describe('InviteActionButtons', () => {
};
const findRemoveMemberButton = () => wrapper.find(RemoveMemberButton);
const findResendInviteButton = () => wrapper.find(ResendInviteButton);
afterEach(() => {
wrapper.destroy();
......@@ -56,4 +58,28 @@ describe('InviteActionButtons', () => {
expect(findRemoveMemberButton().exists()).toBe(false);
});
});
describe('when user has `canResend` permissions', () => {
it('renders resend invite button', () => {
createComponent({
permissions: {
canResend: true,
},
});
expect(findResendInviteButton().exists()).toBe(true);
});
});
describe('when user does not have `canResend` permissions', () => {
it('does not render resend invite button', () => {
createComponent({
permissions: {
canResend: false,
},
});
expect(findResendInviteButton().exists()).toBe(false);
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { GlButton } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import ResendInviteButton from '~/vue_shared/components/members/action_buttons/resend_invite_button.vue';
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
const localVue = createLocalVue();
localVue.use(Vuex);
describe('ResendInviteButton', () => {
let wrapper;
const createStore = (state = {}) => {
return new Vuex.Store({
state: {
memberPath: '/groups/foo-bar/-/group_members/:id',
...state,
},
});
};
const createComponent = (propsData = {}, state) => {
wrapper = shallowMount(ResendInviteButton, {
localVue,
store: createStore(state),
propsData: {
memberId: 1,
...propsData,
},
directives: {
GlTooltip: createMockDirective(),
},
});
};
const findForm = () => wrapper.find('form');
const findButton = () => findForm().find(GlButton);
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('displays a tooltip', () => {
expect(getBinding(findButton().element, 'gl-tooltip')).not.toBeUndefined();
expect(findButton().attributes('title')).toBe('Resend invite');
});
it('submits the form when button is clicked', () => {
expect(findButton().attributes('type')).toBe('submit');
});
it('displays form with correct action and inputs', () => {
expect(findForm().attributes('action')).toBe('/groups/foo-bar/-/group_members/1/resend_invite');
expect(
findForm()
.find('input[name="authenticity_token"]')
.attributes('value'),
).toBe('mock-csrf-token');
});
});
......@@ -169,5 +169,39 @@ describe('MemberList', () => {
});
});
});
describe('canResend', () => {
describe('when member type is `invite`', () => {
it('returns `true` when `canResend` is `true`', () => {
createComponent({
member: invite,
});
expect(findWrappedComponent().props('permissions').canResend).toBe(true);
});
it('returns `false` when `canResend` is `false`', () => {
createComponent({
member: {
...invite,
invite: {
...invite,
canResend: false,
},
},
});
expect(findWrappedComponent().props('permissions').canResend).toBe(false);
});
});
describe('when member type is not `invite`', () => {
it('returns `false`', () => {
createComponent({ member: memberMock });
expect(findWrappedComponent().props('permissions').canResend).toBe(false);
});
});
});
});
});
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