Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visual diff: trigger DOM_CHANGED event after enable/disable #465

Merged
merged 5 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions public/_/readthedocs-addons.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@
"enabled": true,
"code": "UA-12345"
},
"linkpreviews": {
"enabled": true
},
"notifications": {
"enabled": true,
"show_on_latest": true,
Expand Down
12 changes: 11 additions & 1 deletion src/docdiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { AddonBase } from "./utils";
import {
EVENT_READTHEDOCS_DOCDIFF_ADDED_REMOVED_SHOW,
EVENT_READTHEDOCS_DOCDIFF_HIDE,
EVENT_READTHEDOCS_ROOT_DOM_CHANGED,
} from "./events";
import { nothing, LitElement } from "lit";
import { default as objectPath } from "object-path";
Expand Down Expand Up @@ -103,7 +104,9 @@ export class DocDiffElement extends LitElement {

// Enable DocDiff if the URL parameter is present
if (hasQueryParam(DOCDIFF_URL_PARAM)) {
event = new CustomEvent(EVENT_READTHEDOCS_DOCDIFF_ADDED_REMOVED_SHOW);
const event = new CustomEvent(
EVENT_READTHEDOCS_DOCDIFF_ADDED_REMOVED_SHOW,
);
document.dispatchEvent(event);
}
}
Expand Down Expand Up @@ -152,6 +155,10 @@ export class DocDiffElement extends LitElement {
this.cachedRemoteContent = text;
this.performDiff(text);
})
.finally(() => {
const event = new CustomEvent(EVENT_READTHEDOCS_ROOT_DOM_CHANGED);
document.dispatchEvent(event);
})
.catch((error) => {
console.error(error);
});
Expand Down Expand Up @@ -204,6 +211,9 @@ export class DocDiffElement extends LitElement {

this.enabled = false;
document.querySelector(this.rootSelector).replaceWith(this.originalBody);

const event = new CustomEvent(EVENT_READTHEDOCS_ROOT_DOM_CHANGED);
document.dispatchEvent(event);
}

_handleShowDocDiff = (e) => {
Expand Down
17 changes: 17 additions & 0 deletions src/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,26 @@ export const EVENT_READTHEDOCS_DOCDIFF_ADDED_REMOVED_SHOW =
export const EVENT_READTHEDOCS_DOCDIFF_HIDE = "readthedocs-docdiff-hide";
export const EVENT_READTHEDOCS_FLYOUT_SHOW = "readthedocs-flyout-show";
export const EVENT_READTHEDOCS_FLYOUT_HIDE = "readthedocs-flyout-hide";

/**
* Event triggered when the Read the Docs data is ready to be consumed.
*
* This is the event users subscribe to to make usage of Read the Docs data.
* The object received is `ReadTheDocsEventData`.
*/
export const EVENT_READTHEDOCS_ADDONS_DATA_READY =
"readthedocs-addons-data-ready";

/**
* Event triggered when any addons modifies the root DOM.
*
* As an example, DocDiff triggers it when injecting the visual diferences.
* Addons subscribe to this event to re-initialize them in case they perform
* something specific on DOM elements from inside the root.
*/
export const EVENT_READTHEDOCS_ROOT_DOM_CHANGED =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a docstring or similar to describe the events here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 👍🏼

"readthedocs-root-dom-changed";

/**
* Object to pass to user subscribing to `EVENT_READTHEDOCS_ADDONS_DATA_READY`.
*
Expand Down
93 changes: 80 additions & 13 deletions src/linkpreviews.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
IS_TESTING,
docTool,
} from "./utils";
import { EVENT_READTHEDOCS_ROOT_DOM_CHANGED } from "./events";
import {
computePosition,
autoPlacement,
Expand Down Expand Up @@ -200,26 +201,28 @@ function setupTooltip(el, doctoolname, doctoolversion, selector) {
}
}

/**
* LinkPreviews addon
*
* @param {Object} config - Addon configuration object
*/
export class LinkPreviewsAddon extends AddonBase {
static jsonValidationURI =
"http://v1.schemas.readthedocs.org/addons.linkpreviews.json";
static addonEnabledPath = "addons.linkpreviews.enabled";
static addonName = "LinkPreviews";
export class LinkPreviewsElement extends LitElement {
static elementName = "readthedocs-linkpreviews";

constructor(config) {
static properties = {
config: {
state: true,
},
};

constructor() {
super();
this.config = config;

if (!IS_TESTING) {
// Include CSS into the DOM so they can be read.
// We can't include these CSS in the LitElement, because we need them to be globally available.
document.adoptedStyleSheets.push(styleSheet);
}

this.config = null;
}

setupTooltips() {
// Autodetect if the page is built with Sphinx and send the `doctool=` attribute in that case.
const doctoolName = docTool.getDocumentationTool();
const rootSelector =
Expand All @@ -242,7 +245,7 @@ export class LinkPreviewsAddon extends AddonBase {
let elementHostname = elementUrl.hostname;
const pointToSamePage =
window.location.pathname.replace("/index.html", "") ==
elementUrl.pathname.replace("/index.html");
elementUrl.pathname.replace("/index.html", "");
if (elementHostname === window.location.hostname && !pointToSamePage) {
element.classList.add("link-preview");
setupTooltip(element, doctoolName, null, rootSelector);
Expand All @@ -254,4 +257,68 @@ export class LinkPreviewsAddon extends AddonBase {
}
}
}

render() {
return nothing;
}

loadConfig(config) {
if (!LinkPreviewsAddon.isEnabled(config)) {
return;
}

this.config = config;
this.setupTooltips();
}

_handleRootDOMChanged = (e) => {
// Trigger the setup again since the DOM has changed
this.setupTooltips();
};

connectedCallback() {
super.connectedCallback();
document.addEventListener(
EVENT_READTHEDOCS_ROOT_DOM_CHANGED,
this._handleRootDOMChanged,
);
}

disconnectedCallback() {
document.removeEventListener(
EVENT_READTHEDOCS_ROOT_DOM_CHANGED,
this._handleRootDOMChanged,
);
super.disconnectedCallback();
}
}

/**
* LinkPreviews addon
*
* @param {Object} config - Addon configuration object
*/
export class LinkPreviewsAddon extends AddonBase {
static jsonValidationURI =
"http://v1.schemas.readthedocs.org/addons.linkpreviews.json";
static addonEnabledPath = "addons.linkpreviews.enabled";
static addonName = "LinkPreviews";

constructor(config) {
super();

// If there are no elements found, inject one
let elems = document.querySelectorAll("readthedocs-linkpreviews");
if (!elems.length) {
elems = [new LinkPreviewsElement()];
document.body.append(elems[0]);
elems[0].requestUpdate();
}

for (const elem of elems) {
elem.loadConfig(config);
}
}
}

customElements.define("readthedocs-linkpreviews", LinkPreviewsElement);