Commit 5332de96 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Merge branch 'pairing-good-stuff' into 'master'

Refactor confirm modal to work without querySelector

See merge request gitlab-org/gitlab!73848
parents 568cab5c 17dd37c6
<script> <script>
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922 // TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
...@@ -26,16 +27,15 @@ export default { ...@@ -26,16 +27,15 @@ export default {
required: true, required: true,
}, },
}, },
computed: { methods: {
modalAttributes() { onClick() {
return { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-path': this.path, path: this.path,
'data-method': 'put', method: 'put',
'data-modal-attributes': JSON.stringify({ modalAttributes: {
title: sprintf(s__('AdminUsers|Activate user %{username}?'), { title: sprintf(s__('AdminUsers|Activate user %{username}?'), {
username: this.username, username: this.username,
}), }),
messageHtml,
actionCancel: { actionCancel: {
text: __('Cancel'), text: __('Cancel'),
}, },
...@@ -43,15 +43,16 @@ export default { ...@@ -43,15 +43,16 @@ export default {
text: I18N_USER_ACTIONS.activate, text: I18N_USER_ACTIONS.activate,
attributes: [{ variant: 'confirm' }], attributes: [{ variant: 'confirm' }],
}, },
}), messageHtml,
}; },
});
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
<script> <script>
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922 // TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
...@@ -28,12 +29,12 @@ export default { ...@@ -28,12 +29,12 @@ export default {
required: true, required: true,
}, },
}, },
computed: { methods: {
attributes() { onClick() {
return { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-path': this.path, path: this.path,
'data-method': 'put', method: 'put',
'data-modal-attributes': JSON.stringify({ modalAttributes: {
title: sprintf(s__('AdminUsers|Approve user %{username}?'), { title: sprintf(s__('AdminUsers|Approve user %{username}?'), {
username: this.username, username: this.username,
}), }),
...@@ -45,16 +46,16 @@ export default { ...@@ -45,16 +46,16 @@ export default {
attributes: [{ variant: 'confirm', 'data-qa-selector': 'approve_user_confirm_button' }], attributes: [{ variant: 'confirm', 'data-qa-selector': 'approve_user_confirm_button' }],
}, },
messageHtml, messageHtml,
}),
'data-qa-selector': 'approve_user_button', 'data-qa-selector': 'approve_user_button',
}; },
});
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...attributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922 // TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
...@@ -39,12 +40,12 @@ export default { ...@@ -39,12 +40,12 @@ export default {
required: true, required: true,
}, },
}, },
computed: { methods: {
modalAttributes() { onClick() {
return { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-path': this.path, path: this.path,
'data-method': 'put', method: 'put',
'data-modal-attributes': JSON.stringify({ modalAttributes: {
title: sprintf(s__('AdminUsers|Ban user %{username}?'), { title: sprintf(s__('AdminUsers|Ban user %{username}?'), {
username: this.username, username: this.username,
}), }),
...@@ -56,15 +57,15 @@ export default { ...@@ -56,15 +57,15 @@ export default {
attributes: [{ variant: 'confirm' }], attributes: [{ variant: 'confirm' }],
}, },
messageHtml, messageHtml,
}), },
}; });
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
<script> <script>
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922 // TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
...@@ -29,12 +30,12 @@ export default { ...@@ -29,12 +30,12 @@ export default {
required: true, required: true,
}, },
}, },
computed: { methods: {
modalAttributes() { onClick() {
return { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-path': this.path, path: this.path,
'data-method': 'put', method: 'put',
'data-modal-attributes': JSON.stringify({ modalAttributes: {
title: sprintf(s__('AdminUsers|Block user %{username}?'), { username: this.username }), title: sprintf(s__('AdminUsers|Block user %{username}?'), { username: this.username }),
actionCancel: { actionCancel: {
text: __('Cancel'), text: __('Cancel'),
...@@ -44,15 +45,15 @@ export default { ...@@ -44,15 +45,15 @@ export default {
attributes: [{ variant: 'confirm' }], attributes: [{ variant: 'confirm' }],
}, },
messageHtml, messageHtml,
}), },
}; });
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
<script> <script>
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922 // TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
...@@ -36,12 +37,12 @@ export default { ...@@ -36,12 +37,12 @@ export default {
required: true, required: true,
}, },
}, },
computed: { methods: {
modalAttributes() { onClick() {
return { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-path': this.path, path: this.path,
'data-method': 'put', method: 'put',
'data-modal-attributes': JSON.stringify({ modalAttributes: {
title: sprintf(s__('AdminUsers|Deactivate user %{username}?'), { title: sprintf(s__('AdminUsers|Deactivate user %{username}?'), {
username: this.username, username: this.username,
}), }),
...@@ -53,15 +54,15 @@ export default { ...@@ -53,15 +54,15 @@ export default {
attributes: [{ variant: 'confirm' }], attributes: [{ variant: 'confirm' }],
}, },
messageHtml, messageHtml,
}), },
}; });
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922 // TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
...@@ -39,12 +40,12 @@ export default { ...@@ -39,12 +40,12 @@ export default {
required: true, required: true,
}, },
}, },
computed: { methods: {
modalAttributes() { onClick() {
return { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-path': this.path, path: this.path,
'data-method': 'delete', method: 'delete',
'data-modal-attributes': JSON.stringify({ modalAttributes: {
title: sprintf(s__('AdminUsers|Reject user %{username}?'), { title: sprintf(s__('AdminUsers|Reject user %{username}?'), {
username: this.username, username: this.username,
}), }),
...@@ -56,15 +57,15 @@ export default { ...@@ -56,15 +57,15 @@ export default {
attributes: [{ variant: 'danger' }], attributes: [{ variant: 'danger' }],
}, },
messageHtml, messageHtml,
}), },
}; });
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
<script> <script>
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
// TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922 // TODO: To be replaced with <template> content in https://gitlab.com/gitlab-org/gitlab/-/issues/320922
...@@ -22,12 +23,12 @@ export default { ...@@ -22,12 +23,12 @@ export default {
required: true, required: true,
}, },
}, },
computed: { methods: {
modalAttributes() { onClick() {
return { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-path': this.path, path: this.path,
'data-method': 'put', method: 'put',
'data-modal-attributes': JSON.stringify({ modalAttributes: {
title: sprintf(s__('AdminUsers|Unban user %{username}?'), { title: sprintf(s__('AdminUsers|Unban user %{username}?'), {
username: this.username, username: this.username,
}), }),
...@@ -39,15 +40,15 @@ export default { ...@@ -39,15 +40,15 @@ export default {
attributes: [{ variant: 'confirm' }], attributes: [{ variant: 'confirm' }],
}, },
messageHtml, messageHtml,
}), },
}; });
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
<script> <script>
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
export default { export default {
...@@ -17,12 +18,13 @@ export default { ...@@ -17,12 +18,13 @@ export default {
required: true, required: true,
}, },
}, },
computed: {
modalAttributes() { methods: {
return { onClick() {
'data-path': this.path, eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-method': 'put', path: this.path,
'data-modal-attributes': JSON.stringify({ method: 'put',
modalAttributes: {
title: sprintf(s__('AdminUsers|Unblock user %{username}?'), { username: this.username }), title: sprintf(s__('AdminUsers|Unblock user %{username}?'), { username: this.username }),
message: s__('AdminUsers|You can always block their account again if needed.'), message: s__('AdminUsers|You can always block their account again if needed.'),
actionCancel: { actionCancel: {
...@@ -32,15 +34,15 @@ export default { ...@@ -32,15 +34,15 @@ export default {
text: I18N_USER_ACTIONS.unblock, text: I18N_USER_ACTIONS.unblock,
attributes: [{ variant: 'confirm' }], attributes: [{ variant: 'confirm' }],
}, },
}), },
}; });
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
<script> <script>
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale'; import { sprintf, s__, __ } from '~/locale';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import { I18N_USER_ACTIONS } from '../../constants'; import { I18N_USER_ACTIONS } from '../../constants';
export default { export default {
...@@ -17,12 +18,12 @@ export default { ...@@ -17,12 +18,12 @@ export default {
required: true, required: true,
}, },
}, },
computed: { methods: {
modalAttributes() { onClick() {
return { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, {
'data-path': this.path, path: this.path,
'data-method': 'put', method: 'put',
'data-modal-attributes': JSON.stringify({ modalAttributes: {
title: sprintf(s__('AdminUsers|Unlock user %{username}?'), { username: this.username }), title: sprintf(s__('AdminUsers|Unlock user %{username}?'), { username: this.username }),
message: __('Are you sure?'), message: __('Are you sure?'),
actionCancel: { actionCancel: {
...@@ -32,15 +33,15 @@ export default { ...@@ -32,15 +33,15 @@ export default {
text: I18N_USER_ACTIONS.unlock, text: I18N_USER_ACTIONS.unlock,
attributes: [{ variant: 'confirm' }], attributes: [{ variant: 'confirm' }],
}, },
}), },
}; });
}, },
}, },
}; };
</script> </script>
<template> <template>
<gl-dropdown-item button-class="js-confirm-modal-button" v-bind="{ ...modalAttributes }"> <gl-dropdown-item @click="onClick">
<slot></slot> <slot></slot>
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
...@@ -2,10 +2,13 @@ ...@@ -2,10 +2,13 @@
import { GlModal, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import { GlModal, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from './confirm_modal_eventhub';
import DomElementListener from './dom_element_listener.vue';
export default { export default {
components: { components: {
GlModal, GlModal,
DomElementListener,
}, },
directives: { directives: {
SafeHtml, SafeHtml,
...@@ -30,18 +33,35 @@ export default { ...@@ -30,18 +33,35 @@ export default {
}; };
}, },
mounted() { mounted() {
document.querySelectorAll(this.selector).forEach((button) => { eventHub.$on(EVENT_OPEN_CONFIRM_MODAL, this.onOpenEvent);
button.addEventListener('click', (e) => { },
e.preventDefault(); destroyed() {
eventHub.$off(EVENT_OPEN_CONFIRM_MODAL, this.onOpenEvent);
},
methods: {
onButtonPress(e) {
const element = e.currentTarget;
this.path = button.dataset.path; if (!element.dataset.path) {
this.method = button.dataset.method; return;
this.modalAttributes = JSON.parse(button.dataset.modalAttributes); }
this.openModal();
}); const modalAttributes = element.dataset.modalAttributes
? JSON.parse(element.dataset.modalAttributes)
: {};
this.onOpenEvent({
path: element.dataset.path,
method: element.dataset.method,
modalAttributes,
}); });
}, },
methods: { onOpenEvent({ path, method, modalAttributes }) {
this.path = path;
this.method = method;
this.modalAttributes = modalAttributes;
this.openModal();
},
openModal() { openModal() {
this.$refs.modal.show(); this.$refs.modal.show();
}, },
...@@ -61,6 +81,7 @@ export default { ...@@ -61,6 +81,7 @@ export default {
</script> </script>
<template> <template>
<dom-element-listener :selector="selector" @click.prevent="onButtonPress">
<gl-modal <gl-modal
ref="modal" ref="modal"
:modal-id="modalId" :modal-id="modalId"
...@@ -78,4 +99,5 @@ export default { ...@@ -78,4 +99,5 @@ export default {
<div v-else>{{ modalAttributes.message }}</div> <div v-else>{{ modalAttributes.message }}</div>
</form> </form>
</gl-modal> </gl-modal>
</dom-element-listener>
</template> </template>
import createEventHub from '~/helpers/event_hub_factory';
export default createEventHub();
export const EVENT_OPEN_CONFIRM_MODAL = Symbol('OPEN');
<script>
export default {
props: {
selector: {
type: String,
required: true,
},
},
mounted() {
this.disposables = Array.from(document.querySelectorAll(this.selector)).flatMap((button) => {
return Object.entries(this.$listeners).map(([key, value]) => {
button.addEventListener(key, value);
return () => {
button.removeEventListener(key, value);
};
});
});
},
destroyed() {
this.disposables.forEach((x) => {
x();
});
},
render() {
return this.$slots.default;
},
};
</script>
import { GlDropdownItem } from '@gitlab/ui'; import { GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { kebabCase } from 'lodash';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { kebabCase } from 'lodash';
import Actions from '~/admin/users/components/actions'; import Actions from '~/admin/users/components/actions';
import SharedDeleteAction from '~/admin/users/components/actions/shared/shared_delete_action.vue'; import SharedDeleteAction from '~/admin/users/components/actions/shared/shared_delete_action.vue';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
...@@ -39,9 +39,6 @@ describe('Action components', () => { ...@@ -39,9 +39,6 @@ describe('Action components', () => {
}); });
await nextTick(); await nextTick();
expect(wrapper.attributes('data-path')).toBe('/test');
expect(wrapper.attributes('data-modal-attributes')).toContain('John Doe');
expect(findDropdownItem().exists()).toBe(true); expect(findDropdownItem().exists()).toBe(true);
}); });
}); });
...@@ -66,7 +63,6 @@ describe('Action components', () => { ...@@ -66,7 +63,6 @@ describe('Action components', () => {
}); });
await nextTick(); await nextTick();
const sharedAction = wrapper.find(SharedDeleteAction); const sharedAction = wrapper.find(SharedDeleteAction);
expect(sharedAction.attributes('data-block-user-url')).toBe(paths.block); expect(sharedAction.attributes('data-block-user-url')).toBe(paths.block);
...@@ -76,6 +72,7 @@ describe('Action components', () => { ...@@ -76,6 +72,7 @@ describe('Action components', () => {
expect(sharedAction.attributes('data-user-deletion-obstacles')).toBe( expect(sharedAction.attributes('data-user-deletion-obstacles')).toBe(
JSON.stringify(userDeletionObstacles), JSON.stringify(userDeletionObstacles),
); );
expect(findDropdownItem().exists()).toBe(true); expect(findDropdownItem().exists()).toBe(true);
}, },
); );
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { merge } from 'lodash';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from '~/vue_shared/components/confirm_modal_eventhub';
import ConfirmModal from '~/vue_shared/components/confirm_modal.vue'; import ConfirmModal from '~/vue_shared/components/confirm_modal.vue';
import DomElementListener from '~/vue_shared/components/dom_element_listener.vue';
jest.mock('~/lib/utils/csrf', () => ({ token: 'test-csrf-token' })); jest.mock('~/lib/utils/csrf', () => ({ token: 'test-csrf-token' }));
...@@ -54,12 +57,50 @@ describe('vue_shared/components/confirm_modal', () => { ...@@ -54,12 +57,50 @@ describe('vue_shared/components/confirm_modal', () => {
findForm() findForm()
.findAll('input') .findAll('input')
.wrappers.map((x) => ({ name: x.attributes('name'), value: x.attributes('value') })); .wrappers.map((x) => ({ name: x.attributes('name'), value: x.attributes('value') }));
const findDomElementListener = () => wrapper.find(DomElementListener);
const triggerOpenWithEventHub = (modalData) => {
eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, modalData);
};
const triggerOpenWithDomListener = (modalData) => {
const element = document.createElement('button');
element.dataset.path = modalData.path;
element.dataset.method = modalData.method;
element.dataset.modalAttributes = JSON.stringify(modalData.modalAttributes);
findDomElementListener().vm.$emit('click', {
preventDefault: jest.fn(),
currentTarget: element,
});
};
describe('default', () => {
beforeEach(() => {
createComponent();
});
it('renders empty GlModal', () => {
expect(findModal().props()).toEqual({});
});
it('renders form missing values', () => {
expect(findForm().attributes('action')).toBe('');
expect(findFormData()).toEqual([
{ name: '_method', value: undefined },
{ name: 'authenticity_token', value: 'test-csrf-token' },
]);
});
});
describe('template', () => { describe('template', () => {
describe('when modal data is set', () => { describe.each`
desc | trigger
${'when opened from eventhub'} | ${triggerOpenWithEventHub}
${'when opened from dom listener'} | ${triggerOpenWithDomListener}
`('$desc', ({ trigger }) => {
beforeEach(() => { beforeEach(() => {
createComponent(); createComponent();
wrapper.vm.modalAttributes = MOCK_MODAL_DATA.modalAttributes; trigger(MOCK_MODAL_DATA);
}); });
it('renders GlModal with data', () => { it('renders GlModal with data', () => {
...@@ -71,6 +112,14 @@ describe('vue_shared/components/confirm_modal', () => { ...@@ -71,6 +112,14 @@ describe('vue_shared/components/confirm_modal', () => {
}), }),
); );
}); });
it('renders form', () => {
expect(findForm().attributes('action')).toBe(MOCK_MODAL_DATA.path);
expect(findFormData()).toEqual([
{ name: '_method', value: MOCK_MODAL_DATA.method },
{ name: 'authenticity_token', value: 'test-csrf-token' },
]);
});
}); });
describe.each` describe.each`
...@@ -79,11 +128,10 @@ describe('vue_shared/components/confirm_modal', () => { ...@@ -79,11 +128,10 @@ describe('vue_shared/components/confirm_modal', () => {
${'when message has html'} | ${{ messageHtml: '<p>Header</p><ul onhover="alert(1)"><li>First</li></ul>' }} | ${'<p>Header</p><ul><li>First</li></ul>'} ${'when message has html'} | ${{ messageHtml: '<p>Header</p><ul onhover="alert(1)"><li>First</li></ul>' }} | ${'<p>Header</p><ul><li>First</li></ul>'}
`('$desc', ({ attrs, expectation }) => { `('$desc', ({ attrs, expectation }) => {
beforeEach(() => { beforeEach(() => {
const modalData = merge({ ...MOCK_MODAL_DATA }, { modalAttributes: attrs });
createComponent(); createComponent();
wrapper.vm.modalAttributes = { triggerOpenWithEventHub(modalData);
...MOCK_MODAL_DATA.modalAttributes,
...attrs,
};
}); });
it('renders message', () => { it('renders message', () => {
...@@ -96,8 +144,7 @@ describe('vue_shared/components/confirm_modal', () => { ...@@ -96,8 +144,7 @@ describe('vue_shared/components/confirm_modal', () => {
describe('submitModal', () => { describe('submitModal', () => {
beforeEach(() => { beforeEach(() => {
createComponent(); createComponent();
wrapper.vm.path = MOCK_MODAL_DATA.path; triggerOpenWithEventHub(MOCK_MODAL_DATA);
wrapper.vm.method = MOCK_MODAL_DATA.method;
}); });
it('does not submit form', () => { it('does not submit form', () => {
......
import { mount } from '@vue/test-utils';
import { setHTMLFixture } from 'helpers/fixtures';
import DomElementListener from '~/vue_shared/components/dom_element_listener.vue';
const DEFAULT_SLOT_CONTENT = 'Default slot content';
const SELECTOR = '.js-test-include';
const HTML = `
<div>
<button class="js-test-include" data-testid="lorem">Lorem</button>
<button class="js-test-include" data-testid="ipsum">Ipsum</button>
<button data-testid="hello">Hello</a>
</div>
`;
describe('~/vue_shared/components/dom_element_listener.vue', () => {
let wrapper;
let spies;
const createComponent = () => {
wrapper = mount(DomElementListener, {
propsData: {
selector: SELECTOR,
},
listeners: spies,
slots: {
default: DEFAULT_SLOT_CONTENT,
},
});
};
const findElement = (testId) => document.querySelector(`[data-testid="${testId}"]`);
const spiesCallCount = () =>
Object.values(spies)
.map((x) => x.mock.calls.length)
.reduce((a, b) => a + b);
beforeEach(() => {
setHTMLFixture(HTML);
spies = {
click: jest.fn(),
focus: jest.fn(),
};
});
afterEach(() => {
wrapper.destroy();
});
describe('default', () => {
beforeEach(() => {
createComponent();
});
it('renders default slot', () => {
expect(wrapper.text()).toBe(DEFAULT_SLOT_CONTENT);
});
it('does not initially trigger listeners', () => {
expect(spiesCallCount()).toBe(0);
});
describe.each`
event | testId
${'click'} | ${'lorem'}
${'focus'} | ${'ipsum'}
`(
'when matching element triggers event (testId=$testId, event=$event)',
({ event, testId }) => {
beforeEach(() => {
findElement(testId).dispatchEvent(new Event(event));
});
it('triggers listener', () => {
expect(spiesCallCount()).toBe(1);
expect(spies[event]).toHaveBeenCalledWith(expect.any(Event));
expect(spies[event]).toHaveBeenCalledWith(
expect.objectContaining({
target: findElement(testId),
}),
);
});
},
);
describe.each`
desc | event | testId
${'when non-matching element triggers event'} | ${'click'} | ${'hello'}
${'when matching element triggers unlistened event'} | ${'hover'} | ${'lorem'}
`('$desc', ({ event, testId }) => {
beforeEach(() => {
findElement(testId).dispatchEvent(new Event(event));
});
it('does not trigger listeners', () => {
expect(spiesCallCount()).toBe(0);
});
});
});
describe('after destroyed', () => {
beforeEach(() => {
createComponent();
wrapper.destroy();
});
describe('when matching element triggers event', () => {
beforeEach(() => {
findElement('lorem').dispatchEvent(new Event('click'));
});
it('does not trigger any listeners', () => {
expect(spiesCallCount()).toBe(0);
});
});
});
});
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