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
18 changes: 17 additions & 1 deletion src/pat/tooltip/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ By default, the tooltip uses the `title` attribute as its source. To use the
The label is displayed as plain text and remains on the trigger element to
provide its accessible name.

To use the trigger's text without its child markup, set `source: content-text`.
This includes visually hidden text, making it useful for icon links with a
screen-reader label:

<a href="/settings" class="pat-tooltip"
data-pat-tooltip="source: content-text; trigger: hover">
<svg aria-hidden="true"><use href="#icon-settings"></use></svg>
<span class="sr-only">Settings</span>
</a>

The tooltip displays `Settings` as plain text, without copying the SVG or the
label's styling. Define `sr-only` in your site's CSS to visually hide the label.
Leading and trailing whitespace is removed; the original contents remain in place.
This source uses `textContent`, so text inside an SVG (such as a `<title>`) is
also included, even when the SVG has `aria-hidden="true"`.

### Display

Tooltips are shown when clicking on the triggering element.
Expand Down Expand Up @@ -169,7 +185,7 @@ The available options are:
| `position-list` | `auto` | `tl` `tm` `tr` `rt` `rm` `rb` `br` `bm` `bl` `lb` `lm` `lt` | The priority in which the pattern will try to position the tooltip. With the tooltip is positioned where the most space is on the screen. The two letters indicate the position of the triangle as opposed to the tooltip body. Adding `force` will force the tooltip position, even if it would end up out of view. | Multiple value |
| `position-policy` | `auto` | `auto` `force` | Policy used to place a tooltip: either always use a listed position, or allow other positions if no space is available for the listed positions. | Mutually exclusive |
| `trigger` | `click` | `click` `hover` | Sets which user action should make the tooltip appear. | Mutually exclusive |
| `source` | `title` | `ajax` `content` `title`, `aria-label` | Select where the contents of the tooltip is taken from: `ajax` load the tooltip contents from the link target url or from a selector given by the `href` attrobite. `content`: tooltip contents are the tooltip elements children. `title`, `aria-label`: tooltip content is the title resp aria-label attribute value. | Mutually exclusive |
| `source` | `title` | `ajax` `content` `content-text` `title` `aria-label` | Select where the contents of the tooltip is taken from: `ajax` load the tooltip contents from the link target url or from a selector given by the `href` attrobite. `content`: tooltip contents are the tooltip elements children. `content-text`: trimmed text content of the trigger, including visually hidden text, displayed as plain text. `title`, `aria-label`: tooltip content is the title resp aria-label attribute value. | Mutually exclusive |
| `delay` | `0` | _time_ | The delay for the tooltip to appear, expressed in milliseconds or with `m` (minutes), `s` (seconds), `ms` (milliseconds) units. | Time |
| `mark-inactive` | `true` | `true` `false` | Should we add inactive class to the tooltip trigger? | Bool |
| `closing` | `auto` | `auto` `sticky` `close-button` | Auto means that the tooltip will disappear when the user clicks out of the tooltip, or — in case of hover triggered tooltips — hovers away from the trigger element. `close-button` will add a close button to the tooltip which must be used to close the tooltip. | Mutually exclusive |
Expand Down
12 changes: 11 additions & 1 deletion src/pat/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ const all_positions = [
parser.addArgument("position-list", [], all_positions, true);
parser.addArgument("position-policy", "auto", ["auto", "force"]);
parser.addArgument("trigger", "click", ["click", "hover", "none"]);
parser.addArgument("source", "title", ["ajax", "aria-label", "content", "title"]);
parser.addArgument("source", "title", [
"ajax",
"aria-label",
"content",
"content-text",
"title",
]);
parser.addArgument("ajax-data-type", "html", ["html", "markdown"]);
parser.addArgument("closing", "auto", ["auto", "sticky", "close-button"]);
parser.addArgument("delay");
Expand Down Expand Up @@ -213,6 +219,10 @@ class Pattern extends BasePattern {
// Tooltip content from trigger childs.
content = this.el.innerHTML;
tippy_options.allowHTML = true;
} else if (opts.source === "content-text") {
// Include visually hidden text without copying child markup.
content = this.el.textContent.trim();
tippy_options.allowHTML = false;
} else if (opts.source === "ajax") {
// Tooltiop content from AJAX request.
content = document.createElement("progress");
Expand Down
36 changes: 36 additions & 0 deletions src/pat/tooltip/tooltip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,42 @@ describe("pat-tooltip", () => {
).textContent;
expect(expected).toBe(tooltip_content);
});

describe("6.10 - source: content-text", () => {
it.each([
[
"6.10.1 - extracts the visually hidden label without SVG markup",
'<svg aria-hidden="true"><path d="M0 0h10v10z" /></svg><span class="sr-only" style="position: absolute; clip: rect(0, 0, 0, 0)">Save changes</span>',
"Save changes",
],
[
"6.10.2 - renders nested markup and escaped HTML as plain text",
"<span>&lt;strong&gt;Save&lt;/strong&gt; &amp; <em>close</em></span>",
"<strong>Save</strong> & close",
],
["6.10.3 - trims whitespace-only content to an empty string", " ", ""],
])("%s", async (_description, markup, expected) => {
document.body.innerHTML = `
<a class="pat-tooltip" href="#save"
data-pat-tooltip="source: content-text; trigger: hover">
${markup}
</a>
`;
const el = document.querySelector(".pat-tooltip");
const original_markup = el.innerHTML;
const instance = new Pattern(el);
await events.await_pattern_init(instance);

el.dispatchEvent(new Event("focus"));
await utils.timeout(1);

const content = instance.tippy.popper.querySelector(".tippy-content");
expect(content.textContent).toBe(expected);
expect(content.children).toHaveLength(0);
expect(el.innerHTML).toBe(original_markup);
instance.destroy();
});
});
});

describe(`7 - if the 'source' parameter is 'ajax'`, () => {
Expand Down
Loading