mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 06:04:11 +08:00
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:
parent
38dab71448
commit
2704a02e3a
|
@ -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 ||
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user