Skip to content
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
6 changes: 0 additions & 6 deletions src/pat/tooltip/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,6 @@ option:
This will load the contents of the `#myTip` element of
balloon-contents.html and display it in a tooltip.

You can also use the `::element` modifier after a document fragment to select
the element itself instead of it's contents. E.g.:

<a href="balloon-contents.html#myTip::element" class="pat-tooltip" data-pat-tooltip="source: ajax">
</a>

### Generated markup

Expand Down
62 changes: 0 additions & 62 deletions src/pat/tooltip/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -449,73 +449,11 @@ <h4>works with other patterns</h4>
Like
</button>
</form>

<h4>works with other patterns / pt2</h4>
This opens a
<a
href="#tooltip-source-form::element"
class="pat-tooltip"
data-pat-tooltip="trigger: click; closing: close-button; source: content"
>Tooltip form with other patterns</a
>.
</div>

<div style="display: none">
<!-- Tooltip content elements -->
<section id="test-tooltip-w-submit--content">Liked</section>
<form id="tooltip-source-form">
<fieldset
class="vertical"
data-pat-inject="source: #listing; target: #listing;"
>
<fieldset
class="pat-checklist checked has-value"
data-value="on"
>
<label class="checked has-value" data-value="on">
<input
type="checkbox"
name="display-previews"
checked=""
class="has-value"
data-value="on"
/>
Display previews
</label>
</fieldset>
<fieldset
class="pat-checklist radio has-value checked"
data-value="relevancy"
>
<label class="has-value checked" data-value="relevancy">
<input
type="radio"
name="results-sorting"
checked=""
value="relevancy"
class="has-value"
data-value="relevancy"
/>
Sort by relevancy
</label>
<label class="unchecked">
<input
type="radio"
name="results-sorting"
value="date"
/>
Sort by date
</label>
</fieldset>
</fieldset>
<li>
<a
class="close-panel pat-modal"
href="./pattern-test-response.html#message"
>Open a modal.</a
>
</li>
</form>
<div id="tooltip-source" class="sources">
<strong>Hello there</strong>
</div>
Expand Down
34 changes: 14 additions & 20 deletions src/pat/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export default Base.extend({
if (this.ajax_state.isFetching || !this.ajax_state.canFetch) {
return undefined;
}
const { url, selector, modifier } = this.get_url_parts(
const { url, selector } = this.get_url_parts(
this.el.getAttribute("href")
);
let content;
Expand All @@ -327,15 +327,15 @@ export default Base.extend({
// TODO: use pat-inject, once it supports async
const response = await fetch(url);
const text = await response.text();
content = await handler(text, url, selector, modifier);
content = await handler(text, url, selector);
} catch (e) {
log.error(`Error on ajax request ${e}`);
}
this.ajax_state.isFetching = false;
} else if (selector) {
// Tooltip content from current DOM tree.
content = document.querySelector(selector);
content = content ? content[modifier] : undefined;
content = content?.innerHTML || undefined;
}
if (content) {
this.tippy.setContent(content);
Expand All @@ -344,34 +344,28 @@ export default Base.extend({
},

get_url_parts(href) {
// Return the URL, a CSS ID selector and a DOM query modifier.
// The modifier is a as defined in pat-inject:
// ::element selects the element itself and not it's children.
let url, selector, modifier;
// Return the URL and a CSS ID selector.
let url, selector;
if (!href) {
return { url, selector, modifier };
return { url, selector };
}
url = (href.split("#")[0] || "").split("::")[0] || undefined;
selector = (href.split("#")[1] || "").split("::")[0] || undefined;
url = href.split("#")[0] || undefined;
selector = href.split("#")[1] || undefined;
selector = selector ? `#${selector}` : undefined;
modifier = (href.split("#")[1] || "").split("::")[1] || undefined;
modifier = modifier === "element" ? "outerHTML" : "innerHTML";
return { url, selector, modifier };
return { url, selector };
},

_ajaxDataTypeHandlers: {
html(text, url, selector, modifier) {
const tmp = document.createElement("div");
html(text, url, selector) {
let tmp = document.createElement("div");
tmp.innerHTML = text;
if (selector) {
const el = tmp.querySelector(selector);
return el ? el[modifier] : "";
tmp = tmp.querySelector(selector);
}
return tmp.innerHTML;
return tmp?.innerHTML || "";
},

// eslint-disable-next-line no-unused-vars
async markdown(text, url, selector, modifier) {
async markdown(text, url, selector) {
const pat_markdown = await import("../markdown/markdown");
const pat = pat_markdown.default.init($("<div/>"));
const cfg = { url };
Expand Down
129 changes: 1 addition & 128 deletions src/pat/tooltip/tooltip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1409,150 +1409,23 @@ this will be extracted.
});
});

describe("::element modifier support", () => {
it("ajax mode: it fetches the outerHTML with the ::element modifier", async (done) => {
global.fetch = jest
.fn()
.mockImplementation(
mockFetch('<div id="outer">External content</div>')
);

const $el = testutils.createTooltip({
data: "source: ajax; trigger: click",
href: "http://test.com/#outer::element",
});
new pattern($el);
await utils.timeout(1);

testutils.click($el);
await utils.timeout(1); // wait a tick for async fetch

expect(
document.querySelector(".tippy-box .tippy-content").innerHTML
).toBe('<div id="outer">External content</div>');

global.fetch.mockClear();
delete global.fetch;

done();
});

it("ajax mode: it fetches the innerHTML without the ::element modifier", async (done) => {
global.fetch = jest
.fn()
.mockImplementation(
mockFetch('<div id="outer">External content</div>')
);

const $el = testutils.createTooltip({
data: "source: ajax; trigger: click",
href: "http://test.com/#outer",
});
new pattern($el);
await utils.timeout(1);

testutils.click($el);
await utils.timeout(1); // wait a tick for async fetch

expect(
document.querySelector(".tippy-box .tippy-content").innerHTML
).toBe("External content");

global.fetch.mockClear();
delete global.fetch;

done();
});

it("local content: it uses the outerHTML with the ::element modifier", async (done) => {
const content = document.createElement("div");
content.setAttribute("id", "local-content");
content.innerHTML = '<strong class="testinner">okay</strong>';
document.body.appendChild(content);

const $el = testutils.createTooltip({
data: "source: ajax; trigger: click",
href: "#local-content::element",
});
new pattern($el);
await utils.timeout(1);

testutils.click($el);
await utils.timeout(1); // wait a tick for async fetch

expect(
document.querySelector(
".tippy-box .tippy-content #local-content"
)
).toBeTruthy();

done();
});

it("local content: it uses the innerHTML without the ::element modifier", async (done) => {
const content = document.createElement("div");
content.setAttribute("id", "local-content");
content.innerHTML = '<strong class="testinner">okay</strong>';
document.body.appendChild(content);

const $el = testutils.createTooltip({
data: "source: ajax; trigger: click",
href: "#local-content",
});
new pattern($el);
await utils.timeout(1);

testutils.click($el);
await utils.timeout(1); // wait a tick for async fetch

expect(
document.querySelector(
".tippy-box .tippy-content #local-content"
)
).toBeFalsy();

expect(
document.querySelector(".tippy-box .tippy-content .testinner")
).toBeTruthy();

done();
});
});

describe("URL splitting", () => {
it("it extracts the correct parts from any url", async (done) => {
const $el = testutils.createTooltip({});
const instance = new pattern($el);
await utils.timeout(1);

let parts = instance.get_url_parts(
"https://text.com/#selector::modifier"
);
expect(parts.url === "https://text.com/").toBeTruthy();
expect(parts.selector === "#selector").toBeTruthy();
expect(parts.modifier === "innerHTML").toBeTruthy();

parts = instance.get_url_parts(
"https://text.com/#selector::element"
);
let parts = instance.get_url_parts("https://text.com/#selector");
expect(parts.url === "https://text.com/").toBeTruthy();
expect(parts.selector === "#selector").toBeTruthy();
expect(parts.modifier === "outerHTML").toBeTruthy();

parts = instance.get_url_parts("#selector::element");
expect(typeof parts.url === "undefined").toBeTruthy();
expect(parts.selector === "#selector").toBeTruthy();
expect(parts.modifier === "outerHTML").toBeTruthy();

parts = instance.get_url_parts("#selector");
expect(typeof parts.url === "undefined").toBeTruthy();
expect(parts.selector === "#selector").toBeTruthy();
expect(parts.modifier === "innerHTML").toBeTruthy();

parts = instance.get_url_parts("https://text.com/");
expect(parts.url === "https://text.com/").toBeTruthy();
expect(typeof parts.selector === "undefined").toBeTruthy();
expect(parts.modifier === "innerHTML").toBeTruthy();

done();
});
Expand Down