Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(alert): auto-dismissible retains close button and dismisses timer while a user is hovering over #5872

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0d0d9c2
fix(alert): auto-dismissible retains close button and dismisses timer…
Elijbet Nov 30, 2022
de497fc
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Nov 30, 2022
8894ba5
WIP: hover pauses animation, handle timer for mouseOver and mouseLeave
Elijbet Dec 1, 2022
aaa0835
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Dec 1, 2022
3d1bce6
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Dec 5, 2022
e941c0f
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Dec 6, 2022
b40d27d
mouseOver and mouseLeave do not need to run if autoDuration is set false
Elijbet Dec 6, 2022
9b539c0
fix timer and cleanup
Elijbet Dec 6, 2022
cfd4c15
adjust existing related test for correct output: expect close icon to…
Elijbet Dec 6, 2022
f37bfe0
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Dec 6, 2022
ea66b78
WIP e2e tests
Elijbet Dec 6, 2022
8060b8b
master merge
Elijbet Dec 6, 2022
ead889d
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Dec 7, 2022
f0e8c5e
add tests for auto-dismiss behavior: should render close button and p…
Elijbet Dec 7, 2022
3a870fc
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Dec 7, 2022
bdc9b6a
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Dec 7, 2022
c0f59d1
added test for auto-dismiss behavior on queued items: should display …
Elijbet Dec 8, 2022
2a20d94
add types and cleanup
Elijbet Dec 8, 2022
8c8c8f0
Merge branch 'master' into elijbet/3338-alert-usability-enhancements-…
Elijbet Dec 8, 2022
4ac06ae
cleanup
Elijbet Dec 8, 2022
ed086c5
merge master
Elijbet Dec 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 92 additions & 17 deletions src/components/alert/alert.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { newE2EPage } from "@stencil/core/testing";
import { E2EElement, E2EPage, newE2EPage } from "@stencil/core/testing";
import { renders, accessible, HYDRATED_ATTR, hidden } from "../../tests/commonTests";
import { html } from "../../../support/formatting";
import { CSS } from "./resources";
import { CSS, DURATIONS } from "./resources";
import { getElementXY } from "../../tests/utils";

describe("calcite-alert", () => {
const alertContent = `
Expand Down Expand Up @@ -50,12 +51,10 @@ describe("calcite-alert", () => {
</calcite-alert>`);

const element = await page.find("calcite-alert");
const close = await page.find("calcite-alert >>> .alert-close");
const icon = await page.find("calcite-alert >>> .alert-icon");

expect(element).toEqualAttribute("color", "yellow");
expect(element).toEqualAttribute("auto-dismiss-duration", "fast");
expect(close).toBeNull();
expect(icon).toBeNull();
});

Expand Down Expand Up @@ -320,30 +319,106 @@ describe("calcite-alert", () => {
expect(await container.isVisible()).toBe(false);
});

describe("when multiple alerts are queued", () => {
describe("auto-dismiss behavior on queued items", () => {
it("should display number of queued alerts with a calcite-chip", async () => {
const page = await newE2EPage({
html: `
<calcite-alert open id="first-open" icon="3d-glasses" auto-dismiss-duration="fast" scale="l">
<div slot="title">Title of alert #1</div>
<div slot="message">Message text of the alert</div>
const page = await newE2EPage();
await page.setContent(html`
<calcite-button id="buttonOne" onclick="document.querySelector('#first-open').setAttribute('open', '')"
>open alert</calcite-button
>
<calcite-button id="buttonTwo" onclick="document.querySelector('#alert-to-be-queued').setAttribute('open', '')"
>open alert</calcite-button
>

<calcite-alert open id="first-open" icon="3d-glasses" auto-dismiss scale="l">
<div slot="title">Title of alert Uno</div>
<div slot="message">Message text of the alert Uno</div>
<a slot="link" href="#">Retry</a>
</calcite-alert>

<calcite-alert id="alert-to-be-queued" icon auto-dismiss scale="l">
<div slot="title">Title of alert #2</div>
<div slot="message">Message text of the alert</div>
<div slot="title">Title of alert Dos</div>
<div slot="message">Message text of the alert Dos</div>
<a slot="link" href="#">Retry</a>
</calcite-alert>
`
});
await page.addScriptTag({
content: `document.querySelector("#alert-to-be-queued").setAttribute("open", "");`
});
`);
const buttonOne = await page.find("#buttonOne");
const buttonTwo = await page.find("#buttonTwo");
const alertOne = await page.find("#first-open");
const alertTwo = await page.find("#alert-to-be-queued");

await buttonOne.click();
await page.waitForTimeout(animationDurationInMs);
expect(await alertOne.isVisible()).toBe(true);

await buttonTwo.click();
expect(await alertTwo.isVisible()).toBe(true);

const chip = await page.find("calcite-alert[id='first-open'] >>> calcite-chip");
const chipQueueCount = "+1";
expect(await chip.getProperty("value")).toEqual(chipQueueCount);
expect(chip.textContent).toEqual(chipQueueCount);

await page.waitForTimeout(DURATIONS.medium * 2 + animationDurationInMs * 5);
await page.waitForSelector("#first-open", { visible: false });
await page.waitForSelector("#alert-to-be-queued", { visible: false });
});
});

describe("auto-dismiss behavior", () => {
let page: E2EPage;
let alert: E2EElement;
let button: E2EElement;
let buttonClose: E2EElement;
let playState: string;

beforeEach(async () => {
page = await newE2EPage();
await page.setContent(html`
<div>
<calcite-button id="button" onclick="document.querySelector('#alert').setAttribute('open', '')"
>open alert</calcite-button
>
<calcite-alert label="this is a success" id="alert" auto-dismiss icon color="green">
${alertContent}</calcite-alert
>
</div>
`);
alert = await page.find("#alert");
button = await page.find("#button");
buttonClose = await page.find(`#alert >>> .${CSS.close}`);

playState = await page.evaluate(async () => {
const alert = document.querySelector("calcite-alert");
return window.getComputedStyle(alert).animationPlayState;
});
});

it("should render close button", async () => {
await button.click();
await page.waitForTimeout(animationDurationInMs);

expect(await alert.isVisible()).toBe(true);
expect(buttonClose).toBeTruthy();
});

it("pauses on mouseOver and resumes on mouseLeave", async () => {
await button.click();

expect(await alert.isVisible()).toBe(true);
expect(await alert.getProperty("autoDismissDuration")).toEqual("medium");
expect(playState).toEqual("running");

const [alertLocationX, alertLocationY] = await getElementXY(page, "calcite-alert", `.${CSS.close}`);
await page.mouse.move(alertLocationX, alertLocationY);

await page.waitForTimeout(DURATIONS.medium);
expect(await alert.isVisible()).toBe(true);

await page.mouse.move(0, 0);

await page.waitForTimeout(DURATIONS.medium + animationDurationInMs);
await page.waitForSelector("#alert", { visible: false });
});
});
});
3 changes: 3 additions & 0 deletions src/components/alert/alert.scss
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ $alertDurations: "fast" 6000ms, "medium" 10000ms, "slow" 14000ms;
:host([auto-dismiss-duration="#{$name}"]) .alert-dismiss-progress:after {
animation: dismissProgress $duration ease-out;
}
:host(:hover[auto-dismiss-duration="#{$name}"]) .alert-dismiss-progress:after {
animation-play-state: paused;
}
}

@keyframes dismissProgress {
Expand Down
8 changes: 8 additions & 0 deletions src/components/alert/alert.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,11 @@ export const actionsEndQueued_TestOnly = (): string => html`
}, "1000");
</script>
`;

export const autoDismissableRetainsCloseButton_TestOnly = (): string => html`
<calcite-alert auto-dismiss auto-dismiss-duration="medium" open scale="m" color="blue">
<div slot="title">Here's a general bit of information</div>
<div slot="message">Some kind of contextually relevant content</div>
<calcite-link slot="link" title="my action" role="presentation"> Take action </calcite-link>
</calcite-alert>
`;
26 changes: 24 additions & 2 deletions src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export class Alert implements OpenCloseComponent, LocalizedComponent, LoadableCo

disconnectedCallback(): void {
window.clearTimeout(this.autoDismissTimeoutId);
window.clearTimeout(this.queueTimeout);
disconnectOpenCloseComponent(this);
disconnectLocalized(this);
}
Expand Down Expand Up @@ -226,6 +227,12 @@ export class Alert implements OpenCloseComponent, LocalizedComponent, LoadableCo
queued,
[placement]: true
}}
onPointerOut={
this.autoDismiss && this.autoDismissTimeoutId ? this.handleMouseLeave : null
}
onPointerOver={
this.autoDismiss && this.autoDismissTimeoutId ? this.handleMouseOver : null
}
ref={this.setTransitionEl}
>
{requestedIcon ? (
Expand All @@ -242,7 +249,7 @@ export class Alert implements OpenCloseComponent, LocalizedComponent, LoadableCo
{slotNode}
</div>
{this.queueLength > 1 ? queueCount : null}
{!autoDismiss ? closeButton : null}
{closeButton}
{open && !queued && autoDismiss ? <div class="alert-dismiss-progress" /> : null}
</div>
</Host>
Expand Down Expand Up @@ -351,7 +358,9 @@ export class Alert implements OpenCloseComponent, LocalizedComponent, LoadableCo

private queueTimeout: number;

private trackTimer = Date.now();
private trackTimer: number;

private remainingPausedTimeout = 0;

/** the computed icon to render */
/* @internal */
Expand Down Expand Up @@ -423,4 +432,17 @@ export class Alert implements OpenCloseComponent, LocalizedComponent, LoadableCo
private actionsEndSlotChangeHandler = (event: Event): void => {
this.hasEndActions = slotChangeHasAssignedElement(event);
};

private handleMouseOver = (): void => {
window.clearTimeout(this.autoDismissTimeoutId);
Elijbet marked this conversation as resolved.
Show resolved Hide resolved
this.remainingPausedTimeout =
DURATIONS[this.autoDismissDuration] - Date.now() - this.trackTimer;
};

private handleMouseLeave = (): void => {
this.autoDismissTimeoutId = window.setTimeout(
() => this.closeAlert(),
this.remainingPausedTimeout
);
};
}
3 changes: 2 additions & 1 deletion src/components/alert/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export const SLOTS = {

export const CSS = {
actionsEnd: "actions-end",
container: "container"
container: "container",
close: "alert-close"
};