FIX: In-page anchor links were broken in subfolder setups (#18250)

The key fix in this commit is that it removes `this.replaceState(path)` for anchor-only URLs. We still intercept those routing changes to properly calculate the scroll position of the anchor via `jumpToElement`, but we no longer use the Ember router to override the browser's history. This fixes the subfolder issue and also lets the browser maintain its history correctly.

The commit also includes a small refactor to the `jumpToElement` helper to facilitate stubbing in tests.
This commit is contained in:
Penar Musaraj 2022-09-15 08:09:34 -04:00 committed by GitHub
parent 38dab71448
commit 2704a02e3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 29 deletions

View File

@ -27,7 +27,7 @@ export default function interceptClick(e) {
if ( if (
!href || !href ||
href === "#" || href.startsWith("#") ||
currentTarget.getAttribute("target") || currentTarget.getAttribute("target") ||
currentTarget.dataset.emberAction || currentTarget.dataset.emberAction ||
currentTarget.dataset.autoRoute || currentTarget.dataset.autoRoute ||

View File

@ -1,4 +1,4 @@
import DiscourseURL, { jumpToElement } from "discourse/lib/url"; import DiscourseURL from "discourse/lib/url";
import DiscourseRoute from "discourse/routes/discourse"; import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n"; import I18n from "I18n";
import StaticPage from "discourse/models/static-page"; import StaticPage from "discourse/models/static-page";
@ -25,7 +25,7 @@ export default function (page) {
activate() { activate() {
this._super(...arguments); this._super(...arguments);
jumpToElement(document.location.hash.slice(1)); DiscourseURL.jumpToElement(document.location.hash.slice(1));
}, },
model() { model() {

View File

@ -73,29 +73,6 @@ let _jumpScheduled = false;
let _transitioning = false; let _transitioning = false;
let lockOn = null; let lockOn = null;
export function jumpToElement(elementId) {
if (_jumpScheduled || isEmpty(elementId)) {
return;
}
const selector = `#main #${elementId}, a[name=${elementId}]`;
_jumpScheduled = true;
schedule("afterRender", function () {
if (lockOn) {
lockOn.clearLock();
}
lockOn = new LockOn(selector, {
finished() {
_jumpScheduled = false;
lockOn = null;
},
});
lockOn.lock();
});
}
const DiscourseURL = EmberObject.extend({ const DiscourseURL = EmberObject.extend({
isJumpScheduled() { isJumpScheduled() {
return _transitioning || _jumpScheduled; return _transitioning || _jumpScheduled;
@ -237,8 +214,8 @@ const DiscourseURL = EmberObject.extend({
// Scroll to the same page, different anchor // Scroll to the same page, different anchor
const m = /^#(.+)$/.exec(path); const m = /^#(.+)$/.exec(path);
if (m) { if (m) {
jumpToElement(m[1]); this.jumpToElement(m[1]);
return this.replaceState(path); return;
} }
const oldPath = this.router.currentURL; const oldPath = this.router.currentURL;
@ -479,9 +456,33 @@ const DiscourseURL = EmberObject.extend({
transition._discourse_original_url = path; transition._discourse_original_url = path;
const promise = transition.promise || transition; const promise = transition.promise || transition;
promise.then(() => jumpToElement(elementId)); promise.then(() => this.jumpToElement(elementId));
},
jumpToElement(elementId) {
if (_jumpScheduled || isEmpty(elementId)) {
return;
}
const selector = `#main #${elementId}, a[name=${elementId}]`;
_jumpScheduled = true;
schedule("afterRender", function () {
if (lockOn) {
lockOn.clearLock();
}
lockOn = new LockOn(selector, {
finished() {
_jumpScheduled = false;
lockOn = null;
}, },
}); });
lockOn.lock();
});
},
});
let _urlInstance = DiscourseURL.create(); let _urlInstance = DiscourseURL.create();
export function setURLContainer(container) { export function setURLContainer(container) {

View File

@ -153,4 +153,13 @@ module("Unit | Utility | url", function () {
) )
); );
}); });
test("anchor handling", async function (assert) {
sinon.stub(DiscourseURL, "jumpToElement");
DiscourseURL.routeTo("#heading1");
assert.ok(
DiscourseURL.jumpToElement.calledWith("heading1"),
"in-page anchors call jumpToElement"
);
});
}); });