summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/widgets/tooltip/inactive-css-tooltip-helper.js
blob: 0c19df3e2ea74dd7d261255d905641a230f0cf8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

loader.lazyRequireGetter(
  this,
  "openDocLink",
  "resource://devtools/client/shared/link.js",
  true
);

class InactiveCssTooltipHelper {
  constructor() {
    this.addTab = this.addTab.bind(this);
  }

  /**
   * Fill the tooltip with inactive CSS information.
   *
   * @param {String} propertyName
   *        The property name to be displayed in bold.
   * @param {String} text
   *        The main text, which follows property name.
   */
  async setContent(data, tooltip) {
    const fragment = this.getTemplate(data, tooltip);
    const { doc } = tooltip;

    tooltip.panel.innerHTML = "";

    tooltip.panel.addEventListener("click", this.addTab);
    tooltip.once("hidden", () => {
      tooltip.panel.removeEventListener("click", this.addTab);
    });

    // Because Fluent is async we need to manually translate the fragment and
    // then insert it into the tooltip. This is needed in order for the tooltip
    // to size to the contents properly and for tests.
    await doc.l10n.translateFragment(fragment);
    doc.l10n.pauseObserving();
    tooltip.panel.appendChild(fragment);
    doc.l10n.resumeObserving();

    // Size the content.
    tooltip.setContentSize({ width: 267, height: Infinity });
  }

  /**
   * Get the template that the Fluent string will be merged with. This template
   * looks something like this but there is a variable amount of properties in the
   * fix section:
   *
   * <div class="devtools-tooltip-inactive-css">
   *   <p data-l10n-id="inactive-css-not-grid-or-flex-container"
   *      data-l10n-args="{&quot;property&quot;:&quot;align-content&quot;}">
   *   </p>
   *   <p data-l10n-id="inactive-css-not-grid-or-flex-container-fix">
   *     <span data-l10n-name="link" class="link"></span>
   *   </p>
   * </div>
   *
   * @param {Object} data
   *        An object in the following format: {
   *          fixId: "inactive-css-not-grid-item-fix-2", // Fluent id containing the
   *                                                     // Inactive CSS fix.
   *          msgId: "inactive-css-not-grid-item", // Fluent id containing the
   *                                               // Inactive CSS message.
   *          property: "color", // Property name
   *        }
   * @param {HTMLTooltip} tooltip
   *        The tooltip we are targetting.
   */
  getTemplate(data, tooltip) {
    const XHTML_NS = "http://www.w3.org/1999/xhtml";
    const { fixId, msgId, property, display, lineCount, learnMoreURL } = data;
    const { doc } = tooltip;

    const documentUrl = new URL(
      learnMoreURL || `https://developer.mozilla.org/docs/Web/CSS/${property}`
    );
    this._currentTooltip = tooltip;
    const { searchParams } = documentUrl;
    searchParams.append("utm_source", "devtools");
    searchParams.append("utm_medium", "inspector-inactive-css");
    this._currentUrl = documentUrl.toString();

    const templateNode = doc.createElementNS(XHTML_NS, "template");

    // eslint-disable-next-line
    templateNode.innerHTML = `
    <div class="devtools-tooltip-inactive-css">
      <p data-l10n-id="${msgId}"
         data-l10n-args='${JSON.stringify({ property, display, lineCount })}'>
      </p>
      <p data-l10n-id="${fixId}">
        <span data-l10n-name="link" class="link"></span>
      </p>
    </div>`;

    return doc.importNode(templateNode.content, true);
  }

  /**
   * Hide the tooltip, open `this._currentUrl` in a new tab and focus it.
   *
   * @param {DOMEvent} event
   *        The click event originating from the tooltip.
   *
   */
  addTab(event) {
    // The XUL panel swallows click events so handlers can't be added directly
    // to the link span. As a workaround we listen to all click events in the
    // panel and if a link span is clicked we proceed.
    if (event.target.className !== "link") {
      return;
    }

    const tooltip = this._currentTooltip;
    tooltip.hide();
    openDocLink(this._currentUrl);
  }

  destroy() {
    this._currentTooltip = null;
    this._currentUrl = null;
  }
}

module.exports = InactiveCssTooltipHelper;