• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / blurts-server / #12992

pending completion
#12992

push

circleci

web-flow
MNTOR-1437 Do not use spaces in event names, per GA4's event naming rules (#2943)

* We should not use spaces in event names, per GA4's event naming rules: https://support.google.com/analytics/answer/13316687

I also went over and instrumented a few more success/fail cases, they are sent in a `result` parameter.

* Update src/client/js/components/toast-alert.js

Co-authored-by: Vincent <Vinnl@users.noreply.github.com>

---------

Co-authored-by: Vincent <Vinnl@users.noreply.github.com>

282 of 1599 branches covered (17.64%)

Branch coverage included in aggregate %.

14 of 14 new or added lines in 7 files covered. (100.0%)

959 of 4310 relevant lines covered (22.25%)

1.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/client/js/components/toast-alert.js
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4

5
/**
6
 * Toast alert
7
 *
8
 * Displays a short message towards the top of user's screen,
9
 * and auto removes itself after a period of time (default ~7s)
10
 *
11
 * Client JS examples:
12
 * ```
13
 * const toast = document.createElement('toast-alert')
14
 * toast.textContent = 'Alert message here'
15
 * document.body.append(toast)
16
 * ```
17
 *
18
 * ```
19
 * const toast = document.createElement('toast-alert')
20
 * toast.textContent = 'Another alert message here'
21
 * toast.ttl = 10 // seconds before fade-out (defaults to 7)
22
 * toast.type = 'error' // 'error' (default) or 'success'
23
 * document.body.append(toast)
24
 * ```
25
 *
26
 * SSR/HTML examples:
27
 * ```
28
 * <toast-alert>Alert message here</toast-alert>
29
 * <toast-alert ttl='10' type='error'>Another alert message here</toast-alert>
30
 * ```
31
 */
32

33
const html = `
×
34
<style>
35
  :host{
36
    contain: layout style;
37
    position: fixed;
38
    top: var(--padding-md);
39
    left: 0;
40
    width: 100%;
41
    text-align: center;
42
    font-size: .875rem;
43
    color: white;
44
    transform: translateY(var(--toast-y, 0));
45
    transition: transform 0.3s;
46
    animation: fade-out 0.6s var(--ttl, 7s) forwards;
47
    z-index: 2;
48
    pointer-events: none;
49
  }
50

51
  :host(:hover){
52
    animation-play-state: paused;
53
  }
54

55
  :host([hidden]) {
56
    display: none 
57
  }
58

59
  output{
60
    position: relative;
61
    display: inline-block;
62
    padding: var(--padding-sm) var(--padding-xl);
63
    border-radius: var(--border-radius);
64
    box-shadow: 0 0 6px -3px black;
65
    animation: fly-in 0.3s forwards;
66
    pointer-events: auto;
67
  }
68

69
  :host([type="error"]) output {
70
    background-color: var(--red-70);
71
  }
72

73
  :host([type="success"]) output {
74
    background-color: var(--green-80);
75
  }
76

77
  button{
78
    position: absolute;
79
    top: 0;
80
    right: 0;
81
    height: 100%;
82
    padding: 0 var(--padding-md);
83
    border: none;
84
    cursor: pointer;
85
    font: inherit;
86
    color: inherit;
87
    background-color: transparent;
88
  }
89

90
  button:hover{
91
    box-shadow: inset 0 0 64px #fc95;
92
  }
93

94
  @keyframes fly-in{
95
    from{
96
      opacity: 0;
97
      transform: translateY(-30%);
98
    }
99
    to{
100
      opacity: 1;
101
    }
102
  }
103

104
  @keyframes fade-out{
105
    from{
106
      opacity: 1;
107
    }
108
    to{
109
      opacity: 0;
110
    }
111
  }
112
</style>
113

114
<output>
115
  <slot></slot>
116
  <button aria-label="Close">✕</button>
117
</output>
118
`
119

120
const ToastTypes = {
×
121
  Error: 'error',
122
  Success: 'success'
123
}
124

125
customElements.define('toast-alert', class extends HTMLElement {
×
126
  constructor () {
127
    super()
×
128
    this.attachShadow({ mode: 'open' })
×
129
    this.shadowRoot.innerHTML = html
×
130
  }
131

132
  get ttl () {
133
    return this.getAttribute('ttl')
×
134
  }
135

136
  set ttl (value) {
137
    this.setAttribute('ttl', value)
×
138
    this.style.setProperty('--ttl', `${value}s`) // seconds before fade-out starts
×
139
  }
140

141
  get type () {
142
    return this.getAttribute('type')
×
143
  }
144

145
  set type (value) {
146
    const isValidType = Object.values(ToastTypes).includes(value)
×
147
    if (!isValidType) {
×
148
      console.warning(`Unknown toast type ${value}.`)
×
149
    }
150

151
    this.setAttribute('type', value)
×
152
  }
153

154
  connectedCallback () {
155
    const toasts = Array.from(document.querySelectorAll('toast-alert')).reverse()
×
156

157
    for (let i = 1, y = 0; i < toasts.length; i++) {
×
158
      // start at index 1 to push old toasts down with aggregated toast heights plus 10px gap
159
      y += toasts[i - 1].getBoundingClientRect().height + 10
×
160
      toasts[i].style.setProperty('--toast-y', `${y}px`)
×
161
    }
162

163
    if (this.hasAttribute('ttl')) {
×
164
      this.ttl = this.getAttribute('ttl')
×
165
    }
166

167
    if (this.hasAttribute('type')) {
×
168
      this.type = this.getAttribute('type')
×
169
    } else {
170
      this.type = ToastTypes.Error
×
171
    }
172

173
    this.shadowRoot.addEventListener('click', this)
×
174
    this.addEventListener('animationend', this)
×
175
  }
176

177
  handleEvent (e) {
178
    switch (true) {
×
179
      case e.target.matches('button'):
180
        this.remove()
×
181
        window.gtag('event', 'toast_alert', { action: 'dismiss' })
×
182
        break
×
183
      case e.animationName === 'fade-out':
184
        this.remove()
×
185
        window.gtag('event', 'toast_alert', { action: 'faded' })
×
186
        break
×
187
    }
188
  }
189

190
  disconnectedCallback () {
191
    this.shadowRoot.removeEventListener('click', this)
×
192
    this.removeEventListener('animationend', this)
×
193
  }
194
})
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc