kopia lustrzana https://github.com/wagtail/wagtail
Co-authored-by: Thibaud Colas <thibaudcolas@gmail.com>pull/7833/head
rodzic
317f100a78
commit
a7aabf76ac
|
@ -33,6 +33,7 @@ Changelog
|
|||
* Fix: Additional login form fields from `WAGTAILADMIN_USER_LOGIN_FORM` are now rendered correctly (Michael Karamuth)
|
||||
* Fix: Icon only button styling issue on small devices where height would not be set correctly (Vu Pham)
|
||||
* Fix: Add padding to the Draftail editor to ensure `ol` items are not cut off (Khanh Hoang)
|
||||
* Fix: Prevent opening choosers multiple times for Image, Page, Document, Snippet (LB (Ben Johnston))
|
||||
|
||||
|
||||
2.15.2 (xx.xx.xxxx) - IN DEVELOPMENT
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`modal-workflow should generate a modal when called that is removed when closed 1`] = `
|
||||
HTMLCollection [
|
||||
<div
|
||||
aria-hidden="false"
|
||||
class="modal fade in"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="modal-dialog"
|
||||
>
|
||||
|
||||
|
||||
<div
|
||||
class="modal-content"
|
||||
>
|
||||
|
||||
|
||||
<button
|
||||
class="button close button--icon text-replace"
|
||||
data-dismiss="modal"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="icon icon-cross"
|
||||
focusable="false"
|
||||
>
|
||||
<use
|
||||
href="#icon-cross"
|
||||
/>
|
||||
</svg>
|
||||
Close
|
||||
</button>
|
||||
|
||||
|
||||
<div
|
||||
class="modal-body"
|
||||
>
|
||||
<div
|
||||
id="url"
|
||||
>
|
||||
path/to/endpoint
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
|
||||
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
|
||||
|
||||
</div>,
|
||||
]
|
||||
`;
|
|
@ -25,6 +25,10 @@ function ModalWorkflow(opts) {
|
|||
/* remove any previous modals before continuing (closing doesn't remove them from the dom) */
|
||||
$('body > .modal').remove();
|
||||
|
||||
// disable the trigger element so it cannot be clicked twice while modal is loading
|
||||
self.triggerElement = document.activeElement;
|
||||
self.triggerElement.setAttribute('disabled', true);
|
||||
|
||||
// set default contents of container
|
||||
const iconClose = '<svg class="icon icon-cross" aria-hidden="true" focusable="false"><use href="#icon-cross"></use></svg>';
|
||||
const container = $('<div class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">\n <div class="modal-dialog">\n <div class="modal-content">\n <button type="button" class="button close button--icon text-replace" data-dismiss="modal">' + iconClose + wagtailConfig.STRINGS.CLOSE + '</button>\n <div class="modal-body"></div>\n </div><!-- /.modal-content -->\n </div><!-- /.modal-dialog -->\n</div>');
|
||||
|
@ -33,6 +37,17 @@ function ModalWorkflow(opts) {
|
|||
$('body').append(container);
|
||||
container.modal('hide');
|
||||
|
||||
// add listener - once modal is about to be hidden, re-enable the trigger
|
||||
container.on('hide.bs.modal', () => {
|
||||
self.triggerElement.removeAttribute('disabled');
|
||||
});
|
||||
|
||||
// add listener - once modal is fully hidden (closed & css transitions end) - re-focus on trigger and remove from DOM
|
||||
container.on('hidden.bs.modal', function () {
|
||||
self.triggerElement.focus();
|
||||
container.remove();
|
||||
});
|
||||
|
||||
self.body = container.find('.modal-body');
|
||||
|
||||
self.loadUrl = function (url, urlParams) {
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
import $ from 'jquery';
|
||||
window.$ = $;
|
||||
window.jQuery = $;
|
||||
|
||||
import '../../../../wagtail/admin/static_src/wagtailadmin/js/vendor/bootstrap-modal';
|
||||
import './modal-workflow';
|
||||
|
||||
$.get = jest.fn().mockImplementation((url, data, cb) => {
|
||||
cb(JSON.stringify({ html: `<div id="url">${url}</div>`, data, step: 'start' }));
|
||||
return { fail: jest.fn() };
|
||||
});
|
||||
|
||||
describe('modal-workflow', () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML =
|
||||
'<div id="content"><button class="button action-choose" id="trigger">Open</button></div>';
|
||||
});
|
||||
|
||||
it('exposes module as global', () => {
|
||||
expect(window.ModalWorkflow).toBeDefined();
|
||||
});
|
||||
|
||||
it('should generate a modal when called that is removed when closed', () => {
|
||||
let modalWorkflow;
|
||||
|
||||
const openModal = () => {
|
||||
// eslint-disable-next-line new-cap
|
||||
modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
|
||||
};
|
||||
|
||||
expect(document.getElementsByClassName('modal')).toHaveLength(0);
|
||||
|
||||
const triggerButton = document.getElementById('trigger');
|
||||
|
||||
triggerButton.addEventListener('click', openModal);
|
||||
triggerButton.dispatchEvent(new MouseEvent('click'));
|
||||
|
||||
// check that modal has been created in the DOM
|
||||
const modal = document.getElementsByClassName('modal');
|
||||
expect(modal).toHaveLength(1);
|
||||
expect(modal).toMatchSnapshot();
|
||||
|
||||
// check that modalWorkflow returns self
|
||||
expect(modalWorkflow).toBeInstanceOf(Object);
|
||||
expect(modalWorkflow).toHaveProperty('close', expect.any(Function));
|
||||
|
||||
// close modal & check it is removed from the DOM
|
||||
modalWorkflow.close();
|
||||
expect(document.getElementsByClassName('modal')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should pull focus from the trigger element to the modal and back to the trigger when closed', () => {
|
||||
let modalWorkflow;
|
||||
|
||||
const openModal = () => {
|
||||
// eslint-disable-next-line new-cap
|
||||
modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
|
||||
};
|
||||
|
||||
expect(document.getElementsByClassName('modal')).toHaveLength(0);
|
||||
|
||||
const triggerButton = document.getElementById('trigger');
|
||||
|
||||
triggerButton.focus();
|
||||
triggerButton.addEventListener('click', openModal);
|
||||
|
||||
expect(document.activeElement).toEqual(triggerButton);
|
||||
triggerButton.dispatchEvent(new MouseEvent('click'));
|
||||
|
||||
// check that modal has been created in the DOM & takes focus
|
||||
expect(document.getElementsByClassName('modal')).toHaveLength(1);
|
||||
expect(document.activeElement.className).toEqual('modal fade in');
|
||||
|
||||
// close modal & check that focus moves back to the trigger
|
||||
modalWorkflow.close();
|
||||
expect(document.getElementsByClassName('modal')).toHaveLength(0);
|
||||
expect(document.activeElement).toEqual(triggerButton);
|
||||
});
|
||||
|
||||
it('should disable the trigger element when the modal is opened and re-enable it when closing', () => {
|
||||
let modalWorkflow;
|
||||
|
||||
const openModal = () => {
|
||||
// eslint-disable-next-line new-cap
|
||||
modalWorkflow = window.ModalWorkflow({ url: 'path/to/endpoint' });
|
||||
};
|
||||
|
||||
expect(document.getElementsByClassName('modal')).toHaveLength(0);
|
||||
|
||||
const triggerButton = document.getElementById('trigger');
|
||||
|
||||
triggerButton.focus();
|
||||
triggerButton.addEventListener('click', openModal);
|
||||
expect(triggerButton.disabled).toBe(false);
|
||||
triggerButton.dispatchEvent(new MouseEvent('click'));
|
||||
|
||||
// check that the trigger button is disabled
|
||||
expect(triggerButton.disabled).toBe(true);
|
||||
|
||||
// close modal & check that trigger button is re-enabled
|
||||
modalWorkflow.close();
|
||||
expect(triggerButton.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle onload and URL param options', () => {
|
||||
const onload = { start: jest.fn() };
|
||||
const urlParams = { page: 23 };
|
||||
|
||||
let modalWorkflow;
|
||||
|
||||
const openModal = () => {
|
||||
// eslint-disable-next-line new-cap
|
||||
modalWorkflow = window.ModalWorkflow({
|
||||
url: 'path/to/endpoint',
|
||||
urlParams,
|
||||
onload,
|
||||
});
|
||||
};
|
||||
|
||||
expect(onload.start).not.toHaveBeenCalled();
|
||||
const triggerButton = document.getElementById('trigger');
|
||||
triggerButton.addEventListener('click', openModal);
|
||||
|
||||
const event = new MouseEvent('click');
|
||||
triggerButton.dispatchEvent(event);
|
||||
|
||||
expect(modalWorkflow).toBeInstanceOf(Object);
|
||||
|
||||
// important: see mock implementation above, returning a response with injected data to validate behaviour
|
||||
const response = { data: urlParams, html: '<div id="url">path/to/endpoint</div>', step: 'start' };
|
||||
expect(onload.start).toHaveBeenCalledWith(modalWorkflow, response);
|
||||
});
|
||||
});
|
|
@ -41,6 +41,7 @@
|
|||
* Additional login form fields from `WAGTAILADMIN_USER_LOGIN_FORM` are now rendered correctly (Michael Karamuth)
|
||||
* Fix icon only button styling issue on small devices where height would not be set correctly (Vu Pham)
|
||||
* Add padding to the Draftail editor to ensure `ol` items are not cut off (Khanh Hoang)
|
||||
* Prevent opening choosers multiple times for Image, Page, Document, Snippet (LB (Ben Johnston))
|
||||
|
||||
## Upgrade considerations
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue