mirror of
https://github.com/wagtail/wagtail.git
synced 2024-11-25 05:02:57 +01:00
DropdownController - fix test & refine events & methods
- Test that uses setTimeout never ran as Tippy relies on `transitionend` events, which JSDom will not dispatch. Rework tests to correctly test this custom event gets dispatched - Use Stimulus dispatch method for custom event dispatching as the controller name (e.g. 'w-dropdown') will automatically be added, work around TypeScript bug in Stimulus which should be fixed in a future release https://github.com/hotwired/stimulus/issues/642 - Add show/hide methods, pull out options to own get method - Add unit tests for show/hide, content being in dropdown - Relates to #10557
This commit is contained in:
parent
fef8c4ac3c
commit
aa3863f17c
@ -4,11 +4,9 @@ import { DropdownController } from './DropdownController';
|
||||
describe('DropdownController', () => {
|
||||
let application;
|
||||
|
||||
beforeAll(() => {
|
||||
application?.stop();
|
||||
|
||||
beforeEach(async () => {
|
||||
document.body.innerHTML = `
|
||||
<div data-controller="w-dropdown">
|
||||
<div data-controller="w-dropdown" data-action="custom:show->w-dropdown#show custom:hide->w-dropdown#hide">
|
||||
<button type="button" data-w-dropdown-target="toggle" aria-label="Actions"></button>
|
||||
<div data-w-dropdown-target="content">
|
||||
<a href="/">Option</a>
|
||||
@ -17,25 +15,74 @@ describe('DropdownController', () => {
|
||||
|
||||
application = Application.start();
|
||||
application.register('w-dropdown', DropdownController);
|
||||
|
||||
await Promise.resolve(requestAnimationFrame);
|
||||
|
||||
// set all animation durations to 0 so that tests can ignore animation delays
|
||||
// Tippy relies on transitionend which is not yet supported in JSDom
|
||||
// https://github.com/jsdom/jsdom/issues/1781
|
||||
|
||||
document
|
||||
.querySelectorAll('[data-controller="w-dropdown"]')
|
||||
.forEach((element) => {
|
||||
application
|
||||
.getControllerForElementAndIdentifier(element, 'w-dropdown')
|
||||
.tippy.setProps({ duration: 0 }); // tippy will merge props with whatever has already been set
|
||||
});
|
||||
});
|
||||
|
||||
it('initialises Tippy.js on connect', () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
application?.stop();
|
||||
});
|
||||
|
||||
it('initialises Tippy.js on connect and shows content in a dropdown', () => {
|
||||
const toggle = document.querySelector('[data-w-dropdown-target="toggle"]');
|
||||
const content = document.querySelector(
|
||||
'[data-w-dropdown-target="content"]',
|
||||
);
|
||||
expect(toggle.getAttribute('aria-expanded')).toBe('false');
|
||||
expect(content).toBe(null);
|
||||
|
||||
toggle.dispatchEvent(new Event('click'));
|
||||
|
||||
const expandedContent = document.querySelectorAll('[role="tooltip"]');
|
||||
expect(expandedContent).toHaveLength(1);
|
||||
|
||||
expect(expandedContent[0].innerHTML).toContain('<a href="/">Option</a>');
|
||||
});
|
||||
|
||||
it('triggers custom event on activation', () => {
|
||||
it('triggers custom event on activation', async () => {
|
||||
const toggle = document.querySelector('[data-w-dropdown-target="toggle"]');
|
||||
const mock = jest.fn();
|
||||
document.addEventListener('w-dropdown:shown', mock);
|
||||
|
||||
const mock = new Promise((resolve) => {
|
||||
document.addEventListener('w-dropdown:shown', (event) => {
|
||||
resolve(event);
|
||||
});
|
||||
});
|
||||
|
||||
toggle.dispatchEvent(new Event('click'));
|
||||
// Leave time for animation to complete.
|
||||
setTimeout(() => {
|
||||
expect(mock).toHaveBeenCalled();
|
||||
}, 500);
|
||||
|
||||
const event = await mock;
|
||||
|
||||
expect(event).toEqual(
|
||||
expect.objectContaining({ type: 'w-dropdown:shown', target: document }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should support methods to show and hide the dropdown', async () => {
|
||||
expect(document.querySelectorAll('[role="tooltip"]')).toHaveLength(0);
|
||||
|
||||
const dropdownElement = document.querySelector(
|
||||
'[data-controller="w-dropdown"]',
|
||||
);
|
||||
|
||||
dropdownElement.dispatchEvent(new CustomEvent('custom:show'));
|
||||
|
||||
expect(document.querySelectorAll('[role="tooltip"]')).toHaveLength(1);
|
||||
|
||||
dropdownElement.dispatchEvent(new CustomEvent('custom:hide'));
|
||||
|
||||
expect(document.querySelectorAll('[role="tooltip"]')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
@ -25,13 +25,32 @@ export class DropdownController extends Controller<HTMLElement> {
|
||||
declare readonly toggleTarget: HTMLButtonElement;
|
||||
declare readonly contentTarget: HTMLDivElement;
|
||||
declare readonly hideOnClickValue: boolean;
|
||||
declare readonly hasContentTarget: boolean;
|
||||
tippy?: Instance<Props>;
|
||||
|
||||
connect() {
|
||||
this.tippy = tippy(this.toggleTarget, this.options);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.tippy?.hide();
|
||||
}
|
||||
|
||||
show() {
|
||||
this.tippy?.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Tippy Options
|
||||
*/
|
||||
get options(): Partial<Props> {
|
||||
// If the dropdown toggle uses an ARIA label, use this as a hover tooltip.
|
||||
const hoverTooltip = this.toggleTarget.getAttribute('aria-label');
|
||||
let hoverTooltipInstance: Instance;
|
||||
|
||||
this.contentTarget.hidden = false;
|
||||
if (this.hasContentTarget) {
|
||||
this.contentTarget.hidden = false;
|
||||
}
|
||||
|
||||
if (hoverTooltip) {
|
||||
hoverTooltipInstance = tippy(this.toggleTarget, {
|
||||
@ -51,11 +70,17 @@ export class DropdownController extends Controller<HTMLElement> {
|
||||
plugins.push(hideTooltipOnClickInside);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default Tippy Options
|
||||
*/
|
||||
const tippyOptions: Partial<Props> = {
|
||||
content: this.contentTarget as Content,
|
||||
const onShown = () => {
|
||||
this.dispatch('shown', {
|
||||
// work around for target type bug https://github.com/hotwired/stimulus/issues/642
|
||||
target: ((key = 'document') => window[key])(),
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
...(this.hasContentTarget
|
||||
? { content: this.contentTarget as Content }
|
||||
: {}),
|
||||
trigger: 'click',
|
||||
interactive: true,
|
||||
theme: 'dropdown',
|
||||
@ -67,7 +92,7 @@ export class DropdownController extends Controller<HTMLElement> {
|
||||
}
|
||||
},
|
||||
onShown() {
|
||||
document.dispatchEvent(new CustomEvent('w-dropdown:shown'));
|
||||
onShown();
|
||||
},
|
||||
onHide() {
|
||||
if (hoverTooltipInstance) {
|
||||
@ -75,7 +100,5 @@ export class DropdownController extends Controller<HTMLElement> {
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
tippy(this.toggleTarget, tippyOptions);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user