diff --git a/src/pat/tooltip/documentation.md b/src/pat/tooltip/documentation.md index 3273c5629..c55f7f754 100644 --- a/src/pat/tooltip/documentation.md +++ b/src/pat/tooltip/documentation.md @@ -13,6 +13,33 @@ Tooltips are intended to display contextual information and function about the t class="pat-tooltip">More information +By default, the tooltip uses the `title` attribute as its source. To use the +`aria-label` attribute instead, set `source: aria-label`: + + + +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: + + + + Settings + + +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 ``) is +also included, even when the SVG has `aria-hidden="true"`. + ### Display Tooltips are shown when clicking on the triggering element. @@ -104,7 +131,6 @@ option: This will load the contents of the `#myTip` element of balloon-contents.html and display it in a tooltip. - ### Generated markup The first time the tooltip is shown the tip itself will be wrapped in a @@ -154,15 +180,15 @@ be temporarily shown with a progress indicator: The tooltip can be configured through a `data-pat-tooltip` attribute. The available options are: -| Property | Default value | Values | Description | Type | -| ----------------- | ------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | -| `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` | 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``: tooltip content is the title 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 | -| `class` | _none_ | _class value_ | Assigns a class to the tooltip. For instance to give a specific tooltip a different colour | | -| `ajax-data-type` | `html` | `html` `markdown` | Data type of content to be loaded when AJAX is used as source. | Mutually exclusive | -| `target` | `body` | _selector_ | Selects where the tooltip container is appended in the DOM | | +| Property | Default value | Values | Description | Type | +| ----------------- | ------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | +| `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` `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 | +| `class` | _none_ | _class value_ | Assigns a class to the tooltip. For instance to give a specific tooltip a different colour | | +| `ajax-data-type` | `html` | `html` `markdown` | Data type of content to be loaded when AJAX is used as source. | Mutually exclusive | +| `target` | `body` | _selector_ | Selects where the tooltip container is appended in the DOM | | diff --git a/src/pat/tooltip/tooltip.js b/src/pat/tooltip/tooltip.js index 8eaf71eeb..94bbd9978 100644 --- a/src/pat/tooltip/tooltip.js +++ b/src/pat/tooltip/tooltip.js @@ -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", "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"); @@ -206,15 +212,25 @@ class Pattern extends BasePattern { if (opts.source === "title") { // Tooltip content from title attribute content = this.el.getAttribute("title"); + } else if (opts.source === "aria-label") { + // Tooltip content from aria-label attribute + content = this.el.getAttribute("aria-label"); } else if (opts.source === "content") { // 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"); tippy_options.allowHTML = true; } + if (!content) { + return; + } tippy_options.content = content; }, diff --git a/src/pat/tooltip/tooltip.test.js b/src/pat/tooltip/tooltip.test.js index a21c5b7d5..f11b80743 100644 --- a/src/pat/tooltip/tooltip.test.js +++ b/src/pat/tooltip/tooltip.test.js @@ -1285,6 +1285,72 @@ describe("pat-tooltip", () => { global.fetch.mockRestore(); delete global.fetch; }); + + it("6.9 - source: aria-label.", async () => { + const tooltip_content = "this button deserves to be clicked."; + + document.body.innerHTML = ` + <button + class="pat-tooltip" + type="button" + aria-label="${tooltip_content}" + data-pat-tooltip=" + source: aria-label; + trigger: click"> + click me + </button> + `; + + const el = document.body.querySelector(".pat-tooltip"); + + const instance = new Pattern(el); + await events.await_pattern_init(instance); + await utils.timeout(1); + + el.click(); + await utils.timeout(1); // wait a tick + + const expected = document.querySelector( + ".tooltip-container .tippy-content" + ).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><strong>Save</strong> & <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'`, () => {