@mdn/fred 2.4.3 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.nvmrc +1 -1
  2. package/CHANGELOG.md +48 -0
  3. package/README.md +2 -0
  4. package/components/a11y-menu/server.js +2 -2
  5. package/components/baseline-indicator/server.js +86 -32
  6. package/components/breadcrumbs/server.js +6 -1
  7. package/components/collection-save-button/element.js +15 -0
  8. package/components/color-theme/element.js +17 -1
  9. package/components/compat-section/server.js +45 -0
  10. package/components/compat-table/element.js +10 -1
  11. package/components/compat-table/utils.js +10 -0
  12. package/components/compat-table-lazy/element.js +2 -5
  13. package/components/content-section/server.css +5 -0
  14. package/components/content-section/server.js +4 -22
  15. package/components/curriculum/sidebar.css +1 -1
  16. package/components/curriculum-landing/server.js +4 -1
  17. package/components/dropdown/element.js +12 -1
  18. package/components/env/index.js +1 -1
  19. package/components/featured-articles/server.js +10 -3
  20. package/components/footer/server.js +6 -1
  21. package/components/generic-about/server.js +1 -0
  22. package/components/generic-toc/server.js +6 -1
  23. package/components/language-always-redirect-button/element.js +2 -0
  24. package/components/language-switcher/element.js +5 -0
  25. package/components/latest-news/server.js +10 -3
  26. package/components/left-sidebar/server.css +1 -1
  27. package/components/login-button/element.js +1 -1
  28. package/components/menu/server.js +3 -1
  29. package/components/observatory-form/element.js +3 -0
  30. package/components/observatory-results/element.js +6 -1
  31. package/components/observatory-results/rating.js +6 -1
  32. package/components/outer-layout/server.js +3 -3
  33. package/components/placement-top/element.js +16 -4
  34. package/components/playground/element.js +17 -4
  35. package/components/recent-contributions/server.js +4 -1
  36. package/components/reference-toc/server.js +5 -1
  37. package/components/scrim-inline/element.js +13 -1
  38. package/components/server/index.js +4 -0
  39. package/components/sidebar-filter/element.js +11 -0
  40. package/components/sidebar-filter/sidebar-filterer.js +4 -2
  41. package/components/specifications-list/server.js +72 -0
  42. package/components/translation-banner/server.js +4 -1
  43. package/components/user-menu/element.js +1 -0
  44. package/components/viewed-controller/viewed-controller.js +15 -59
  45. package/components/viewed-controller/viewed-observer.js +79 -0
  46. package/hooks/glean-init.js +49 -6
  47. package/l10n/locales/zh-CN.ftl +317 -1
  48. package/out/service-worker.js +1 -1
  49. package/out/static/client/{2173.c74593faf7e14cbc.js → 2173.a662dd40657c8426.js} +2 -2
  50. package/out/static/client/{2173.c74593faf7e14cbc.js.map → 2173.a662dd40657c8426.js.map} +1 -1
  51. package/out/static/client/{251.db6a4b10ef58ad3a.js → 251.f498960ce719d884.js} +6 -6
  52. package/out/static/client/251.f498960ce719d884.js.map +1 -0
  53. package/out/static/client/{2868.b09544148fff25a1.js → 2868.03299f12d5927ed2.js} +2 -2
  54. package/out/static/client/{2868.b09544148fff25a1.js.map → 2868.03299f12d5927ed2.js.map} +1 -1
  55. package/out/static/client/{291.91650ffed3a60424.js → 291.c90e43b3c10a9a1f.js} +27 -16
  56. package/out/static/client/291.c90e43b3c10a9a1f.js.map +1 -0
  57. package/out/static/client/{3259.ac8a5539251c647a.js → 3259.c4b809cd884ad515.js} +29 -24
  58. package/out/static/client/3259.c4b809cd884ad515.js.map +1 -0
  59. package/out/static/client/{3775.c64e09b2fc1d9b21.js → 3775.cf93e968f166d25a.js} +2 -1
  60. package/out/static/client/{3775.c64e09b2fc1d9b21.js.map → 3775.cf93e968f166d25a.js.map} +1 -1
  61. package/out/static/client/{4060.9a7bb116e3112c13.js → 4060.a23acd149888555c.js} +2 -2
  62. package/out/static/client/{4060.9a7bb116e3112c13.js.map → 4060.a23acd149888555c.js.map} +1 -1
  63. package/out/static/client/4249.19f86ee6b57a13b0.js +2 -0
  64. package/out/static/client/4249.19f86ee6b57a13b0.js.map +1 -0
  65. package/out/static/client/{4759.8981c79d33088d8f.js → 4759.5852733a45132491.js} +2 -2
  66. package/out/static/client/{4759.8981c79d33088d8f.js.map → 4759.5852733a45132491.js.map} +1 -1
  67. package/out/static/client/{4920.033c028023478df5.js → 4920.929203da4613c89a.js} +3 -2
  68. package/out/static/client/4920.929203da4613c89a.js.map +1 -0
  69. package/out/static/client/{5183.6c532a6e7b0d0bbb.js → 5183.48a6059ffe2931f0.js} +12 -12
  70. package/out/static/client/5183.48a6059ffe2931f0.js.map +1 -0
  71. package/out/static/client/{5408.a0622390fa1726dc.css → 5408.77c9b71d6a5df982.css} +2 -2
  72. package/out/static/client/{5408.a0622390fa1726dc.css.map → 5408.77c9b71d6a5df982.css.map} +1 -1
  73. package/out/static/client/{544.1ed814181b24bcc8.js → 544.2ccdc928c5292631.js} +3 -3
  74. package/out/static/client/544.2ccdc928c5292631.js.map +1 -0
  75. package/out/static/client/{5667.332b2c6c10792ad6.js → 5667.edd77c81ec38a7e2.js} +5 -4
  76. package/out/static/client/5667.edd77c81ec38a7e2.js.map +1 -0
  77. package/out/static/client/5685.d30658d4c274ac13.js +4 -0
  78. package/out/static/client/5685.d30658d4c274ac13.js.map +1 -0
  79. package/out/static/client/6002.bb83fdc66501b6e4.js +167 -0
  80. package/out/static/client/6002.bb83fdc66501b6e4.js.map +1 -0
  81. package/out/static/client/{6067.a60fb66b75579c98.js → 6067.169a9ec1f47871ae.js} +3 -3
  82. package/out/static/client/6067.169a9ec1f47871ae.js.map +1 -0
  83. package/out/static/client/6216.2a29b5ce2a1bf6f6.js +2 -0
  84. package/out/static/client/6216.2a29b5ce2a1bf6f6.js.map +1 -0
  85. package/out/static/client/{8364.13d1d6770264c970.js → 8364.f7862244a6ff6a1b.js} +3 -3
  86. package/out/static/client/8364.f7862244a6ff6a1b.js.map +1 -0
  87. package/out/static/client/{9204.f8aa5d9f8a79f3d4.js → 9204.f45896951abfbd28.js} +3 -3
  88. package/out/static/client/{9204.f8aa5d9f8a79f3d4.js.map → 9204.f45896951abfbd28.js.map} +1 -1
  89. package/out/static/client/9324.9646c5839948c8ff.js +27 -0
  90. package/out/static/client/9324.9646c5839948c8ff.js.map +1 -0
  91. package/out/static/client/9456.a17794342ff9086a.js +319 -0
  92. package/out/static/client/9456.a17794342ff9086a.js.map +1 -0
  93. package/out/static/client/index.c41c45d10cf2b50b.js +221 -0
  94. package/out/static/client/index.c41c45d10cf2b50b.js.map +1 -0
  95. package/out/static/client/{runtime.8ebca7827bf4c35c.js → runtime.796d2e8ab20fb93f.js} +2 -2
  96. package/out/static/client/runtime.796d2e8ab20fb93f.js.map +1 -0
  97. package/out/static/client/stats.json +142 -142
  98. package/out/static/client/{styles-content-section.049c2352791fac9b.css → styles-content-section.cd5635d274b730e9.css} +2 -2
  99. package/out/static/client/{styles-content-section.049c2352791fac9b.css.map → styles-content-section.cd5635d274b730e9.css.map} +1 -1
  100. package/out/static/client/{styles-global.445b59e1ff23f88c.js → styles-global.2a17ab4cc47bca1f.js} +1 -1
  101. package/out/static/client/{styles-global.3da376739551015c.css → styles-global.79b14873ea7ab386.css} +2 -2
  102. package/out/static/client/{styles-global.3da376739551015c.css.map → styles-global.79b14873ea7ab386.css.map} +1 -1
  103. package/out/static/client/{styles-left-sidebar.6f954e8939555f77.css → styles-left-sidebar.517b9efefbdeed1d.css} +2 -2
  104. package/out/static/client/{styles-left-sidebar.6f954e8939555f77.css.map → styles-left-sidebar.517b9efefbdeed1d.css.map} +1 -1
  105. package/out/static/legacy/6753.072394f4f09652a0.js +27 -0
  106. package/out/static/legacy/6753.072394f4f09652a0.js.map +1 -0
  107. package/out/static/legacy/asset-manifest.json +5 -5
  108. package/out/static/legacy/{index.85161ba7fe682ee8.html → index.c05c933009dda5b3.html} +1 -1
  109. package/out/static/legacy/stats.json +9 -9
  110. package/out/static/legacy/{yari.01d97d56e30935ec.js → yari.e457418f21984506.js} +3 -3
  111. package/out/static/legacy/{yari.01d97d56e30935ec.js.map → yari.e457418f21984506.js.map} +1 -1
  112. package/out/static/ssr/9456.js +317 -1
  113. package/out/static/ssr/9456.js.map +1 -1
  114. package/out/static/ssr/index.js +315 -235
  115. package/out/static/ssr/index.js.map +1 -1
  116. package/out/static/ssr/stats.json +4 -4
  117. package/package.json +14 -8
  118. package/components/specifications-list/index.js +0 -60
  119. package/out/static/client/251.db6a4b10ef58ad3a.js.map +0 -1
  120. package/out/static/client/291.91650ffed3a60424.js.map +0 -1
  121. package/out/static/client/3259.ac8a5539251c647a.js.map +0 -1
  122. package/out/static/client/4920.033c028023478df5.js.map +0 -1
  123. package/out/static/client/5183.6c532a6e7b0d0bbb.js.map +0 -1
  124. package/out/static/client/544.1ed814181b24bcc8.js.map +0 -1
  125. package/out/static/client/5649.00f25ce181894101.js +0 -2
  126. package/out/static/client/5649.00f25ce181894101.js.map +0 -1
  127. package/out/static/client/5667.332b2c6c10792ad6.js.map +0 -1
  128. package/out/static/client/5685.46c8d178d072b8ac.js +0 -4
  129. package/out/static/client/5685.46c8d178d072b8ac.js.map +0 -1
  130. package/out/static/client/6067.a60fb66b75579c98.js.map +0 -1
  131. package/out/static/client/6216.f526aaf8b3dedd72.js +0 -2
  132. package/out/static/client/6216.f526aaf8b3dedd72.js.map +0 -1
  133. package/out/static/client/718.3a790c4c45336906.js +0 -167
  134. package/out/static/client/718.3a790c4c45336906.js.map +0 -1
  135. package/out/static/client/8364.13d1d6770264c970.js.map +0 -1
  136. package/out/static/client/9324.9bb064ea7377127e.js +0 -26
  137. package/out/static/client/9324.9bb064ea7377127e.js.map +0 -1
  138. package/out/static/client/9456.dd8c12ddf52e2a7d.js +0 -3
  139. package/out/static/client/9456.dd8c12ddf52e2a7d.js.map +0 -1
  140. package/out/static/client/index.b2ccf995e945e9dc.js +0 -221
  141. package/out/static/client/index.b2ccf995e945e9dc.js.map +0 -1
  142. package/out/static/client/runtime.8ebca7827bf4c35c.js.map +0 -1
  143. package/out/static/legacy/6753.811fcf40c897d487.js +0 -27
  144. package/out/static/legacy/6753.811fcf40c897d487.js.map +0 -1
  145. /package/out/static/client/{291.91650ffed3a60424.js.LICENSE.txt → 291.c90e43b3c10a9a1f.js.LICENSE.txt} +0 -0
  146. /package/out/static/client/{5183.6c532a6e7b0d0bbb.js.LICENSE.txt → 5183.48a6059ffe2931f0.js.LICENSE.txt} +0 -0
  147. /package/out/static/client/{6067.a60fb66b75579c98.js.LICENSE.txt → 6067.169a9ec1f47871ae.js.LICENSE.txt} +0 -0
  148. /package/out/static/client/{8364.13d1d6770264c970.js.LICENSE.txt → 8364.f7862244a6ff6a1b.js.LICENSE.txt} +0 -0
  149. /package/out/static/client/{index.b2ccf995e945e9dc.js.LICENSE.txt → index.c41c45d10cf2b50b.js.LICENSE.txt} +0 -0
  150. /package/out/static/legacy/{6753.811fcf40c897d487.js.LICENSE.txt → 6753.072394f4f09652a0.js.LICENSE.txt} +0 -0
  151. /package/out/static/legacy/{yari.01d97d56e30935ec.js.LICENSE.txt → yari.e457418f21984506.js.LICENSE.txt} +0 -0
@@ -186,6 +186,7 @@ export class Footer extends ServerComponent {
186
186
  rel="noopener"
187
187
  aria-label=${item.ariaLabel}
188
188
  data-icon=${item.icon}
189
+ data-glean-id=${`footer: social -> ${item.icon}`}
189
190
  ></a>
190
191
  </li>
191
192
  `,
@@ -206,6 +207,7 @@ export class Footer extends ServerComponent {
206
207
  class=${link.external ? "external" : ""}
207
208
  target=${link.external ? "_blank" : undefined}
208
209
  rel=${link.external ? "noopener" : undefined}
210
+ data-glean-id=${`footer: link -> ${link.href}`}
209
211
  >
210
212
  ${link.text}
211
213
  </a>
@@ -230,7 +232,10 @@ export class Footer extends ServerComponent {
230
232
  ${mozillaLinks(context).map(
231
233
  (item) => html`
232
234
  <li>
233
- <a href=${item.href} class=${item.external ? "external" : ""}
235
+ <a
236
+ href=${item.href}
237
+ class=${item.external ? "external" : ""}
238
+ data-glean-id=${`footer: mozilla -> ${item.href}`}
234
239
  >${item.text}</a
235
240
  >
236
241
  </li>
@@ -80,6 +80,7 @@ export class GenericAbout extends ServerComponent {
80
80
  slot="tab"
81
81
  href="#${h3Value.id}"
82
82
  data-panel-id=${h3Value.id}
83
+ data-glean-id=${`about: tab -> ${h3Value.id}`}
83
84
  >
84
85
  ${h3Value.title}
85
86
  </a>
@@ -25,7 +25,12 @@ export class GenericToc extends ServerComponent {
25
25
  ${context.hyData.toc.map(
26
26
  ({ id, text }) =>
27
27
  html`<li class="generic-toc__item">
28
- <a class="generic-toc__link" href="#${id}">${text}</a>
28
+ <a
29
+ class="generic-toc__link"
30
+ href="#${id}"
31
+ data-glean-id="toc_click: #${id}"
32
+ >${text}</a
33
+ >
29
34
  </li>`,
30
35
  )}
31
36
  </ul>
@@ -1,5 +1,6 @@
1
1
  import { LitElement, html } from "lit";
2
2
 
3
+ import { gleanClick } from "../../utils/glean.js";
3
4
  import { setPreferredLocale } from "../preferred-locale/utils.js";
4
5
 
5
6
  export class MDNLanguageAlwaysRedirectButton extends LitElement {
@@ -15,6 +16,7 @@ export class MDNLanguageAlwaysRedirectButton extends LitElement {
15
16
  }
16
17
 
17
18
  _handleClick() {
19
+ gleanClick(`language: ${this.locale} -> ${this.to} (always)`);
18
20
  setPreferredLocale(this.to);
19
21
  const url = document.location.pathname.replace(
20
22
  `/${this.locale}/`,
@@ -2,6 +2,7 @@ import { Task } from "@lit/task";
2
2
  import { LitElement, html, nothing } from "lit";
3
3
 
4
4
  import { L10nMixin } from "../../l10n/mixin.js";
5
+ import { gleanClick } from "../../utils/glean.js";
5
6
 
6
7
  import infoIcon from "../icon/info.svg?lit";
7
8
  import { getEnglishDoc } from "../not-found/utils.js";
@@ -63,12 +64,15 @@ export class MDNLanguageSwitcher extends L10nMixin(LitElement) {
63
64
 
64
65
  _togglePreferredLocale() {
65
66
  if (this.notFound) return;
67
+ const oldValue = this._preferredLocale ?? "0";
66
68
  if (this._isLocalePreferred) {
67
69
  resetPreferredLocale();
68
70
  this._preferredLocale = undefined;
71
+ gleanClick(`language_remember: ${oldValue} -> 0`);
69
72
  } else {
70
73
  setPreferredLocale(this.locale);
71
74
  this._preferredLocale = this.locale;
75
+ gleanClick(`language_remember: ${oldValue} -> ${this.locale}`);
72
76
  }
73
77
  }
74
78
 
@@ -160,6 +164,7 @@ export class MDNLanguageSwitcher extends L10nMixin(LitElement) {
160
164
  `/${notFound ? "en-US" : locale}/`,
161
165
  `/${translation.locale}/`,
162
166
  )}
167
+ data-glean-id=${`language: ${locale} -> ${translation.locale}`}
163
168
  >${translation.native}</a
164
169
  >
165
170
  </li>
@@ -14,16 +14,23 @@ export class LatestNews extends ServerComponent {
14
14
 
15
15
  return html`<ul class="latest-news">
16
16
  ${items.map(
17
- (item) =>
17
+ (item, index) =>
18
18
  html`<li>
19
19
  <article class="latest-news__item">
20
20
  ${item.source
21
- ? html`<a class="latest-news__source" href=${item.source.url}
21
+ ? html`<a
22
+ class="latest-news__source"
23
+ href=${item.source.url}
24
+ data-glean-id=${`homepage: news_source ${index + 1} -> ${item.source.url}`}
22
25
  >${item.source.name}</a
23
26
  >`
24
27
  : nothing}
25
28
  <h3 class="latest-news__title">
26
- <a href=${item.url}>${item.title}</a>
29
+ <a
30
+ href=${item.url}
31
+ data-glean-id=${`homepage: news ${index + 1} -> ${item.url}`}
32
+ >${item.title}</a
33
+ >
27
34
  </h3>
28
35
  <time class="latest-news__date" datetime=${item.published_at}>
29
36
  ${rtf.format(
@@ -115,7 +115,7 @@
115
115
  }
116
116
 
117
117
  a,
118
- span:not(.highlight-container) {
118
+ span:not(.sidebar-filter-mark-container) {
119
119
  display: inline-block;
120
120
  padding-block: 0.25rem;
121
121
  vertical-align: middle;
@@ -16,7 +16,7 @@ export class MDNLoginButton extends L10nMixin(LitElement) {
16
16
  }
17
17
 
18
18
  render() {
19
- return html`<mdn-button href=${this._loginUrl}
19
+ return html`<mdn-button href=${this._loginUrl} data-glean-id="login_button"
20
20
  >${this.l10n("login-button-login")`Login`}</mdn-button
21
21
  >`;
22
22
  }
@@ -71,7 +71,9 @@ export class Menu extends ServerComponent {
71
71
  data-glean-id=${`menu_click_link: top-level -> ${tab.href}`}
72
72
  >${tab.buttonText}</a
73
73
  >`
74
- : html`<mdn-dropdown>
74
+ : html`<mdn-dropdown
75
+ data-glean-toggle-open=${`menu_toggle: ${tab.id}`}
76
+ >
75
77
  <button class="menu__tab-button" type="button" slot="button">
76
78
  ${typeof tab.buttonText === "string"
77
79
  ? html`<span class="menu__tab-label"
@@ -1,6 +1,7 @@
1
1
  import { LitElement, html, nothing } from "lit";
2
2
  import { createRef, ref } from "lit/directives/ref.js";
3
3
 
4
+ import { gleanClick } from "../../utils/glean.js";
4
5
  import { OBSERVATORY_API_URL } from "../env/index.js";
5
6
  import "../progress-bar/element.js";
6
7
  import "../button/element.js";
@@ -61,6 +62,7 @@ export class MDNObservatoryForm extends LitElement {
61
62
  this._hostname = host;
62
63
  }
63
64
  this._queryRunning = true;
65
+ gleanClick("observatory: scan");
64
66
  try {
65
67
  const apiUrl = new URL(
66
68
  OBSERVATORY_API_URL +
@@ -77,6 +79,7 @@ export class MDNObservatoryForm extends LitElement {
77
79
  } catch (error) {
78
80
  // @ts-expect-error
79
81
  this._errorMessage = `${ERROR_MAP[error.name] || "message" in error ? error["message"] : error}`;
82
+ gleanClick(`observatory: error -> ${this._errorMessage}`);
80
83
  } finally {
81
84
  this._queryRunning = false;
82
85
  }
@@ -2,6 +2,7 @@ import { Task } from "@lit/task";
2
2
  import { LitElement, html } from "lit";
3
3
  import { nothing } from "lit";
4
4
 
5
+ import { gleanClick } from "../../utils/glean.js";
5
6
  import { OBSERVATORY_API_URL } from "../env/index.js";
6
7
 
7
8
  import styles from "./element.css?lit";
@@ -62,7 +63,9 @@ export class MDNObservatoryResults extends LitElement {
62
63
  }
63
64
  return await res.json();
64
65
  } catch (error) {
65
- throw new Error("Observatory API request for scan data failed", {
66
+ const message = "Observatory API request for scan data failed";
67
+ gleanClick(`observatory: error -> ${message}`);
68
+ throw new Error(message, {
66
69
  cause: error,
67
70
  });
68
71
  }
@@ -116,6 +119,7 @@ export class MDNObservatoryResults extends LitElement {
116
119
  if (!this.host) {
117
120
  return;
118
121
  }
122
+ gleanClick("observatory: rescan");
119
123
  this._usePostInApi = true;
120
124
  this._apiTask.run();
121
125
  }
@@ -127,6 +131,7 @@ export class MDNObservatoryResults extends LitElement {
127
131
  */
128
132
  _handleTabSelect(index, key) {
129
133
  this.selectedTab = index;
134
+ gleanClick(`observatory: tab -> ${key}`);
130
135
  globalThis.history.replaceState(
131
136
  "",
132
137
  "",
@@ -65,7 +65,12 @@ export function Rating({ result, host, rescan }) {
65
65
  @click=${rescan}
66
66
  ></mdn-observatory-rescan-button>
67
67
  <div class="scan-another">
68
- <a href="/en-US/observatory"> Scan another website </a>
68
+ <a
69
+ href="/en-US/observatory"
70
+ data-glean-id="observatory: scan-another"
71
+ >
72
+ Scan another website
73
+ </a>
69
74
  </div>
70
75
  </section>
71
76
  </section>
@@ -6,7 +6,7 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js";
6
6
  import inlineScript from "../../entry.inline.js?source&csp=true";
7
7
  import {
8
8
  ROBOTS_GLOBAL_ALLOW,
9
- TRANSCEND_BUNDLE_ID,
9
+ TRANSCEND_AIRGAP_URL,
10
10
  WRITER_MODE,
11
11
  } from "../env/index.js";
12
12
  import { RUNTIME_ENV, runtimeVariables } from "../env/runtime.js";
@@ -116,12 +116,12 @@ export class OuterLayout extends ServerComponent {
116
116
  fetchpriority="low"
117
117
  />`,
118
118
  )}
119
- ${TRANSCEND_BUNDLE_ID &&
119
+ ${TRANSCEND_AIRGAP_URL &&
120
120
  html`<script
121
121
  data-cfasync="false"
122
122
  data-report-only="on"
123
123
  data-prompt="0"
124
- src=${`https://transcend-cdn.com/cm/${TRANSCEND_BUNDLE_ID}/airgap.js`}
124
+ src=${TRANSCEND_AIRGAP_URL}
125
125
  ></script>`}
126
126
  ${scripts?.map(
127
127
  (path) => html`<script src=${path} type="module"></script>`,
@@ -1,12 +1,14 @@
1
1
  import { LitElement, html, nothing } from "lit";
2
- import { ref } from "lit/directives/ref.js";
2
+ import { createRef, ref } from "lit/directives/ref.js";
3
3
  import { styleMap } from "lit/directives/style-map.js";
4
4
 
5
+ import { gleanClick } from "../../utils/glean.js";
6
+ import { PlacementMixin } from "../placement/mixin.js";
7
+ import { ViewedController } from "../viewed-controller/viewed-controller.js";
8
+
5
9
  import "../placement-note/element.js";
6
10
  import "../placement-no/element.js";
7
11
 
8
- import { PlacementMixin } from "../placement/mixin.js";
9
-
10
12
  import styles from "./element.css?lit";
11
13
 
12
14
  /**
@@ -17,6 +19,16 @@ import styles from "./element.css?lit";
17
19
  export class MDNPlacementTop extends PlacementMixin(LitElement) {
18
20
  static styles = styles;
19
21
 
22
+ /** @type {import("lit/directives/ref.js").Ref<HTMLElement>} */
23
+ _fallbackRef = createRef();
24
+
25
+ constructor() {
26
+ super();
27
+ new ViewedController(this, this._fallbackRef, () => {
28
+ gleanClick("banner_scrimba_view");
29
+ });
30
+ }
31
+
20
32
  /**
21
33
  *
22
34
  * @returns {TemplateResult | symbol}
@@ -27,7 +39,7 @@ export class MDNPlacementTop extends PlacementMixin(LitElement) {
27
39
 
28
40
  renderFallback() {
29
41
  return html`
30
- <div class="fallback">
42
+ <div ${ref(this._fallbackRef)} class="fallback">
31
43
  <p class="fallback__copy">
32
44
  Learn frontend, backend, and AI from our course partner
33
45
  <a
@@ -342,6 +342,7 @@ ${"```"}`,
342
342
  @click=${this._share}
343
343
  ?disabled=${!hasCode}
344
344
  data-id="share"
345
+ data-glean-id="playground: share-click"
345
346
  >${this.l10n("playground-share")`Share`}</mdn-button
346
347
  >
347
348
  <mdn-button
@@ -356,6 +357,7 @@ ${"```"}`,
356
357
  variant="secondary"
357
358
  @click=${this._reset}
358
359
  ?disabled=${!isResettable}
360
+ data-glean-id="playground: reset-click"
359
361
  >${this.l10n("playground-reset")`Reset`}</mdn-button
360
362
  >`
361
363
  : nothing}
@@ -391,6 +393,7 @@ ${"```"}`,
391
393
  @click=${this._reportOpen}
392
394
  variant="secondary"
393
395
  .icon=${warningIcon}
396
+ data-glean-id="playground: flag-click"
394
397
  >
395
398
  ${this.l10n(
396
399
  "playground-seeing-something-inappropriate",
@@ -437,8 +440,14 @@ ${"```"}`,
437
440
  )`Share your code via Permalink`}
438
441
  </h2>
439
442
  ${this._user.render({
440
- initial: () => html`<mdn-login-button></mdn-login-button>`,
441
- pending: () => html`<mdn-login-button></mdn-login-button>`,
443
+ initial: () =>
444
+ html`<mdn-login-button
445
+ data-glean-id="playground: banner-login"
446
+ ></mdn-login-button>`,
447
+ pending: () =>
448
+ html`<mdn-login-button
449
+ data-glean-id="playground: banner-login"
450
+ ></mdn-login-button>`,
442
451
  complete: (user) =>
443
452
  user.isAuthenticated
444
453
  ? this._permalink && !isResettable
@@ -453,12 +462,16 @@ ${"```"}`,
453
462
  )`Copy to clipboard`}</mdn-button
454
463
  >
455
464
  `
456
- : html`<mdn-button @click=${this._createPermalink}
465
+ : html`<mdn-button
466
+ @click=${this._createPermalink}
467
+ data-glean-id="playground: share-permalink"
457
468
  >${this.l10n(
458
469
  "playground-create-link",
459
470
  )`Create link`}</mdn-button
460
471
  >`
461
- : html`<mdn-login-button></mdn-login-button>`,
472
+ : html`<mdn-login-button
473
+ data-glean-id="playground: banner-login"
474
+ ></mdn-login-button>`,
462
475
  })}
463
476
  </section>
464
477
  </mdn-modal>
@@ -33,7 +33,10 @@ export class RecentContributions extends ServerComponent {
33
33
  )}
34
34
  </time>
35
35
  <div class="recent-contributions__title">
36
- <a class="external" href=${contribution.url}
36
+ <a
37
+ class="external"
38
+ href=${contribution.url}
39
+ data-glean-id=${`homepage: contribution ${index + 1} -> ${contribution.url}`}
37
40
  >${contribution.title}</a
38
41
  >
39
42
  </div>
@@ -21,7 +21,11 @@ export class ReferenceToc extends ServerComponent {
21
21
  <ul>
22
22
  ${toc?.map(
23
23
  ({ id, text }) =>
24
- html`<li><a href="#${id}">${unsafeHTML(text)}</a></li>`,
24
+ html`<li>
25
+ <a href="#${id}" data-glean-id="toc_click: #${id}"
26
+ >${unsafeHTML(text)}</a
27
+ >
28
+ </li>`,
25
29
  )}
26
30
  </ul>
27
31
  </nav>`;
@@ -1,8 +1,11 @@
1
1
  import { LitElement, html, nothing } from "lit";
2
2
  import { ifDefined } from "lit/directives/if-defined.js";
3
+ import { createRef, ref } from "lit/directives/ref.js";
3
4
  import { styleMap } from "lit/directives/style-map.js";
4
5
 
5
6
  import { L10nMixin } from "../../l10n/mixin.js";
7
+ import { gleanClick } from "../../utils/glean.js";
8
+ import { ViewedController } from "../viewed-controller/viewed-controller.js";
6
9
 
7
10
  import playSvg from "./assets/scrim-play.svg?lit";
8
11
  import styles from "./element.css?lit";
@@ -20,6 +23,9 @@ export class MDNScrimInline extends L10nMixin(LitElement) {
20
23
  _scrimLoaded: { state: true },
21
24
  };
22
25
 
26
+ /** @type {import("lit/directives/ref.js").Ref<HTMLElement>} */
27
+ _bodyRef = createRef();
28
+
23
29
  constructor() {
24
30
  super();
25
31
  /** @type {string | undefined} */
@@ -41,6 +47,12 @@ export class MDNScrimInline extends L10nMixin(LitElement) {
41
47
  this._fullscreen = false;
42
48
  /** @type {boolean} */
43
49
  this._scrimLoaded = false;
50
+
51
+ new ViewedController(this, this._bodyRef, () => {
52
+ if (this._scrimId) {
53
+ gleanClick(`curriculum: scrim view id:${this._scrimId}`);
54
+ }
55
+ });
44
56
  }
45
57
 
46
58
  /**
@@ -109,7 +121,7 @@ export class MDNScrimInline extends L10nMixin(LitElement) {
109
121
  >
110
122
  </a>
111
123
  </div>
112
- <div class="body">
124
+ <div class="body" ${ref(this._bodyRef)}>
113
125
  ${this._scrimLoaded
114
126
  ? html`
115
127
  <iframe
@@ -13,6 +13,9 @@ export class ServerComponent {
13
13
  /** @type {string | undefined} */
14
14
  static inlineScript;
15
15
 
16
+ /** `true` if we're rendering in a renderSimplified context */
17
+ simplifiedMode = false;
18
+
16
19
  /**
17
20
  * @template {typeof ServerComponent} T
18
21
  * @this {T}
@@ -28,6 +31,7 @@ export class ServerComponent {
28
31
  const component = new this();
29
32
 
30
33
  if ("renderSimplified" in asyncStore) {
34
+ component.simplifiedMode = true;
31
35
  return component.renderSimplified(...args);
32
36
  }
33
37
 
@@ -5,6 +5,7 @@
5
5
  import { LitElement, html } from "lit";
6
6
 
7
7
  import { L10nMixin } from "../../l10n/mixin.js";
8
+ import { gleanClick } from "../../utils/glean.js";
8
9
  import cancelIcon from "../icon/cancel.svg?lit";
9
10
 
10
11
  import styles from "./element.css?lit";
@@ -91,6 +92,7 @@ class MDNSidebarFilter extends L10nMixin(LitElement) {
91
92
  // Mark that the user has typed if the query is non-empty.
92
93
  if (this.query && this.query.trim().length > 0 && !this.hasTyped) {
93
94
  this.hasTyped = true;
95
+ gleanClick("sidebar_filter_typed");
94
96
  }
95
97
 
96
98
  if (this._quicklinks) {
@@ -116,6 +118,14 @@ class MDNSidebarFilter extends L10nMixin(LitElement) {
116
118
  }
117
119
  }
118
120
 
121
+ /**
122
+ * Event handler for focus events on the text field.
123
+ * @private
124
+ */
125
+ _onFocus() {
126
+ gleanClick("sidebar_filter_focus");
127
+ }
128
+
119
129
  /**
120
130
  * Event handler for input events on the text field.
121
131
  * @param {Event} event The input event.
@@ -152,6 +162,7 @@ class MDNSidebarFilter extends L10nMixin(LitElement) {
152
162
  type="text"
153
163
  placeholder=${this.l10n("sidebar-filter-filter")`Filter`}
154
164
  .value=${this.query}
165
+ @focus=${this._onFocus}
155
166
  @input=${this._onInput}
156
167
  />
157
168
  ${this.matchCount === undefined
@@ -154,7 +154,9 @@ export class SidebarFilterer {
154
154
  * @param {HTMLAnchorElement} link The link element to reset.
155
155
  */
156
156
  resetHighlighting(link) {
157
- const nodes = [...link.querySelectorAll("span, mark")];
157
+ const nodes = [
158
+ ...link.querySelectorAll("span.sidebar-filter-mark-container, mark"),
159
+ ];
158
160
  const parents = new Set();
159
161
  for (const node of nodes) {
160
162
  const parent = node.parentElement;
@@ -250,7 +252,7 @@ export class SidebarFilterer {
250
252
  );
251
253
 
252
254
  const span = this.replaceChildNode(node, "span");
253
- span.className = "highlight-container";
255
+ span.className = "sidebar-filter-mark-container";
254
256
 
255
257
  /** @type {Text|undefined} */
256
258
  const initialRest = [...span.childNodes].find(
@@ -0,0 +1,72 @@
1
+ import { html } from "@lit-labs/ssr";
2
+ import { join } from "lit/directives/join.js";
3
+
4
+ import { ServerComponent } from "../server/index.js";
5
+
6
+ export class SpecificationsList extends ServerComponent {
7
+ /**
8
+ * @param {import("@fred").Context} context
9
+ * @param {import("@rari").Specification[]} specifications
10
+ */
11
+ render(context, specifications) {
12
+ if (specifications.length === 0) {
13
+ return html`${context.l10n(
14
+ "specifications-list-this-feature-does-not-appear-to",
15
+ )`This feature does not appear to be defined in any specification.`}`;
16
+ }
17
+
18
+ /** @type {Map<string, string[]>} */
19
+ const urlsByTitle = new Map();
20
+ for (const { title, bcdSpecificationURL: url } of specifications) {
21
+ const urls = urlsByTitle.get(title) ?? [];
22
+ urls.push(url);
23
+ urlsByTitle.set(title, urls);
24
+ }
25
+ const links = urlsByTitle
26
+ .entries()
27
+ .flatMap(([title, urls]) =>
28
+ urls.map((url) => this.renderLink(url, title)),
29
+ );
30
+
31
+ if (this.simplifiedMode) {
32
+ return html`<ul>
33
+ ${links.map((link) => html`<li>${link}</li>`)}
34
+ </ul>`;
35
+ }
36
+ return html`<table>
37
+ <thead>
38
+ <tr>
39
+ <th scope="col">
40
+ ${context.l10n("specifications-list-specification")`Specification`}
41
+ </th>
42
+ </tr>
43
+ </thead>
44
+ <tbody>
45
+ ${links.map(
46
+ (link) =>
47
+ html`<tr>
48
+ <td>${link}</td>
49
+ </tr>`,
50
+ )}
51
+ </tbody>
52
+ </table>`;
53
+ }
54
+
55
+ /**
56
+ * @param {string} url
57
+ * @param {string} [title]
58
+ */
59
+ renderLink(url, title) {
60
+ const hash = url.split("#")[1];
61
+
62
+ const label = [
63
+ title && html`${title}`,
64
+ title && hash && html`<br />`,
65
+ hash && html`# ${hash}`,
66
+ ].filter(Boolean);
67
+
68
+ return html`<a class="external" href=${url} rel="noopener" target="_blank"
69
+ >${join(label, "")}</a
70
+ >`;
71
+ }
72
+ }
@@ -99,7 +99,10 @@ export class TranslationBanner extends ServerComponent {
99
99
  // Note: Do not translate, this is intentionally in English.
100
100
 
101
101
  return html`<p class="translation-banner__switch" lang="en-US">
102
- <mdn-button data-variant="secondary" href=${url}
102
+ <mdn-button
103
+ data-variant="secondary"
104
+ href=${url}
105
+ data-glean-id=${`language: ${context.locale} -> en-US (view)`}
103
106
  >View in English</mdn-button
104
107
  >
105
108
  <mdn-language-always-redirect-button locale=${context.locale} to="en-US"
@@ -144,6 +144,7 @@ export class MDNUserMenu extends L10nMixin(LitElement) {
144
144
  href=${this.#loginUrl()}
145
145
  .icon=${logInIcon}
146
146
  variant="plain"
147
+ data-glean-id="top_nav: login"
147
148
  >
148
149
  ${this.l10n("login")}
149
150
  </mdn-button>