From 22a8ccfba687608e9a8538d26f9413d9a2091a94 Mon Sep 17 00:00:00 2001 From: Ken Sternberg Date: Fri, 17 May 2024 15:01:59 -0700 Subject: [PATCH] Provide documentation and tests for the LoadingOverlay. --- web/src/elements/LoadingOverlay.ts | 26 ++++++- .../elements/stories/LoadingOverlay.docs.mdx | 36 +++++++++ .../stories/LoadingOverlay.stories.ts | 73 +++++++++++++++++++ .../elements/stories/LoadingOverlay.test.ts | 41 +++++++++++ 4 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 web/src/elements/stories/LoadingOverlay.docs.mdx create mode 100644 web/src/elements/stories/LoadingOverlay.stories.ts create mode 100644 web/src/elements/stories/LoadingOverlay.test.ts diff --git a/web/src/elements/LoadingOverlay.ts b/web/src/elements/LoadingOverlay.ts index 8420156df6..4d2633d06f 100644 --- a/web/src/elements/LoadingOverlay.ts +++ b/web/src/elements/LoadingOverlay.ts @@ -6,8 +6,26 @@ import { customElement, property } from "lit/decorators.js"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; +export interface ILoadingOverlay { + topMost?: boolean; +} + +/** + * @class LoadingOverlay + * @element ak-loading-overlay + * + * The LoadingOverlay is meant to cover the container element completely, hiding the content behind + * a dimming filter, while content loads. + * + * @slot "body" - [Optional] message content to display while the overlay is visible. + */ @customElement("ak-loading-overlay") -export class LoadingOverlay extends AKElement { +export class LoadingOverlay extends AKElement implements ILoadingOverlay { + /** + * When true, forces the overlay onto the top layer of the display stack. + * + * @attr + */ @property({ type: Boolean }) topMost = false; @@ -38,3 +56,9 @@ export class LoadingOverlay extends AKElement { `; } } + +declare global { + interface HTMLElementTagNameMap { + "ak-loading-overlay": LoadingOverlay; + } +} diff --git a/web/src/elements/stories/LoadingOverlay.docs.mdx b/web/src/elements/stories/LoadingOverlay.docs.mdx new file mode 100644 index 0000000000..2ad0403ced --- /dev/null +++ b/web/src/elements/stories/LoadingOverlay.docs.mdx @@ -0,0 +1,36 @@ +import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks"; + +import * as LoadingOverlayStories from "./LoadingOverlay.stories"; + + + +# LoadingOverlay + +The LoadingOverlay is meant to cover the container element completely, hiding the content behind a +dimming filter, while content loads. + +It has a single named slot, "body" into which messages about the loading process can be included. + +## Usage + +```Typescript +import "@goauthentik/elements/LoadingOverlay.js"; +``` + +Note that the content of an alert _must_ be a valid HTML component; plain text does not work here. + +```html + + >This would display in the "body" slot +``` + +## Demo + +### Default + + + +### With a message + + diff --git a/web/src/elements/stories/LoadingOverlay.stories.ts b/web/src/elements/stories/LoadingOverlay.stories.ts new file mode 100644 index 0000000000..e955ad2200 --- /dev/null +++ b/web/src/elements/stories/LoadingOverlay.stories.ts @@ -0,0 +1,73 @@ +import type { Meta, StoryObj } from "@storybook/web-components"; + +import { LitElement, TemplateResult, css, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import { type ILoadingOverlay, LoadingOverlay } from "../LoadingOverlay.js"; +import "../LoadingOverlay.js"; + +const metadata: Meta = { + title: "Elements/", + component: "ak-loading-overlay", + parameters: { + docs: { + description: "Our empty state spinner", + }, + }, + argTypes: { + topMost: { control: "boolean" }, + message: { control: "text" }, + }, +}; + +export default metadata; + +@customElement("ak-storybook-demo-container") +export class Container extends LitElement { + static get styles() { + return css` + :host { + display: block; + position: relative; + height: 25vh; + width: 75vw; + } + #main-container { + position: relative; + width: 100%; + height: 100%; + } + `; + } + + @property({ type: Object, attribute: false }) + content!: TemplateResult; + + render() { + return html`
${this.content}
`; + } +} + +export const DefaultStory: StoryObj = { + args: { + topMost: undefined, + // @ts-ignore + message: undefined, + }, + + // @ts-ignore + render: ({ topMost, message }: ILoadingOverlay) => { + message = typeof message === "string" ? html`${message}` : message; + const content = html` ${message ?? ""} + `; + return html``; + }, +}; + +export const WithAMessage: StoryObj = { + ...DefaultStory, + args: { ...DefaultStory.args, message: html`

Overlay with a message

` }, +}; diff --git a/web/src/elements/stories/LoadingOverlay.test.ts b/web/src/elements/stories/LoadingOverlay.test.ts new file mode 100644 index 0000000000..54b442f593 --- /dev/null +++ b/web/src/elements/stories/LoadingOverlay.test.ts @@ -0,0 +1,41 @@ +import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet.js"; +import { $, expect } from "@wdio/globals"; + +import { msg } from "@lit/localize"; +import { TemplateResult, html, render as litRender } from "lit"; + +import AKGlobal from "@goauthentik/common/styles/authentik.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +import "../LoadingOverlay.js"; + +const render = (body: TemplateResult) => { + document.adoptedStyleSheets = [ + ...document.adoptedStyleSheets, + ensureCSSStyleSheet(PFBase), + ensureCSSStyleSheet(AKGlobal), + ]; + return litRender(body, document.body); +}; + +describe("ak-loading-overlay", () => { + it("should render the default loader", async () => { + render( + html``, + ); + + const empty = await $("ak-loading-overlay"); + await expect(empty).toExist(); + }); + + it("should render a slotted message", async () => { + render( + html` +

Try again with a different filter

+
`, + ); + + const message = await $("ak-loading-overlay").$(">>>p"); + await expect(message).toHaveText("Try again with a different filter"); + }); +});