fix scroll when popstate from a full screen code block (#19674)

This commit is contained in:
zhangted 2023-07-07 15:54:24 -04:00 committed by GitHub
parent 4632c6bbf4
commit 5fe3a04287
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 127 additions and 2 deletions

View file

@ -0,0 +1,106 @@
const importModule = () => import('@utilities/codeFullscreenModeSwitcher');
describe('CodeFullScreenModeSwitcher Utility', () => {
let addFullScreenModeControl, getFullScreenModeStatus;
let onPressEscape, onPopstate;
const getFullScreenElements = () =>
['.js-fullscreen-code.is-open', '.js-code-highlight.is-fullscreen'].map(
(selector) => document.body.querySelector(selector),
);
const testFullScreenElements = ({ exists }) => {
for (const element of getFullScreenElements()) {
if (exists) expect(element).not.toBeNull();
else expect(element).toBeNull();
}
};
const testNonFullScreen = () => {
expect(getFullScreenModeStatus()).toBe(false);
testFullScreenElements({ exists: false });
expect(document.body.style.overflow).toBe('');
};
const testFullScreen = () => {
expect(getFullScreenModeStatus()).toBe(true);
testFullScreenElements({ exists: true });
expect(document.body.style.overflow).toBe('hidden');
};
const getEnterFullScreenButtons = () =>
document.getElementsByClassName('js-fullscreen-code-action');
beforeAll(() => {
global.scrollTo = jest.fn();
document.body.innerHTML = `
<div class="js-code-highlight">
<div class="js-fullscreen-code-action"><div>
</div>
<div class="js-fullscreen-code"></div>
`;
});
beforeAll(async () => {
({ addFullScreenModeControl, getFullScreenModeStatus } =
await importModule());
addFullScreenModeControl(getEnterFullScreenButtons());
});
beforeAll(async () => {
// eventListener functions
({ onPressEscape, onPopstate } = await importModule());
});
it('starts in non-fullscreen mode', testNonFullScreen);
it('enters fullscreen mode on click', () => {
const goFullScreenButtons = getEnterFullScreenButtons();
const spyDoc = jest.spyOn(document.body, 'addEventListener');
const spyWindow = jest.spyOn(window, 'addEventListener');
goFullScreenButtons[0].click();
testFullScreen();
expect(spyDoc).toHaveBeenCalledWith('keyup', onPressEscape);
expect(spyWindow).toHaveBeenCalledWith('popstate', onPopstate);
});
it('exits fullscreen mode on Escape key', () => {
const spyDocRemove = jest.spyOn(document.body, 'removeEventListener');
const spyWindowRemove = jest.spyOn(window, 'removeEventListener');
testFullScreen();
document.body.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));
testNonFullScreen();
expect(spyDocRemove).toHaveBeenCalledWith('keyup', onPressEscape);
expect(spyWindowRemove).toHaveBeenCalledWith('popstate', onPopstate);
});
it('re-enters fullscreen mode on click', () => {
const goFullScreenButtons = getEnterFullScreenButtons();
const spyDoc = jest.spyOn(document.body, 'addEventListener');
const spyWindow = jest.spyOn(window, 'addEventListener');
testNonFullScreen();
goFullScreenButtons[0].click();
testFullScreen();
expect(spyDoc).toHaveBeenCalledWith('keyup', onPressEscape);
expect(spyWindow).toHaveBeenCalledWith('popstate', onPopstate);
});
it('exits fullscreen mode on popstate event', () => {
const spyDocRemove = jest.spyOn(document.body, 'removeEventListener');
const spyWindowRemove = jest.spyOn(window, 'removeEventListener');
testFullScreen();
window.dispatchEvent(new PopStateEvent('popstate', { state: { key: '' } }));
testNonFullScreen();
expect(spyDocRemove).toHaveBeenCalledWith('keyup', onPressEscape);
expect(spyWindowRemove).toHaveBeenCalledWith('popstate', onPopstate);
});
});

View file

@ -4,6 +4,10 @@ const fullScreenWindow =
document.getElementsByClassName('js-fullscreen-code')[0];
const { body } = document;
export function getFullScreenModeStatus() {
return isFullScreenModeCodeOn;
}
function setAfterFullScreenScrollPosition() {
window.scrollTo(0, screenScroll);
}
@ -12,7 +16,7 @@ function getBeforeFullScreenScrollPosition() {
screenScroll = window.scrollY;
}
function onPressEscape(event) {
export function onPressEscape(event) {
if (event.key == 'Escape') {
fullScreenModeControl(event);
}
@ -26,6 +30,18 @@ function listenToKeyboardForEscape(listen) {
}
}
export function onPopstate() {
fullScreenModeControl();
}
function listenToWindowForPopstate(listen) {
if (listen) {
window.addEventListener('popstate', onPopstate);
} else {
window.removeEventListener('popstate', onPopstate);
}
}
function toggleOverflowForDocument(overflow) {
if (overflow) {
body.style.overflow = 'hidden';
@ -51,7 +67,7 @@ function removeFullScreenModeControl(elements) {
}
function fullScreenModeControl(event) {
const codeBlock = event.currentTarget.closest('.js-code-highlight')
const codeBlock = event?.currentTarget.closest('.js-code-highlight')
? event.currentTarget.closest('.js-code-highlight').cloneNode(true)
: null;
const codeBlockControls = codeBlock
@ -62,6 +78,7 @@ function fullScreenModeControl(event) {
toggleOverflowForDocument(false);
setAfterFullScreenScrollPosition();
listenToKeyboardForEscape(false);
listenToWindowForPopstate(false);
removeFullScreenModeControl(codeBlockControls);
fullScreenWindow.classList.remove('is-open');
@ -72,6 +89,8 @@ function fullScreenModeControl(event) {
toggleOverflowForDocument(true);
getBeforeFullScreenScrollPosition();
listenToKeyboardForEscape(true);
listenToWindowForPopstate(true);
codeBlock.classList.add('is-fullscreen');
fullScreenWindow.appendChild(codeBlock);
fullScreenWindow.classList.add('is-open');