Hit / to trigger the search modal, or click on the input from the flyout.
+
Test URL changes
+
+
Quick check to see if the SPA url changes using "pushState" are correctly picked up using our utils (check console).
+
+
+
+
Link Previews
diff --git a/src/index.js b/src/index.js
index ad761db..8c01168 100644
--- a/src/index.js
+++ b/src/index.js
@@ -16,6 +16,7 @@ import {
IS_PRODUCTION,
setupLogging,
getMetadataValue,
+ setupHistoryEvents,
} from "./utils";
export function setup() {
@@ -36,6 +37,7 @@ export function setup() {
domReady
.then(() => {
setupLogging();
+ setupHistoryEvents();
let sendUrlParam = false;
for (const addon of addons) {
diff --git a/src/utils.js b/src/utils.js
index 46c11cd..8d4492a 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -24,6 +24,8 @@ export const IS_TESTING =
export const IS_PRODUCTION =
typeof WEBPACK_IS_PRODUCTION === "undefined" ? false : WEBPACK_IS_PRODUCTION;
+export const READTHEDOCS_URL_CHANGED_EVENT = "readthedocsUrlChanged";
+
export const domReady = new Promise((resolve) => {
if (
document.readyState === "interactive" ||
@@ -155,6 +157,44 @@ export class AddonBase {
}
}
+/**
+ * Setup events firing on history `pushState` and `replaceState`
+ *
+ * This is needed when addons are used in SPA. A lot of addons rely
+ * on the current URL. However in the SPA, the pages are not reloaded, so
+ * the addons never get notified of the changes in the URL.
+ *
+ * While History API does have `popstate` event, the only way to listen to
+ * changes via `pushState` and `replaceState` is using monkey-patching, which is
+ * what this function does. (See https://stackoverflow.com/a/4585031)
+ * It will fire a `READTHEDOCS_URL_CHANGED` event, on `pushState` and `replaceState`.
+ *
+ */
+export function setupHistoryEvents() {
+ // Let's ensure that the history will be patched only once, so we create a Symbol to check by
+ const patchKey = Symbol.for("addons_history");
+
+ if (
+ typeof history !== "undefined" &&
+ typeof window[patchKey] === "undefined"
+ ) {
+ for (const methodName of ["pushState", "replaceState"]) {
+ const originalMethod = history[methodName];
+ history[methodName] = function () {
+ const result = originalMethod.apply(this, arguments);
+ const event = new Event(READTHEDOCS_URL_CHANGED_EVENT);
+ event.arguments = arguments;
+
+ dispatchEvent(event);
+ return result;
+ };
+ }
+
+ // Let's leave a flag, so we know that history has been patched
+ Object.defineProperty(window, patchKey, { value: true });
+ }
+}
+
/**
* Debounce a function.
*