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 (
!href ||
href === "#" ||
href.startsWith("#") ||
currentTarget.getAttribute("target") ||
currentTarget.dataset.emberAction ||
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 I18n from "I18n";
import StaticPage from "discourse/models/static-page";
@ -25,7 +25,7 @@ export default function (page) {
activate() {
this._super(...arguments);
jumpToElement(document.location.hash.slice(1));
DiscourseURL.jumpToElement(document.location.hash.slice(1));
},
model() {

View File

@ -73,29 +73,6 @@ let _jumpScheduled = false;
let _transitioning = false;
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({
isJumpScheduled() {
return _transitioning || _jumpScheduled;
@ -237,8 +214,8 @@ const DiscourseURL = EmberObject.extend({
// Scroll to the same page, different anchor
const m = /^#(.+)$/.exec(path);
if (m) {
jumpToElement(m[1]);
return this.replaceState(path);
this.jumpToElement(m[1]);
return;
}
const oldPath = this.router.currentURL;
@ -479,9 +456,33 @@ const DiscourseURL = EmberObject.extend({
transition._discourse_original_url = path;
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();
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"
);
});
});