From 89a689ab13e83d1bb0b87c24aaf2915ee595fe58 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 19 Jul 2023 19:28:56 -0400 Subject: [PATCH 1/2] Add tooltip to Related Resources --- web/app/components/document/sidebar.hbs | 2 +- .../document/sidebar/related-resources.hbs | 1 + .../document/sidebar/related-resources.ts | 4 ++++ .../document/sidebar/section-header.hbs | 13 +++++++++++-- .../document/sidebar/section-header.ts | 1 + web/app/modifiers/tooltip.ts | 17 ++++++++++++++++- 6 files changed, 34 insertions(+), 4 deletions(-) diff --git a/web/app/components/document/sidebar.hbs b/web/app/components/document/sidebar.hbs index f65444a50..6c452eca6 100644 --- a/web/app/components/document/sidebar.hbs +++ b/web/app/components/document/sidebar.hbs @@ -276,7 +276,7 @@ @allowAddingExternalLinks={{true}} @headerTitle="Related resources" @modalHeaderTitle="Add related resource" - @modalInputPlaceholder="Paste a URL or search documents..." + @modalInputPlaceholder="Search docs or paste a URL..." @scrollContainer={{this.body}} @optionalSearchFilters={{array (concat "product:" @document.product)}} /> diff --git a/web/app/components/document/sidebar/related-resources.hbs b/web/app/components/document/sidebar/related-resources.hbs index 3a9f2dbd9..897116da0 100644 --- a/web/app/components/document/sidebar/related-resources.hbs +++ b/web/app/components/document/sidebar/related-resources.hbs @@ -1,6 +1,7 @@ -
+
+ {{#if @titleTooltipText}} + + + + {{/if}} {{#if @badgeText}} {{/if}}
diff --git a/web/app/components/document/sidebar/section-header.ts b/web/app/components/document/sidebar/section-header.ts index b65bf671a..b11c2f9e9 100644 --- a/web/app/components/document/sidebar/section-header.ts +++ b/web/app/components/document/sidebar/section-header.ts @@ -6,6 +6,7 @@ interface DocumentSidebarSectionHeaderComponentSignature { Element: HTMLDivElement; Args: { title: string; + titleTooltipText?: string; badgeText?: string; buttonLabel?: string; buttonAction?: () => void; diff --git a/web/app/modifiers/tooltip.ts b/web/app/modifiers/tooltip.ts index 76ae12eb5..bfd1ae4e0 100644 --- a/web/app/modifiers/tooltip.ts +++ b/web/app/modifiers/tooltip.ts @@ -51,6 +51,7 @@ interface TooltipModifierNamedArgs { stayOpenOnClick?: boolean; delay?: number; openDuration?: number; + class?: string; _useTestDelay?: boolean; } @@ -171,6 +172,11 @@ export default class TooltipModifier extends Modifier */ @tracked stayOpenOnClick = false; + /** + * Optional classnames to append to the tooltip. + */ + @tracked class: string | null = null; + /** * An asserted-to-exist reference to the reference element. */ @@ -240,6 +246,11 @@ export default class TooltipModifier extends Modifier */ this.tooltip = document.createElement("div"); this.tooltip.classList.add("hermes-floating-ui-content", "hermes-tooltip"); + + if (this.class) { + this.tooltip.classList.add(this.class); + } + this.tooltip.setAttribute("id", `tooltip-${this.id}`); this.tooltip.setAttribute("role", "tooltip"); @@ -283,7 +294,7 @@ export default class TooltipModifier extends Modifier middleware: [ offset(8), flip(), - shift(), + shift({ padding: 20 }), arrow({ element: this.arrow, padding: 10, @@ -473,6 +484,10 @@ export default class TooltipModifier extends Modifier this.placement = named.placement; } + if (named.class) { + this.class = named.class; + } + if (named.stayOpenOnClick) { this.stayOpenOnClick = named.stayOpenOnClick; } From 77e1f9afc546f6c7dcf326666bdb69d76ae94fe0 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 20 Jul 2023 09:57:34 -0400 Subject: [PATCH 2/2] Add tests --- .../document/sidebar/related-resources.ts | 4 +- .../document/sidebar/section-header.hbs | 7 +-- web/app/components/tooltip-icon.hbs | 9 ++++ web/app/components/tooltip-icon.ts | 21 ++++++++ .../sidebar/related-resources-test.ts | 28 +++++++++++ .../components/tooltip-icon-test.ts | 50 +++++++++++++++++++ .../integration/modifiers/tooltip-test.ts | 13 ++++- 7 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 web/app/components/tooltip-icon.hbs create mode 100644 web/app/components/tooltip-icon.ts create mode 100644 web/tests/integration/components/tooltip-icon-test.ts diff --git a/web/app/components/document/sidebar/related-resources.ts b/web/app/components/document/sidebar/related-resources.ts index af8323e88..7df22d789 100644 --- a/web/app/components/document/sidebar/related-resources.ts +++ b/web/app/components/document/sidebar/related-resources.ts @@ -159,7 +159,9 @@ export default class DocumentSidebarRelatedResourcesComponent extends Component< } return documents; } - + /** + * The text passed to the TooltipIcon beside the title. + */ protected get titleTooltipText(): string { return `Documents and links that are relevant to this work.`; } diff --git a/web/app/components/document/sidebar/section-header.hbs b/web/app/components/document/sidebar/section-header.hbs index 0d7028533..26a59ee90 100644 --- a/web/app/components/document/sidebar/section-header.hbs +++ b/web/app/components/document/sidebar/section-header.hbs @@ -4,12 +4,7 @@ {{@title}} {{#if @titleTooltipText}} - - - + {{/if}} {{#if @badgeText}} + + diff --git a/web/app/components/tooltip-icon.ts b/web/app/components/tooltip-icon.ts new file mode 100644 index 000000000..d3b733756 --- /dev/null +++ b/web/app/components/tooltip-icon.ts @@ -0,0 +1,21 @@ +import Component from "@glimmer/component"; + +interface TooltipIconComponentSignature { + Element: HTMLSpanElement; + Args: { + text: string; + icon?: string; + }; +} + +export default class TooltipIconComponent extends Component { + protected get icon(): string { + return this.args.icon ?? "help"; + } +} + +declare module "@glint/environment-ember-loose/registry" { + export default interface Registry { + TooltipIcon: typeof TooltipIconComponent; + } +} diff --git a/web/tests/integration/components/document/sidebar/related-resources-test.ts b/web/tests/integration/components/document/sidebar/related-resources-test.ts index e8f324212..97019dacd 100644 --- a/web/tests/integration/components/document/sidebar/related-resources-test.ts +++ b/web/tests/integration/components/document/sidebar/related-resources-test.ts @@ -5,6 +5,7 @@ import { fillIn, find, render, + triggerEvent, waitFor, waitUntil, } from "@ember/test-helpers"; @@ -53,9 +54,12 @@ const ADD_EXTERNAL_RESOURCE_ERROR_SELECTOR = "[data-test-add-external-resource-error]"; const EDIT_EXTERNAL_RESOURCE_ERROR_SELECTOR = "[data-test-external-resource-title-error]"; +const TOOLTIP_TRIGGER_SELECTOR = "[data-test-tooltip-icon-trigger]"; +const TOOLTIP_SELECTOR = ".hermes-tooltip"; interface DocumentSidebarRelatedResourcesTestContext extends MirageTestContext { document: HermesDocument; + body: HTMLElement; } module( @@ -73,6 +77,8 @@ module( }); this.set("document", this.server.schema.document.first().attrs); + const bodyDiv = document.createElement("div"); + this.set("body", bodyDiv); }); test("it renders the related resources list", async function (this: DocumentSidebarRelatedResourcesTestContext, assert) { @@ -98,6 +104,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -113,6 +120,13 @@ module( .dom(HEADER_SELECTOR) .hasText("Test title", "the header title is correct"); + assert.dom(TOOLTIP_SELECTOR).doesNotExist(); + assert.dom(TOOLTIP_TRIGGER_SELECTOR).exists(); + await triggerEvent(TOOLTIP_TRIGGER_SELECTOR, "mouseenter"); + assert + .dom(".hermes-tooltip") + .hasText("Documents and links that are relevant to this work."); + assert.dom(BADGE_SELECTOR).hasText("New", "the 'new' badge is rendered'"); assert @@ -163,6 +177,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -194,6 +209,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -224,6 +240,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Test header" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -251,6 +268,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Test header" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -289,6 +307,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -325,6 +344,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -352,6 +372,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Test placeholder" + @scrollContainer={{this.body}} /> `); @@ -420,6 +441,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Test placeholder" + @scrollContainer={{this.body}} /> `); @@ -462,6 +484,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Test placeholder" + @scrollContainer={{this.body}} /> `); @@ -483,6 +506,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Test placeholder" + @scrollContainer={{this.body}} /> `); @@ -534,6 +558,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Add related resource" @modalInputPlaceholder="Test placeholder" + @scrollContainer={{this.body}} /> `); @@ -564,6 +589,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Test header" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -604,6 +630,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Test header" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); @@ -679,6 +706,7 @@ module( @headerTitle="Test title" @modalHeaderTitle="Test header" @modalInputPlaceholder="Paste a URL or search documents..." + @scrollContainer={{this.body}} /> `); diff --git a/web/tests/integration/components/tooltip-icon-test.ts b/web/tests/integration/components/tooltip-icon-test.ts new file mode 100644 index 000000000..2e3535fd3 --- /dev/null +++ b/web/tests/integration/components/tooltip-icon-test.ts @@ -0,0 +1,50 @@ +import { module, test } from "qunit"; +import { setupRenderingTest } from "ember-qunit"; +import { TestContext, render, triggerEvent } from "@ember/test-helpers"; +import { hbs } from "ember-cli-htmlbars"; + +interface TooltipIconComponentTestContext extends TestContext { + icon?: string; +} + +const TRIGGER_SELECTOR = "[data-test-tooltip-icon-trigger]"; +const TOOLTIP_SELECTOR = ".hermes-tooltip"; + +module("Integration | Component | tooltip-icon", function (hooks) { + setupRenderingTest(hooks); + + test("it renders correctly", async function (this: TooltipIconComponentTestContext, assert) { + this.set("icon", undefined); + + await render(hbs` + + `); + + assert + .dom(TRIGGER_SELECTOR) + .hasAttribute("data-test-icon", "help", "default icon shown") + .hasClass("foo", "splattributes handled"); + + assert.dom(TOOLTIP_SELECTOR).doesNotExist(); + + this.set("icon", "smile"); + + assert + .dom(TRIGGER_SELECTOR) + .hasAttribute( + "data-test-icon", + "smile", + "default icon can be overridden" + ); + + await triggerEvent(TRIGGER_SELECTOR, "mouseenter"); + + assert + .dom(TOOLTIP_SELECTOR) + .hasText("This is a tooltip", "tooltip text shown"); + }); +}); diff --git a/web/tests/integration/modifiers/tooltip-test.ts b/web/tests/integration/modifiers/tooltip-test.ts index 20d2466f9..d37800cc9 100644 --- a/web/tests/integration/modifiers/tooltip-test.ts +++ b/web/tests/integration/modifiers/tooltip-test.ts @@ -3,7 +3,6 @@ import { setupRenderingTest } from "ember-qunit"; import { render, triggerEvent, waitUntil } from "@ember/test-helpers"; import { hbs } from "ember-cli-htmlbars"; import htmlElement from "hermes/utils/html-element"; -import { wait } from "ember-animated/."; module("Integration | Modifier | tooltip", function (hooks) { setupRenderingTest(hooks); @@ -139,4 +138,16 @@ module("Integration | Modifier | tooltip", function (hooks) { assert.equal(tip.getAttribute("data-tooltip-state"), "closed"); }); + + test("you can pass a class to the tooltip", async function (assert) { + await render(hbs` +
+ Hover or focus me +
+ `); + + await triggerEvent(".tip", "mouseenter"); + + assert.dom(".hermes-tooltip").hasClass("foo"); + }); });