Skip to content

Commit 2ab6cf8

Browse files
authored
Merge pull request #132 from umbraco/bugfix/17243-toast-autoclose
2 parents 871092f + 3a5aa52 commit 2ab6cf8

File tree

7 files changed

+122
-72
lines changed

7 files changed

+122
-72
lines changed

package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/uui-base/lib/utils/Timer.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ export class Timer {
1010

1111
public setDuration(duration: number) {
1212
this._duration = duration;
13-
this.restart();
13+
// TODO: Should calculate true offset of _remaining
14+
if (this._timerId !== null) {
15+
this.restart();
16+
}
1417
}
1518

1619
/** starts the timer */
@@ -22,10 +25,8 @@ export class Timer {
2225

2326
/** restarts the timer by setting remaining time to duration. */
2427
public restart() {
25-
if (this._timerId !== null) {
26-
this._remaining = this._duration;
27-
this.resume();
28-
}
28+
this._remaining = this._duration;
29+
this.resume();
2930
}
3031

3132
public pause() {

packages/uui-toast-notification-container/lib/uui-toast-notification-container.element.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,7 @@ export class UUIToastNotificationContainerElement extends LitElement {
7373
*/
7474
public resumeAutoClose = () => {
7575
// Only reset autoClose if we have it and if one of the children does not have focus.
76-
if (
77-
this._autoClose &&
78-
this.matches(':focus-within:not(:focus)') === false
79-
) {
76+
if (this.matches(':focus-within:not(:focus)') === false) {
8077
this._autoClosePause = false;
8178
this._toasts?.forEach(el => el.resumeAutoClose());
8279
}
@@ -136,8 +133,8 @@ export class UUIToastNotificationContainerElement extends LitElement {
136133
UUIToastNotificationEvent.CLOSED,
137134
this.onToastClosed as any
138135
);
139-
toast.removeEventListener('mouseover', this.pauseAutoClose);
140-
toast.removeEventListener('mouseout', this.resumeAutoClose);
136+
toast.removeEventListener('mouseenter', this.pauseAutoClose);
137+
toast.removeEventListener('mouseleave', this.resumeAutoClose);
141138
toast.removeEventListener('focus', this.pauseAutoClose);
142139
toast.removeEventListener('blur', this.resumeAutoClose);
143140
});
@@ -151,8 +148,8 @@ export class UUIToastNotificationContainerElement extends LitElement {
151148
this.onToastClosed as any
152149
);
153150

154-
toast.addEventListener('mouseover', this.pauseAutoClose);
155-
toast.addEventListener('mouseout', this.resumeAutoClose);
151+
toast.addEventListener('mouseenter', this.pauseAutoClose);
152+
toast.addEventListener('mouseleave', this.resumeAutoClose);
156153
toast.addEventListener('focus', this.pauseAutoClose);
157154
toast.addEventListener('blur', this.resumeAutoClose);
158155

packages/uui-toast-notification-container/lib/uui-toast-notification-container.test.ts

Lines changed: 71 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,16 @@ describe('UUIToastNotificationContainerElement', () => {
100100

101101
element.appendChild(toastElement);
102102

103-
await sleep(600); // Enough time to cover if it did happen that the element opened and auto-closed.
103+
// Wait for OPENED event
104+
const openedListener = oneEvent(
105+
toastElement,
106+
UUIToastNotificationEvent.OPENED
107+
);
108+
const openedEvent = await openedListener;
109+
expect(openedEvent).to.exist;
110+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
111+
112+
await sleep(element.autoClose + 1); // Enough time to cover if it did happen that the element opened and auto-closed.
104113

105114
// Check that its open.
106115
expect(toastElement.open).to.be.true;
@@ -113,28 +122,51 @@ describe('UUIToastNotificationContainerElement', () => {
113122
element.appendChild(toastElement);
114123

115124
await elementUpdated(element);
116-
await sleep(100);
125+
126+
// Wait for OPENING event
127+
const openingListener = oneEvent(
128+
toastElement,
129+
UUIToastNotificationEvent.OPENING
130+
);
131+
const openingEvent = await openingListener;
132+
expect(openingEvent).to.exist;
133+
expect(openingEvent.type).to.equal(UUIToastNotificationEvent.OPENING);
117134

118135
element.pauseAutoClose();
119136

120-
await sleep(500); // Enough time to cover if it did happen that the element opened and auto-closed.
137+
// Wait for OPENED event
138+
const openedListener = oneEvent(
139+
toastElement,
140+
UUIToastNotificationEvent.OPENED
141+
);
142+
const openedEvent = await openedListener;
143+
expect(openedEvent).to.exist;
144+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
121145

122146
// Check that its still open, pause actually did work.
123147
expect(toastElement.open).to.be.true;
124148
});
125149

126150
it('pausing autoClose when open', async () => {
127-
element.autoClose = 200;
151+
element.autoClose = 20;
128152
await elementUpdated(element);
129153

130154
element.appendChild(toastElement);
131155

132156
await elementUpdated(element);
133-
await sleep(500);
157+
158+
// Wait for OPENED event
159+
const openedListener = oneEvent(
160+
toastElement,
161+
UUIToastNotificationEvent.OPENED
162+
);
163+
const openedEvent = await openedListener;
164+
expect(openedEvent).to.exist;
165+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
134166

135167
element.pauseAutoClose();
136168

137-
await sleep(300); // Enough time to cover if it did happen that the element auto-closed.
169+
await sleep(element.autoClose + 1); // Enough time to cover if it did happen that the element auto-closed.
138170

139171
// Check that its still open, pause actually did work.
140172
expect(toastElement.open).to.be.true;
@@ -146,7 +178,14 @@ describe('UUIToastNotificationContainerElement', () => {
146178

147179
element.appendChild(toastElement);
148180

149-
await sleep(100); // Enough time to cover if it did happen that the element opened and auto-closed.
181+
// Wait for OPENED event
182+
const openedListener = oneEvent(
183+
toastElement,
184+
UUIToastNotificationEvent.OPENED
185+
);
186+
const openedEvent = await openedListener;
187+
expect(openedEvent).to.exist;
188+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
150189

151190
// Check that its still open, pause actually did work.
152191
expect(toastElement.open).to.be.true;
@@ -168,43 +207,57 @@ describe('UUIToastNotificationContainerElement', () => {
168207
element.appendChild(toastElement);
169208

170209
await elementUpdated(element);
171-
await sleep(100);
210+
211+
// Wait for OPENED event
212+
const openedListener = oneEvent(
213+
toastElement,
214+
UUIToastNotificationEvent.OPENED
215+
);
216+
const openedEvent = await openedListener;
217+
expect(openedEvent).to.exist;
218+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
172219

173220
toastElement.dispatchEvent(new Event('focus'));
174221

175-
await sleep(400); // Enough time to cover if it did happen that the element opened and auto-closed.
222+
await sleep(element.autoClose + 1); // Enough time to cover if it did happen that the element opened and auto-closed.
176223

177224
// Check that its still open, pause actually did work.
178-
expect((toastElement as any)._animate).to.be.false; // Checking private _animate to ensure that guessed animation time was good.
179225
expect(toastElement.open).to.be.true;
180226

181227
toastElement.dispatchEvent(new Event('blur'));
182228

183-
await sleep(40); // Enough time to cover if it did happen that the element opened and auto-closed.
229+
await sleep(element.autoClose + 1); // Enough time to cover if it did happen that the element opened and auto-closed.
184230

185231
// Check that its still open, pause actually did work.
186232
expect(toastElement.open).to.be.false;
187233
});
188234

189-
it('mouseover on child item will pause and resume autoClose', async () => {
235+
it('mouseenter on child item will pause and resume autoClose', async () => {
190236
element.autoClose = 20;
191237

192238
element.appendChild(toastElement);
193239

194240
await elementUpdated(element);
195-
await sleep(100);
196241

197-
toastElement.dispatchEvent(new Event('mouseover'));
242+
// Wait for OPENED event
243+
const openedListener = oneEvent(
244+
toastElement,
245+
UUIToastNotificationEvent.OPENED
246+
);
247+
const openedEvent = await openedListener;
248+
expect(openedEvent).to.exist;
249+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
250+
251+
toastElement.dispatchEvent(new Event('mouseenter'));
198252

199-
await sleep(400); // Enough time to cover if it did happen that the element opened and auto-closed.
253+
await sleep(element.autoClose + 1); // Enough time to cover if it did happen that the element opened and auto-closed.
200254

201255
// Check that its still open, pause actually did work.
202-
expect((toastElement as any)._animate).to.be.false; // Checking private _animate to ensure that guessed animation time was good.
203256
expect(toastElement.open).to.be.true;
204257

205-
toastElement.dispatchEvent(new Event('mouseout'));
258+
toastElement.dispatchEvent(new Event('mouseleave'));
206259

207-
await sleep(40); // Enough time to cover if it did happen that the element opened and auto-closed.
260+
await sleep(element.autoClose + 1); // Enough time to cover if it did happen that the element opened and auto-closed.
208261

209262
// Check that its still open, pause actually did work.
210263
expect(toastElement.open).to.be.false;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { UUIEvent } from '@umbraco-ui/uui-base/lib/events';
2+
23
import { UUIToastNotificationElement } from './uui-toast-notification.element';
34

45
export class UUIToastNotificationEvent extends UUIEvent<
56
{},
67
UUIToastNotificationElement
78
> {
89
public static readonly OPENING = 'opening';
10+
public static readonly OPENED = 'opened';
911
public static readonly CLOSING = 'closing';
1012
public static readonly CLOSED = 'closed';
1113
}

packages/uui-toast-notification/lib/uui-toast-notification.element.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { UUIToastNotificationEvent } from './UUIToastNotificationEvent';
1414
/**
1515
* @element uui-toast-notification
1616
* @fires {UUIToastNotificationEvent} opening - fires when the toast is starting to open
17+
* @fires {UUIToastNotificationEvent} opened - fires when the toast is open after the open-animation
1718
* @fires {UUIToastNotificationEvent} closing - fires when the toast is starting to close
1819
* @fires {UUIToastNotificationEvent} closed - fires when the toast is closed
1920
* @description - Component for displaying a toast notification, preferably used in toast-notification-container.
@@ -174,7 +175,7 @@ export class UUIToastNotificationElement extends LitElement {
174175
this.isOpen === true &&
175176
this._animate === false
176177
) {
177-
this._timer.resume();
178+
this._timer.restart();
178179
}
179180
}
180181

@@ -259,6 +260,10 @@ export class UUIToastNotificationElement extends LitElement {
259260
if (this._pauseTimer === false) {
260261
this._timer?.start();
261262
}
263+
264+
this.dispatchEvent(
265+
new UUIToastNotificationEvent(UUIToastNotificationEvent.OPENED)
266+
);
262267
}
263268
}, 480);
264269
});

packages/uui-toast-notification/lib/uui-toast-notification.test.ts

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -213,52 +213,27 @@ describe('UUIToastNotificationElement', () => {
213213
});
214214
});
215215

216-
describe('autoClose can be paused after open-animation', () => {
217-
it('did element close wait', async () => {
218-
element.autoClose = 200;
219-
element.open = true;
220-
221-
await sleep(600); // enough time for opening animation to be done.
222-
223-
expect(element.open).to.be.true;
224-
element.pauseAutoClose();
225-
226-
await sleep(500); // enough time for opening animation to be done.
227-
228-
element.resumeAutoClose();
229-
expect(element.open).to.be.true;
230-
231-
const closeListener = oneEvent(
232-
element,
233-
UUIToastNotificationEvent.CLOSING
234-
);
235-
const closeEvent = await closeListener;
236-
expect(closeEvent).to.exist;
237-
expect(closeEvent.type).to.equal(UUIToastNotificationEvent.CLOSING);
238-
expect(element.open).to.be.false;
239-
240-
const closedListener = oneEvent(
241-
element,
242-
UUIToastNotificationEvent.CLOSED
243-
);
244-
const closedEvent = await closedListener;
245-
expect(closedEvent).to.exist;
246-
expect(closedEvent.type).to.equal(UUIToastNotificationEvent.CLOSED);
247-
expect(element.open).to.be.false;
248-
});
249-
});
250-
251216
describe('autoClose can be paused while opening', () => {
252217
it('did element close wait', async () => {
253218
element.autoClose = 20;
254219
element.open = true;
255220

256221
element.pauseAutoClose();
257222

258-
await sleep(600); // enough time for opening animation to be done.
223+
// Wait for OPENED event
224+
const openedListener = oneEvent(
225+
element,
226+
UUIToastNotificationEvent.OPENED
227+
);
228+
const openedEvent = await openedListener;
229+
expect(openedEvent).to.exist;
230+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
259231

260232
element.resumeAutoClose();
261-
expect(element.open).to.be.true;
233+
expect(
234+
element.open,
235+
'Element should still be open immediately after resuming'
236+
).to.be.true;
262237

263238
const closeListener = oneEvent(
264239
element,
@@ -286,7 +261,15 @@ describe('UUIToastNotificationElement', () => {
286261
it('clicking on the close-button did close', async () => {
287262
element.open = true;
288263

289-
await sleep(100); // wait a bit.
264+
// Wait for OPENED event
265+
const openedListener = oneEvent(
266+
element,
267+
UUIToastNotificationEvent.OPENED
268+
);
269+
const openedEvent = await openedListener;
270+
expect(openedEvent).to.exist;
271+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
272+
290273
expect(element.open).to.be.true;
291274

292275
const closeButton = element.shadowRoot!.querySelector(
@@ -302,7 +285,15 @@ describe('UUIToastNotificationElement', () => {
302285
it('pressing esc when focus did close', async () => {
303286
element.open = true;
304287

305-
await sleep(100); // wait a bit.
288+
// Wait for OPENED event
289+
const openedListener = oneEvent(
290+
element,
291+
UUIToastNotificationEvent.OPENED
292+
);
293+
const openedEvent = await openedListener;
294+
expect(openedEvent).to.exist;
295+
expect(openedEvent.type).to.equal(UUIToastNotificationEvent.OPENED);
296+
306297
expect(element.open).to.be.true;
307298

308299
element.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }));

0 commit comments

Comments
 (0)