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

mozilla / blurts-server / #12635

pending completion
#12635

push

circleci

web-flow
Merge pull request #2878 from mozilla/MNTOR-1276/auto-hide-toast-after-interaction

refactor to auto-hide toast after interaction

282 of 1416 branches covered (19.92%)

Branch coverage included in aggregate %.

14 of 14 new or added lines in 1 file covered. (100.0%)

959 of 3910 relevant lines covered (24.53%)

2.04 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
 * document.body.append(toast)
23
 * ```
24
 *
25
 * SSR/HTML examples:
26
 * ```
27
 * <toast-alert>Alert message here</toast-alert>
28
 * <toast-alert ttl='10'>Another alert message here</toast-alert>
29
 * ```
30
 */
31

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

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

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

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

69
  button{
70
    position: absolute;
71
    top: 0;
72
    right: 0;
73
    height: 100%;
74
    padding: 0 var(--padding-md);
75
    border: none;
76
    cursor: pointer;
77
    font: inherit;
78
    color: inherit;
79
    background-color: transparent;
80
  }
81

82
  button:hover{
83
    box-shadow: inset 0 0 64px #fc95;
84
  }
85

86
  @keyframes fly-in{
87
    from{
88
      opacity: 0;
89
      transform: translateY(-30%);
90
    }
91
    to{
92
      opacity: 1;
93
    }
94
  }
95

96
  @keyframes fade-out{
97
    from{
98
      opacity: 1;
99
    }
100
    to{
101
      opacity: 0;
102
    }
103
  }
104
</style>
105

106
<output>
107
  <slot></slot>
108
  <button aria-label="Close">✕</button>
109
</output>
110
`
111

112
customElements.define('toast-alert', class extends HTMLElement {
×
113
  constructor () {
114
    super()
×
115
    this.attachShadow({ mode: 'open' })
×
116
    this.shadowRoot.innerHTML = html
×
117
  }
118

119
  get ttl () {
120
    return this.getAttribute('ttl')
×
121
  }
122

123
  set ttl (value) {
124
    this.setAttribute('ttl', value)
×
125
    this.style.setProperty('--ttl', `${value}s`) // seconds before fade-out starts
×
126
  }
127

128
  connectedCallback () {
129
    const toasts = Array.from(document.querySelectorAll('toast-alert')).reverse()
×
130

131
    for (let i = 1, y = 0; i < toasts.length; i++) {
×
132
      // start at index 1 to push old toasts down with aggregated toast heights plus 10px gap
133
      y += toasts[i - 1].getBoundingClientRect().height + 10
×
134
      toasts[i].style.setProperty('--toast-y', `${y}px`)
×
135
    }
136

137
    if (this.hasAttribute('ttl')) {
×
138
      this.ttl = this.getAttribute('ttl')
×
139
    }
140
    this.shadowRoot.addEventListener('click', this)
×
141
    this.addEventListener('animationend', this)
×
142
  }
143

144
  handleEvent (e) {
145
    switch (true) {
×
146
      case e.target.matches('button'):
147
        this.remove()
×
148
        break
×
149
      case e.animationName === 'fade-out':
150
        this.remove()
×
151
        break
×
152
    }
153
  }
154

155
  disconnectedCallback () {
156
    this.shadowRoot.removeEventListener('click', this)
×
157
    this.removeEventListener('animationend', this)
×
158
  }
159
})
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