web: provide storybook demos and docs for existing tests (#11651)
* Added tests and refinements as tests indicate. * Building out the test suite. * web: test the simple things. Fix what the tests revealed. - Move `EmptyState.test.ts` into the `./tests` folder. - Provide unit tests for: - Alert - Divider - Expand - Label - LoadingOverlay - Give all tested items an Interface and a functional variant for rendering - Give Label an alternative syntax for declaring alert levels - Remove the slot name in LoadingOverlay - Change the slot call in `./enterprise/rac/index.ts` to not need the slot name as well - Change the attribute names `topMost`, `textOpen`, and `textClosed` to `topmost`, `text-open`, and `text-closed`, respectively. - Change locations in the code where those are used to correspond ** Why interfaces: ** Provides another check on the input/output boundaries of our elements, gives Storybook and WebdriverIO another validation to check, and guarantees any rendering functions cannot be passed invalid property names. ** Why functions for rendering: ** Providing functions for rendering gets us one step closer to dynamically defining our forms-in-code at runtime without losing any type safety. ** Why rename the attributes: ** A *very* subtle bug: [Element:setAttribute()](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute) automatically "converts an attribute name to all lower-case when called on an HTML element in an HTML document." The three attributes renamed are all treated *as* attributes, either classic boolean or stringly-typed attributes, and attempting to manipulate them with `setAttribute()` will fail. All of these attributes are presentational; none of them end up in a transaction with the back-end, so kebab-to-camel conversions are not a concern. Also, ["topmost" is one word](https://www.merriam-webster.com/dictionary/topmost). ** Why remove the slot name: ** Because there was only one slot. A name is not needed. * Fix minor spelling error. * First pass at a custom, styled input object. * . * web: Demo the simple things. Fix things the Demo says need fixing. - Move the Element's stories into a `./stories` folder - Provide stories for (these are the same ones "provided tests for" in the [previous PR](https://github.com/goauthentik/authentik/pull/11633)) - Alert - Divider - Expand - Label - LoadingOverlay - Provide Storybook documentation for: - AppIcon - ActionButton - AggregateCard - AggregatePromiseCard - QuickActionsCard - Alert - Divider - EmptyState - Expand - Label - LoadingOverlay - ApplicationEmptyState - Fix a bug in LoadingOverlay; naming error in nested slots caused any message attached to the overlay to not sow up correctly. - Revise AppIcon to be independent of authentik; it just cares if the data has a name or an icon reference, it does not need to know about `Application` objects. As such, it's an *element*, not a *component*, and I've moved it into the right location, and updated the few places it is used to match. * Prettier has opinions with which I sometimes diverge. * Found a bug! Although pf-m-xl was defined as a legal size, there was no code to handle drawing something XL! * Found a few typos and incorrect API descriptions.
This commit is contained in:
		@ -1,8 +1,7 @@
 | 
				
			|||||||
import "@goauthentik/admin/applications/ApplicationForm";
 | 
					import "@goauthentik/admin/applications/ApplicationForm";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { PFSize } from "@goauthentik/common/enums.js";
 | 
					 | 
				
			||||||
import "@goauthentik/components/ak-app-icon";
 | 
					 | 
				
			||||||
import MDApplication from "@goauthentik/docs/add-secure-apps/applications/index.md";
 | 
					import MDApplication from "@goauthentik/docs/add-secure-apps/applications/index.md";
 | 
				
			||||||
 | 
					import "@goauthentik/elements/AppIcon.js";
 | 
				
			||||||
import "@goauthentik/elements/Markdown";
 | 
					import "@goauthentik/elements/Markdown";
 | 
				
			||||||
import "@goauthentik/elements/buttons/SpinnerButton";
 | 
					import "@goauthentik/elements/buttons/SpinnerButton";
 | 
				
			||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
					import "@goauthentik/elements/forms/DeleteBulkForm";
 | 
				
			||||||
@ -16,6 +15,7 @@ import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
				
			|||||||
import { msg } from "@lit/localize";
 | 
					import { msg } from "@lit/localize";
 | 
				
			||||||
import { CSSResult, TemplateResult, css, html } from "lit";
 | 
					import { CSSResult, TemplateResult, css, html } from "lit";
 | 
				
			||||||
import { customElement, property } from "lit/decorators.js";
 | 
					import { customElement, property } from "lit/decorators.js";
 | 
				
			||||||
 | 
					import { ifDefined } from "lit/directives/if-defined.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | 
					import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -122,7 +122,10 @@ export class ApplicationListPage extends TablePage<Application> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    row(item: Application): TemplateResult[] {
 | 
					    row(item: Application): TemplateResult[] {
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            html`<ak-app-icon size=${PFSize.Medium} .app=${item}></ak-app-icon>`,
 | 
					            html`<ak-app-icon
 | 
				
			||||||
 | 
					                name=${item.name}
 | 
				
			||||||
 | 
					                icon=${ifDefined(item.metaIcon || undefined)}
 | 
				
			||||||
 | 
					            ></ak-app-icon>`,
 | 
				
			||||||
            html`<a href="#/core/applications/${item.slug}">
 | 
					            html`<a href="#/core/applications/${item.slug}">
 | 
				
			||||||
                <div>${item.name}</div>
 | 
					                <div>${item.name}</div>
 | 
				
			||||||
                ${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
 | 
					                ${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
 | 
				
			||||||
 | 
				
			|||||||
@ -5,8 +5,8 @@ import "@goauthentik/admin/policies/BoundPoliciesList";
 | 
				
			|||||||
import "@goauthentik/admin/rbac/ObjectPermissionsPage";
 | 
					import "@goauthentik/admin/rbac/ObjectPermissionsPage";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { PFSize } from "@goauthentik/common/enums.js";
 | 
					import { PFSize } from "@goauthentik/common/enums.js";
 | 
				
			||||||
import "@goauthentik/components/ak-app-icon";
 | 
					 | 
				
			||||||
import "@goauthentik/components/events/ObjectChangelog";
 | 
					import "@goauthentik/components/events/ObjectChangelog";
 | 
				
			||||||
 | 
					import "@goauthentik/elements/AppIcon";
 | 
				
			||||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
					import { AKElement } from "@goauthentik/elements/Base";
 | 
				
			||||||
import "@goauthentik/elements/EmptyState";
 | 
					import "@goauthentik/elements/EmptyState";
 | 
				
			||||||
import "@goauthentik/elements/PageHeader";
 | 
					import "@goauthentik/elements/PageHeader";
 | 
				
			||||||
@ -102,8 +102,9 @@ export class ApplicationViewPage extends AKElement {
 | 
				
			|||||||
            >
 | 
					            >
 | 
				
			||||||
                <ak-app-icon
 | 
					                <ak-app-icon
 | 
				
			||||||
                    size=${PFSize.Medium}
 | 
					                    size=${PFSize.Medium}
 | 
				
			||||||
 | 
					                    name=${ifDefined(this.application?.name || undefined)}
 | 
				
			||||||
 | 
					                    icon=${ifDefined(this.application?.metaIcon || undefined)}
 | 
				
			||||||
                    slot="icon"
 | 
					                    slot="icon"
 | 
				
			||||||
                    .app=${this.application}
 | 
					 | 
				
			||||||
                ></ak-app-icon>
 | 
					                ></ak-app-icon>
 | 
				
			||||||
            </ak-page-header>
 | 
					            </ak-page-header>
 | 
				
			||||||
            ${this.renderApp()}`;
 | 
					            ${this.renderApp()}`;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
import { applicationListStyle } from "@goauthentik/admin/applications/ApplicationListPage";
 | 
					import { applicationListStyle } from "@goauthentik/admin/applications/ApplicationListPage";
 | 
				
			||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
					import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
 | 
				
			||||||
import { PFSize } from "@goauthentik/common/enums.js";
 | 
					import "@goauthentik/elements/AppIcon";
 | 
				
			||||||
import "@goauthentik/components/ak-app-icon";
 | 
					 | 
				
			||||||
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
 | 
					import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
 | 
				
			||||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
					import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { msg } from "@lit/localize";
 | 
					import { msg } from "@lit/localize";
 | 
				
			||||||
import { CSSResult, TemplateResult, html } from "lit";
 | 
					import { CSSResult, TemplateResult, html } from "lit";
 | 
				
			||||||
import { customElement, property } from "lit/decorators.js";
 | 
					import { customElement, property } from "lit/decorators.js";
 | 
				
			||||||
 | 
					import { ifDefined } from "lit/directives/if-defined.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Application, CoreApi, User } from "@goauthentik/api";
 | 
					import { Application, CoreApi, User } from "@goauthentik/api";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -40,7 +40,10 @@ export class UserApplicationTable extends Table<Application> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    row(item: Application): TemplateResult[] {
 | 
					    row(item: Application): TemplateResult[] {
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
            html`<ak-app-icon size=${PFSize.Medium} .app=${item}></ak-app-icon>`,
 | 
					            html`<ak-app-icon
 | 
				
			||||||
 | 
					                name=${item.name}
 | 
				
			||||||
 | 
					                icon=${ifDefined(item.metaIcon || undefined)}
 | 
				
			||||||
 | 
					            ></ak-app-icon>`,
 | 
				
			||||||
            html`<a href="#/core/applications/${item.slug}">
 | 
					            html`<a href="#/core/applications/${item.slug}">
 | 
				
			||||||
                <div>${item.name}</div>
 | 
					                <div>${item.name}</div>
 | 
				
			||||||
                ${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
 | 
					                ${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,38 +0,0 @@
 | 
				
			|||||||
import "@goauthentik/elements/messages/MessageContainer";
 | 
					 | 
				
			||||||
import { Meta } from "@storybook/web-components";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import { TemplateResult, html } from "lit";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import "../ak-app-icon";
 | 
					 | 
				
			||||||
import AkAppIcon from "../ak-app-icon";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const metadata: Meta<AkAppIcon> = {
 | 
					 | 
				
			||||||
    title: "Components / App Icon",
 | 
					 | 
				
			||||||
    component: "ak-app-icon",
 | 
					 | 
				
			||||||
    parameters: {
 | 
					 | 
				
			||||||
        docs: {
 | 
					 | 
				
			||||||
            description: {
 | 
					 | 
				
			||||||
                component: "A small card displaying an application icon",
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default metadata;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const container = (testItem: TemplateResult) =>
 | 
					 | 
				
			||||||
    html` <div style="background: #000; padding: 2em">
 | 
					 | 
				
			||||||
        <style>
 | 
					 | 
				
			||||||
            li {
 | 
					 | 
				
			||||||
                display: block;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            p {
 | 
					 | 
				
			||||||
                margin-top: 1em;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        </style>
 | 
					 | 
				
			||||||
        ${testItem}
 | 
					 | 
				
			||||||
    </div>`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const AppIcon = () => {
 | 
					 | 
				
			||||||
    return container(html`<ak-app-icon .app=${{ name: "Demo app" }} size="pf-m-md"></ak-app-icon>`);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,23 +1,30 @@
 | 
				
			|||||||
import { PFSize } from "@goauthentik/common/enums.js";
 | 
					import { PFSize } from "@goauthentik/common/enums.js";
 | 
				
			||||||
import { AKElement } from "@goauthentik/elements/Base";
 | 
					import { AKElement } from "@goauthentik/elements/Base";
 | 
				
			||||||
 | 
					import { P, match } from "ts-pattern";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { msg } from "@lit/localize";
 | 
					import { msg } from "@lit/localize";
 | 
				
			||||||
import { CSSResult, TemplateResult, css, html } from "lit";
 | 
					import { CSSResult, TemplateResult, css, html } from "lit";
 | 
				
			||||||
import { customElement, property } from "lit/decorators.js";
 | 
					import { customElement, property } from "lit/decorators.js";
 | 
				
			||||||
import { ifDefined } from "lit/directives/if-defined.js";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";
 | 
					import PFFAIcons from "@patternfly/patternfly/base/patternfly-fa-icons.css";
 | 
				
			||||||
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
 | 
					import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Application } from "@goauthentik/api";
 | 
					export interface IAppIcon {
 | 
				
			||||||
 | 
					    name?: string;
 | 
				
			||||||
 | 
					    icon?: string;
 | 
				
			||||||
 | 
					    size?: PFSize;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@customElement("ak-app-icon")
 | 
					@customElement("ak-app-icon")
 | 
				
			||||||
export class AppIcon extends AKElement {
 | 
					export class AppIcon extends AKElement implements IAppIcon {
 | 
				
			||||||
    @property({ type: Object, attribute: false })
 | 
					    @property({ type: String })
 | 
				
			||||||
    app?: Application;
 | 
					    name?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property({ type: String })
 | 
				
			||||||
 | 
					    icon?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property()
 | 
					    @property()
 | 
				
			||||||
    size?: PFSize;
 | 
					    size: PFSize = PFSize.Medium;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static get styles(): CSSResult[] {
 | 
					    static get styles(): CSSResult[] {
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
@ -39,6 +46,10 @@ export class AppIcon extends AKElement {
 | 
				
			|||||||
                    --icon-height: 1rem;
 | 
					                    --icon-height: 1rem;
 | 
				
			||||||
                    --icon-border: 0.125rem;
 | 
					                    --icon-border: 0.125rem;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                :host([size="pf-m-xl"]) {
 | 
				
			||||||
 | 
					                    --icon-height: 6rem;
 | 
				
			||||||
 | 
					                    --icon-border: 0.25rem;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                .pf-c-avatar {
 | 
					                .pf-c-avatar {
 | 
				
			||||||
                    --pf-c-avatar--BorderRadius: 0;
 | 
					                    --pf-c-avatar--BorderRadius: 0;
 | 
				
			||||||
                    --pf-c-avatar--Height: calc(
 | 
					                    --pf-c-avatar--Height: calc(
 | 
				
			||||||
@ -64,21 +75,17 @@ export class AppIcon extends AKElement {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render(): TemplateResult {
 | 
					    render(): TemplateResult {
 | 
				
			||||||
        if (!this.app) {
 | 
					        // prettier-ignore
 | 
				
			||||||
            return html`<div><i class="icon fas fa-question-circle"></i></div>`;
 | 
					        return match([this.name, this.icon])
 | 
				
			||||||
        }
 | 
					            .with([undefined, undefined],
 | 
				
			||||||
        if (this.app?.metaIcon) {
 | 
					                () => html`<div><i class="icon fas fa-question-circle"></i></div>`)
 | 
				
			||||||
            if (this.app.metaIcon.startsWith("fa://")) {
 | 
					            .with([P._, P.string.startsWith("fa://")],
 | 
				
			||||||
                const icon = this.app.metaIcon.replaceAll("fa://", "");
 | 
					                ([_name, icon]) => html`<div><i class="icon fas ${icon.replaceAll("fa://", "")}"></i></div>`)
 | 
				
			||||||
                return html`<div><i class="icon fas ${icon}"></i></div>`;
 | 
					            .with([P._, P.string],
 | 
				
			||||||
            }
 | 
					                ([_name, icon]) => html`<img class="icon pf-c-avatar" src="${icon}" alt="${msg("Application Icon")}" />`)
 | 
				
			||||||
            return html`<img
 | 
					            .with([P.string, undefined],
 | 
				
			||||||
                class="icon pf-c-avatar"
 | 
					                ([name]) => html`<span class="icon">${name.charAt(0).toUpperCase()}</span>`)
 | 
				
			||||||
                src="${ifDefined(this.app.metaIcon)}"
 | 
					            .exhaustive();
 | 
				
			||||||
                alt="${msg("Application Icon")}"
 | 
					 | 
				
			||||||
            />`;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return html`<span class="icon">${this.app?.name.charAt(0).toUpperCase()}</span>`;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -8,7 +8,7 @@ import { customElement, property } from "lit/decorators.js";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
					import PFBase from "@patternfly/patternfly/patternfly-base.css";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ILoadingOverlay {
 | 
					export interface ILoadingOverlay {
 | 
				
			||||||
    topmost?: boolean;
 | 
					    topmost?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -41,7 +41,7 @@ export class LoadingOverlay extends AKElement implements ILoadingOverlay {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return html`<ak-empty-state loading header="">
 | 
					        return html`<ak-empty-state loading header="">
 | 
				
			||||||
            <slot></slot>
 | 
					            <span slot="body"><slot></slot></span>
 | 
				
			||||||
        </ak-empty-state>`;
 | 
					        </ak-empty-state>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as ActionButtonStories from "./ak-action-button.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={ActionButtonStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Action Button
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					An `<ak-action-button>` takes a zero-arity function (a function that takes no argument) that returns
 | 
				
			||||||
 | 
					a promise. Pressing the button runs the function and the results of the promise drive the behavior
 | 
				
			||||||
 | 
					of the button.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/buttons/ActionButton/ak-action-button.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-action-button .apiRequest=${somePromise}">Your message here</ak-action-button>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Success: button with "promise revolved" animation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={ActionButtonStories.ButtonWithSuccess} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Failure: button with "promise rejected" animation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This shows how the button behaves if the promise rejects.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={ActionButtonStories.ButtonWithError} />
 | 
				
			||||||
@ -7,7 +7,7 @@ import "./ak-action-button";
 | 
				
			|||||||
import AKActionButton from "./ak-action-button";
 | 
					import AKActionButton from "./ak-action-button";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const metadata: Meta<AKActionButton> = {
 | 
					const metadata: Meta<AKActionButton> = {
 | 
				
			||||||
    title: "Elements / Action Button",
 | 
					    title: "Elements / <ak-action-button>",
 | 
				
			||||||
    component: "ak-action-button",
 | 
					    component: "ak-action-button",
 | 
				
			||||||
    parameters: {
 | 
					    parameters: {
 | 
				
			||||||
        docs: {
 | 
					        docs: {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										24
									
								
								web/src/elements/cards/stories/AggregateCard.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								web/src/elements/cards/stories/AggregateCard.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as AggregateCardStories from "./AggregateCard.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={AggregateCardStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Aggregate Cards
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Aggregate Cards are in-page elements to display isolated elements in a consistent, card-like format.
 | 
				
			||||||
 | 
					Cards are used in dashboards and as asides for specific information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/cards/AggregateCard.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-aggregate-card header="Some title"><p>This is the content of your card!</p></ak-aggregate-card>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AggregateCardStories.DefaultStory} />
 | 
				
			||||||
							
								
								
									
										35
									
								
								web/src/elements/cards/stories/AggregatePromiseCard.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								web/src/elements/cards/stories/AggregatePromiseCard.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as AggregatePromiseCardStories from "./AggregatePromiseCard.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={AggregatePromiseCardStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Aggregate Promise Cards
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Aggregate Promise Cards are Aggregate Cards that take a promise from client code and either display
 | 
				
			||||||
 | 
					the contents of that promise or a pre-configured failure notice. The contents must be compliant with
 | 
				
			||||||
 | 
					and produce a meaningful result via the `.toString()` API. HTML in the string will currently be
 | 
				
			||||||
 | 
					escaped.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/cards/AggregatePromiseCard.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-aggregate-card-promise
 | 
				
			||||||
 | 
					    header="Some title"
 | 
				
			||||||
 | 
					    .promise="${somePromise}"
 | 
				
			||||||
 | 
					></ak-aggregate-card-promise>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Success
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AggregatePromiseCardStories.DefaultStory} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Failure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AggregatePromiseCardStories.PromiseRejected} />
 | 
				
			||||||
							
								
								
									
										36
									
								
								web/src/elements/cards/stories/QuickActionsCard.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/src/elements/cards/stories/QuickActionsCard.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as QuickActionsCardStories from "./QuickActionsCard.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={QuickActionsCardStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Quick Action Cards
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A Quick Action Card displays a list of navigation links. It is used on our dashboards to provide
 | 
				
			||||||
 | 
					easy access to basic operations implied by the dashboard. The example here is from the home page
 | 
				
			||||||
 | 
					dashboard.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The QuickAction type has three fields: the string to display, the URL to navigate to, and a flag
 | 
				
			||||||
 | 
					indicating if the browser should open the link in a new tab.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/cards/QuickActionsCard.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ACTIONS: QuickAction[] = [
 | 
				
			||||||
 | 
					    ["Create a new application", "/core/applications"],
 | 
				
			||||||
 | 
					    ["Check the logs", "/events/log"],
 | 
				
			||||||
 | 
					    ["Explore integrations", "https://goauthentik.io/integrations/", true],
 | 
				
			||||||
 | 
					    ["Manage users", "/identity/users"],
 | 
				
			||||||
 | 
					    ["Check the release notes", "https://goauthentik.io/docs/releases/", true],
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-quick-actions-card title="Some title" .actions=${ACTIONS}></ak-aggregate-card>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={QuickActionsCardStories.DefaultStory} />
 | 
				
			||||||
							
								
								
									
										46
									
								
								web/src/elements/stories/Alert.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								web/src/elements/stories/Alert.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as AlertStories from "./Alert.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={AlertStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Alerts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Alerts are in-page elements intended to draw the user's attention and alert them to important
 | 
				
			||||||
 | 
					details. Alerts are used alongside form elements to warn users of potential mistakes they can
 | 
				
			||||||
 | 
					make, as well as in in-line documentation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/Alert.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that the content of an alert _must_ be a valid HTML component; plain text does not work here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-alert><p>This is the content of your alert!</p></ak-alert>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The default state of an alert is _warning_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AlertStories.DefaultStory} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The icon can be changed via the `icon` attribute. It takes the name of a valid `fas`-class Font
 | 
				
			||||||
 | 
					Awesome icon. Changing the icon can be helpful, as not everyone can see the color changes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AlertStories.InfoAlert} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Success
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AlertStories.SuccessAlert} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Danger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AlertStories.DangerAlert} />
 | 
				
			||||||
							
								
								
									
										69
									
								
								web/src/elements/stories/Alert.stories.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								web/src/elements/stories/Alert.stories.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					import type { Meta, StoryObj } from "@storybook/web-components";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { html } from "lit";
 | 
				
			||||||
 | 
					import { ifDefined } from "lit/directives/if-defined.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Alert, type IAlert } from "../Alert.js";
 | 
				
			||||||
 | 
					import "../Alert.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IAlertForTesting = IAlert & { message: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const metadata: Meta<Alert> = {
 | 
				
			||||||
 | 
					    title: "Elements/<ak-alert>",
 | 
				
			||||||
 | 
					    component: "ak-alert",
 | 
				
			||||||
 | 
					    parameters: {
 | 
				
			||||||
 | 
					        docs: {
 | 
				
			||||||
 | 
					            description: "An alert",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    argTypes: {
 | 
				
			||||||
 | 
					        inline: { control: "boolean" },
 | 
				
			||||||
 | 
					        level: { control: "text" },
 | 
				
			||||||
 | 
					        icon: { control: "text" },
 | 
				
			||||||
 | 
					        // @ts-ignore
 | 
				
			||||||
 | 
					        message: { control: "text" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DefaultStory: StoryObj = {
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        inline: false,
 | 
				
			||||||
 | 
					        message: "You should be alarmed.",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // @ts-ignore
 | 
				
			||||||
 | 
					    render: ({ inline, level, icon, message }: IAlertForTesting) => {
 | 
				
			||||||
 | 
					        return html` <div style="background-color: #f0f0f0; padding: 1rem;">
 | 
				
			||||||
 | 
					            <style>
 | 
				
			||||||
 | 
					                ak-alert {
 | 
				
			||||||
 | 
					                    display: inline-block;
 | 
				
			||||||
 | 
					                    width: 32rem;
 | 
				
			||||||
 | 
					                    max-width: 32rem;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            </style>
 | 
				
			||||||
 | 
					            <ak-alert level=${ifDefined(level)} ?inline=${inline} icon=${ifDefined(icon)}>
 | 
				
			||||||
 | 
					                <p>${message}</p>
 | 
				
			||||||
 | 
					            </ak-alert>
 | 
				
			||||||
 | 
					        </div>`;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SuccessAlert = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: { ...DefaultStory, ...{ level: "success", message: "He's a tribute to your genius!" } },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const InfoAlert = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        ...DefaultStory,
 | 
				
			||||||
 | 
					        ...{ level: "info", icon: "fa-coffee", message: "It is time for coffee." },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DangerAlert = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: { ...DefaultStory, ...{ level: "danger", message: "Danger, Will Robinson!  Danger!" } },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										46
									
								
								web/src/elements/stories/AppIcon.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								web/src/elements/stories/AppIcon.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as AppIconStories from "./AppIcon.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={AppIconStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Application Icon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AppIcon displays an icon associated with an authentik application on the User Library page. It takes
 | 
				
			||||||
 | 
					an API "Application" object and a size, with a default size of "medium."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that the variables passed in are how they are used in authentik. Any string and any FontAwesome
 | 
				
			||||||
 | 
					icon supported by the current theme can be referenced.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/components/ak-app-icon.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-app-icon name=${app.name} icon=${app.metaIcon}></ak-ak-app-icon>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Standard App Icon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In this example, the app has no icon reference and is just named "Default." The first letter is used
 | 
				
			||||||
 | 
					as the icon.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AppIconStories.DefaultStory} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### App Icon with Icon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In this example, the app contains an icon reference: `{ metaIcon: "fa://fa-yin-yang" }`, which is
 | 
				
			||||||
 | 
					preferred to just using the first letter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AppIconStories.WithIcon} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### App Icon with Missing Data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This is what is shown if both the name and icon fields of an application are `undefined`. In practice,
 | 
				
			||||||
 | 
					you should never see this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={AppIconStories.AllDataUndefined} />
 | 
				
			||||||
							
								
								
									
										83
									
								
								web/src/elements/stories/AppIcon.stories.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								web/src/elements/stories/AppIcon.stories.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					import { PFSize } from "@goauthentik/common/enums.js";
 | 
				
			||||||
 | 
					import type { Meta, StoryObj } from "@storybook/web-components";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { TemplateResult, html } from "lit";
 | 
				
			||||||
 | 
					import { ifDefined } from "lit/directives/if-defined.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "../AppIcon";
 | 
				
			||||||
 | 
					import { AppIcon } from "../AppIcon";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const sizeOptions = Array.from(Object.values(PFSize));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const metadata: Meta<AppIcon> = {
 | 
				
			||||||
 | 
					    title: "Elements / <ak-app-icon>",
 | 
				
			||||||
 | 
					    component: "ak-app-icon",
 | 
				
			||||||
 | 
					    parameters: {
 | 
				
			||||||
 | 
					        docs: {
 | 
				
			||||||
 | 
					            description: {
 | 
				
			||||||
 | 
					                component: "A small card displaying an application icon",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    argTypes: {
 | 
				
			||||||
 | 
					        name: { control: "text" },
 | 
				
			||||||
 | 
					        icon: { control: "text" },
 | 
				
			||||||
 | 
					        size: { options: sizeOptions, control: "select" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const container = (testItem: TemplateResult) =>
 | 
				
			||||||
 | 
					    html` <div style="background: #f0f0f0; padding: 1em">
 | 
				
			||||||
 | 
					        <style>
 | 
				
			||||||
 | 
					            li {
 | 
				
			||||||
 | 
					                display: block;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            p {
 | 
				
			||||||
 | 
					                margin-top: 1em;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        </style>
 | 
				
			||||||
 | 
					        ${testItem}
 | 
				
			||||||
 | 
					    </div>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DefaultStory: StoryObj = {
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        name: "Demo App",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    render: ({ name, icon, size }) =>
 | 
				
			||||||
 | 
					        container(
 | 
				
			||||||
 | 
					            html`<ak-app-icon
 | 
				
			||||||
 | 
					                size=${size}
 | 
				
			||||||
 | 
					                name=${ifDefined(name)}
 | 
				
			||||||
 | 
					                icon=${ifDefined(icon)}
 | 
				
			||||||
 | 
					            ></ak-app-icon>`,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const WithIcon: StoryObj = {
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        name: "Iconic App",
 | 
				
			||||||
 | 
					        icon: "fa://fa-yin-yang",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    render: ({ name, icon, size }) =>
 | 
				
			||||||
 | 
					        container(
 | 
				
			||||||
 | 
					            html`<ak-app-icon
 | 
				
			||||||
 | 
					                size=${size}
 | 
				
			||||||
 | 
					                name=${ifDefined(name)}
 | 
				
			||||||
 | 
					                icon=${ifDefined(icon || undefined)}
 | 
				
			||||||
 | 
					            ></ak-app-icon>`,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const AllDataUndefined: StoryObj = {
 | 
				
			||||||
 | 
					    args: {},
 | 
				
			||||||
 | 
					    render: ({ name, icon, size }) =>
 | 
				
			||||||
 | 
					        container(
 | 
				
			||||||
 | 
					            html`<ak-app-icon
 | 
				
			||||||
 | 
					                size=${size}
 | 
				
			||||||
 | 
					                name=${ifDefined(name)}
 | 
				
			||||||
 | 
					                icon=${ifDefined(icon)}
 | 
				
			||||||
 | 
					            ></ak-app-icon>`,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										51
									
								
								web/src/elements/stories/Divider.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								web/src/elements/stories/Divider.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as DividerStories from "./Divider.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={DividerStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Divider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Divider is a horizontal rule, an in-page element to separate displayed items.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It has no configurable attributes. It does have a single unnamed slot, which is displayed in-line in
 | 
				
			||||||
 | 
					the center of the rule. If the CSS Base in loaded into the parent context, icons defined in the base
 | 
				
			||||||
 | 
					can be used here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/Divider.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-divider></ak-divider>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With content:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-divider><p>Time for bed!</p></ak-divider>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With an icon:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-divider><i class="fa fa-bed"></i></ak-divider>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that the Divider inherits its background from its parent component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Default Horizontal Rule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={DividerStories.DefaultStory} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### With A Message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={DividerStories.DividerWithSlottedContent} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### With an Icon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={DividerStories.DividerWithSlottedIcon} />
 | 
				
			||||||
							
								
								
									
										41
									
								
								web/src/elements/stories/Divider.stories.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								web/src/elements/stories/Divider.stories.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					import type { Meta, StoryObj } from "@storybook/web-components";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { TemplateResult, html } from "lit";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Divider } from "../Divider.js";
 | 
				
			||||||
 | 
					import "../Divider.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const metadata: Meta<Divider> = {
 | 
				
			||||||
 | 
					    title: "Elements/<ak-divider>",
 | 
				
			||||||
 | 
					    component: "ak-divider",
 | 
				
			||||||
 | 
					    parameters: {
 | 
				
			||||||
 | 
					        docs: {
 | 
				
			||||||
 | 
					            description: "our most simple divider",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const container = (content: TemplateResult) =>
 | 
				
			||||||
 | 
					    html` <div style="background-color: #f0f0f0; padding: 1rem;">
 | 
				
			||||||
 | 
					        <style>
 | 
				
			||||||
 | 
					            ak-divider {
 | 
				
			||||||
 | 
					                display: inline-block;
 | 
				
			||||||
 | 
					                width: 32rem;
 | 
				
			||||||
 | 
					                max-width: 32rem;
 | 
				
			||||||
 | 
					            }</style
 | 
				
			||||||
 | 
					        >${content}
 | 
				
			||||||
 | 
					    </div>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DefaultStory: StoryObj = {
 | 
				
			||||||
 | 
					    render: () => container(html` <ak-divider> </ak-divider> `),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DividerWithSlottedContent: StoryObj = {
 | 
				
			||||||
 | 
					    render: () => container(html` <ak-divider><p>Time for bed!</p></ak-divider> `),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DividerWithSlottedIcon: StoryObj = {
 | 
				
			||||||
 | 
					    render: () => container(html` <ak-divider><i class="fa fa-bed"></i></ak-divider> `),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										59
									
								
								web/src/elements/stories/EmptyState.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								web/src/elements/stories/EmptyState.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as EmptyStateStories from "./EmptyState.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={EmptyStateStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# EmptyState
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The EmptyState is an in-page element to indicate that something is either loading or unavailable.
 | 
				
			||||||
 | 
					When "loading" is true it displays a spinner, otherwise it displays a static icon. The default
 | 
				
			||||||
 | 
					icon is a question mark in a circle.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It has two named slots, `body` and `primary`, to communicate further details about the current state
 | 
				
			||||||
 | 
					this element is meant to display.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/EmptyState.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that the content of an alert _must_ be a valid HTML component; plain text does not work here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-empty-state icon="fa-eject"
 | 
				
			||||||
 | 
					    ><span slot="primary">This would display in the "primary" slot</span></ak-empty-state
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Default: Loading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The default state is _loading_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={EmptyStateStories.DefaultStory} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={EmptyStateStories.DefaultAndLoadingDone} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Alternative "Done" Icon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This also shows the "header" attribute filled, which is rendered in a large, dark typeface.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={EmptyStateStories.DoneWithAlternativeIcon} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### The Body Slot Filled
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The body content slot is rendered in a lighter typeface at default size.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={EmptyStateStories.WithBodySlotFilled} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### The Body and Primary Slot Filled
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The primary content is rendered in the normal dark typeface at default size. It is also spaced
 | 
				
			||||||
 | 
					significantly below the spinner itself.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={EmptyStateStories.WithBodyAndPrimarySlotsFilled} />
 | 
				
			||||||
							
								
								
									
										108
									
								
								web/src/elements/stories/EmptyState.stories.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								web/src/elements/stories/EmptyState.stories.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					import type { Meta, StoryObj } from "@storybook/web-components";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { TemplateResult, html } from "lit";
 | 
				
			||||||
 | 
					import { ifDefined } from "lit/directives/if-defined.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { EmptyState, type IEmptyState } from "../EmptyState.js";
 | 
				
			||||||
 | 
					import "../EmptyState.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const metadata: Meta<EmptyState> = {
 | 
				
			||||||
 | 
					    title: "Elements/<ak-empty-state>",
 | 
				
			||||||
 | 
					    component: "ak-empty-state",
 | 
				
			||||||
 | 
					    parameters: {
 | 
				
			||||||
 | 
					        docs: {
 | 
				
			||||||
 | 
					            description: "Our empty state spinner",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    argTypes: {
 | 
				
			||||||
 | 
					        icon: { control: "text" },
 | 
				
			||||||
 | 
					        loading: { control: "boolean" },
 | 
				
			||||||
 | 
					        fullHeight: { control: "boolean" },
 | 
				
			||||||
 | 
					        header: { control: "text" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const container = (content: TemplateResult) =>
 | 
				
			||||||
 | 
					    html` <div style="background-color: #f0f0f0; padding: 1rem;">
 | 
				
			||||||
 | 
					        <style>
 | 
				
			||||||
 | 
					            ak-divider {
 | 
				
			||||||
 | 
					                display: inline-block;
 | 
				
			||||||
 | 
					                width: 32rem;
 | 
				
			||||||
 | 
					                max-width: 32rem;
 | 
				
			||||||
 | 
					            }</style
 | 
				
			||||||
 | 
					        >${content}
 | 
				
			||||||
 | 
					    </div>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DefaultStory: StoryObj = {
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        icon: undefined,
 | 
				
			||||||
 | 
					        loading: true,
 | 
				
			||||||
 | 
					        fullHeight: false,
 | 
				
			||||||
 | 
					        header: undefined,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render: ({ icon, loading, fullHeight, header }: IEmptyState) =>
 | 
				
			||||||
 | 
					        container(
 | 
				
			||||||
 | 
					            html` <ak-empty-state
 | 
				
			||||||
 | 
					                ?loading=${loading}
 | 
				
			||||||
 | 
					                ?fullHeight=${fullHeight}
 | 
				
			||||||
 | 
					                icon=${ifDefined(icon)}
 | 
				
			||||||
 | 
					                header=${ifDefined(header)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					            </ak-empty-state>`,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DefaultAndLoadingDone = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: { ...DefaultStory, ...{ loading: false } },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DoneWithAlternativeIcon = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        ...DefaultStory,
 | 
				
			||||||
 | 
					        ...{ loading: false, icon: "fa-space-shuttle", header: "The final frontier" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const WithBodySlotFilled = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        ...DefaultStory,
 | 
				
			||||||
 | 
					        ...{ loading: false, icon: "fa-space-shuttle", header: "The final frontier" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    render: ({ icon, loading, fullHeight, header }: IEmptyState) =>
 | 
				
			||||||
 | 
					        container(html`
 | 
				
			||||||
 | 
					            <ak-empty-state
 | 
				
			||||||
 | 
					                ?loading=${loading}
 | 
				
			||||||
 | 
					                ?fullHeight=${fullHeight}
 | 
				
			||||||
 | 
					                icon=${ifDefined(icon)}
 | 
				
			||||||
 | 
					                header=${ifDefined(header)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <span slot="body">This is the body content</span>
 | 
				
			||||||
 | 
					            </ak-empty-state>
 | 
				
			||||||
 | 
					        `),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const WithBodyAndPrimarySlotsFilled = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        ...DefaultStory,
 | 
				
			||||||
 | 
					        ...{ loading: false, icon: "fa-space-shuttle", header: "The final frontier" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    render: ({ icon, loading, fullHeight, header }: IEmptyState) =>
 | 
				
			||||||
 | 
					        container(
 | 
				
			||||||
 | 
					            html` <ak-empty-state
 | 
				
			||||||
 | 
					                ?loading=${loading}
 | 
				
			||||||
 | 
					                ?fullHeight=${fullHeight}
 | 
				
			||||||
 | 
					                icon=${ifDefined(icon)}
 | 
				
			||||||
 | 
					                header=${ifDefined(header)}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					                <span slot="body">This is the body content slot</span>
 | 
				
			||||||
 | 
					                <span slot="primary">This is the primary content slot</span>
 | 
				
			||||||
 | 
					            </ak-empty-state>`,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										38
									
								
								web/src/elements/stories/Expand.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								web/src/elements/stories/Expand.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as ExpandStories from "./Expand.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={ExpandStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Expand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Expand is an in-page element used to hide cluttering details that a user may wish to reveal, such as raw
 | 
				
			||||||
 | 
					details of an alert or event.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It has one unnamed slot for the content to be displayed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/Expand.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-expand><p>Your primary content goes here</p></ak-expand>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To show the expanded content on initial render:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-expand expanded><p>Your primary content goes here</p></ak-expand>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Default: The content is hidden
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={ExpandStories.DefaultStory} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Expanded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={ExpandStories.Expanded} />
 | 
				
			||||||
							
								
								
									
										60
									
								
								web/src/elements/stories/Expand.stories.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								web/src/elements/stories/Expand.stories.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					import type { Meta, StoryObj } from "@storybook/web-components";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { TemplateResult, html } from "lit";
 | 
				
			||||||
 | 
					import { ifDefined } from "lit/directives/if-defined.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { Expand, type IExpand } from "../Expand.js";
 | 
				
			||||||
 | 
					import "../Expand.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const metadata: Meta<Expand> = {
 | 
				
			||||||
 | 
					    title: "Elements/<ak-expand>",
 | 
				
			||||||
 | 
					    component: "ak-expand",
 | 
				
			||||||
 | 
					    parameters: {
 | 
				
			||||||
 | 
					        docs: {
 | 
				
			||||||
 | 
					            description: "Our accordion component",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    argTypes: {
 | 
				
			||||||
 | 
					        expanded: { control: "boolean" },
 | 
				
			||||||
 | 
					        textOpen: { control: "text" },
 | 
				
			||||||
 | 
					        textClosed: { control: "text" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const container = (content: TemplateResult) =>
 | 
				
			||||||
 | 
					    html` <div style="background-color: #f0f0f0; padding: 1rem;">
 | 
				
			||||||
 | 
					        <style>
 | 
				
			||||||
 | 
					            ak-divider {
 | 
				
			||||||
 | 
					                display: inline-block;
 | 
				
			||||||
 | 
					                width: 32rem;
 | 
				
			||||||
 | 
					                max-width: 32rem;
 | 
				
			||||||
 | 
					            }</style
 | 
				
			||||||
 | 
					        >${content}
 | 
				
			||||||
 | 
					    </div>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DefaultStory: StoryObj = {
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        expanded: false,
 | 
				
			||||||
 | 
					        textOpen: undefined,
 | 
				
			||||||
 | 
					        textClosed: undefined,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render: ({ expanded, textOpen, textClosed }: IExpand) =>
 | 
				
			||||||
 | 
					        container(
 | 
				
			||||||
 | 
					            html` <ak-expand
 | 
				
			||||||
 | 
					                ?expanded=${expanded}
 | 
				
			||||||
 | 
					                textOpen=${ifDefined(textOpen)}
 | 
				
			||||||
 | 
					                textClosed=${ifDefined(textClosed)}
 | 
				
			||||||
 | 
					                ><div>
 | 
				
			||||||
 | 
					                    <p>Μήτ᾽ ἔμοι μέλι μήτε μέλισσα</p>
 | 
				
			||||||
 | 
					                    <p>"Neither the bee nor the honey for me." - Sappho, 600 BC</p>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </ak-expand>`,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export const Expanded = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: { ...DefaultStory, ...{ expanded: true } },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										53
									
								
								web/src/elements/stories/Label.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								web/src/elements/stories/Label.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as LabelStories from "./Label.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={LabelStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Labels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Labels are in-page elements that provide pointers or guidance. Frequently called "chips" in other
 | 
				
			||||||
 | 
					design systems. Labels are used alongside other elements to warn users of conditions or status that
 | 
				
			||||||
 | 
					they might want to be aware of
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/elements/Label.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that the content of a label _must_ be a valid HTML component; plain text does not work here. The
 | 
				
			||||||
 | 
					default label is informational:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-label><p>This is the content of your alert!</p></ak-label>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Default: Info
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The default state of an alert is _info_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={LabelStories.DefaultStory} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Warning
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The icon can be changed via the `icon` attribute. It takes the name of a valid `fas`-class Font
 | 
				
			||||||
 | 
					Awesome icon. Changing the icon can be helpful, as not everyone can see the color changes. It has
 | 
				
			||||||
 | 
					also been set "compact" to show the difference.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={LabelStories.CompactWarningLabel} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Success
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This label is illustrated using the PFColor enum, rather than the attribute name. Note that the content
 | 
				
			||||||
 | 
					is _slotted_, and can be styled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={LabelStories.SuccessLabel} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Danger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This label is illustrated using the PFColor enum, rather than the attribute name.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={LabelStories.DangerLabel} />
 | 
				
			||||||
							
								
								
									
										83
									
								
								web/src/elements/stories/Label.stories.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								web/src/elements/stories/Label.stories.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					import type { Meta, StoryObj } from "@storybook/web-components";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { html } from "lit";
 | 
				
			||||||
 | 
					import { ifDefined } from "lit/directives/if-defined.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { type ILabel, Label, PFColor } from "../Label.js";
 | 
				
			||||||
 | 
					import "../Label.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ILabelForTesting = ILabel & { message: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const metadata: Meta<Label> = {
 | 
				
			||||||
 | 
					    title: "Elements/<ak-label>",
 | 
				
			||||||
 | 
					    component: "ak-label",
 | 
				
			||||||
 | 
					    parameters: {
 | 
				
			||||||
 | 
					        docs: {
 | 
				
			||||||
 | 
					            description: "An alert",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    argTypes: {
 | 
				
			||||||
 | 
					        compact: { control: "boolean" },
 | 
				
			||||||
 | 
					        color: { control: "text" },
 | 
				
			||||||
 | 
					        icon: { control: "text" },
 | 
				
			||||||
 | 
					        // @ts-ignore
 | 
				
			||||||
 | 
					        message: { control: "text" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default metadata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DefaultStory: StoryObj = {
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        compact: false,
 | 
				
			||||||
 | 
					        message: "Eat at Joe's.",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // @ts-ignore
 | 
				
			||||||
 | 
					    render: ({ compact, color, icon, message }: ILabelForTesting) => {
 | 
				
			||||||
 | 
					        return html` <div style="background-color: #f0f0f0; padding: 1rem;">
 | 
				
			||||||
 | 
					            <style>
 | 
				
			||||||
 | 
					                ak-label {
 | 
				
			||||||
 | 
					                    display: inline-block;
 | 
				
			||||||
 | 
					                    width: 48rem;
 | 
				
			||||||
 | 
					                    max-width: 48rem;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            </style>
 | 
				
			||||||
 | 
					            <ak-label color=${ifDefined(color)} ?compact=${compact} icon=${ifDefined(icon)}>
 | 
				
			||||||
 | 
					                <p>${message}</p>
 | 
				
			||||||
 | 
					            </ak-label>
 | 
				
			||||||
 | 
					        </div>`;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SuccessLabel = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        ...DefaultStory,
 | 
				
			||||||
 | 
					        ...{
 | 
				
			||||||
 | 
					            color: PFColor.Green,
 | 
				
			||||||
 | 
					            message: html`I'll show them! I'll show them <i>all</i> ! Mwahahahahaha!`,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CompactWarningLabel = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        ...DefaultStory,
 | 
				
			||||||
 | 
					        ...{
 | 
				
			||||||
 | 
					            compact: true,
 | 
				
			||||||
 | 
					            color: "warning",
 | 
				
			||||||
 | 
					            icon: "fa-coffee",
 | 
				
			||||||
 | 
					            message: "It is time for coffee.",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DangerLabel = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        ...DefaultStory,
 | 
				
			||||||
 | 
					        ...{ color: "danger", message: "Grave danger? Is there another kind?" },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										36
									
								
								web/src/elements/stories/LoadingOverlay.docs.mdx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/src/elements/stories/LoadingOverlay.docs.mdx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as LoadingOverlayStories from "./LoadingOverlay.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={LoadingOverlayStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# 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
 | 
				
			||||||
 | 
					<ak-loading-overlay topmost>
 | 
				
			||||||
 | 
					    <span>This would display below the loading spinner</span>
 | 
				
			||||||
 | 
					</ak-loading-overlay>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={LoadingOverlayStories.DefaultStory} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### With a message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={LoadingOverlayStories.WithAMessage} />
 | 
				
			||||||
							
								
								
									
										74
									
								
								web/src/elements/stories/LoadingOverlay.stories.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								web/src/elements/stories/LoadingOverlay.stories.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					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<LoadingOverlay> = {
 | 
				
			||||||
 | 
					    title: "Elements/<ak-loading-overlay>",
 | 
				
			||||||
 | 
					    component: "ak-loading-overlay",
 | 
				
			||||||
 | 
					    parameters: {
 | 
				
			||||||
 | 
					        docs: {
 | 
				
			||||||
 | 
					            description: "Our empty state spinner",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    argTypes: {
 | 
				
			||||||
 | 
					        topmost: { control: "boolean" },
 | 
				
			||||||
 | 
					        // @ts-ignore
 | 
				
			||||||
 | 
					        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` <div id="main-container">${this.content}</div>`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DefaultStory: StoryObj = {
 | 
				
			||||||
 | 
					    args: {
 | 
				
			||||||
 | 
					        topmost: undefined,
 | 
				
			||||||
 | 
					        // @ts-ignore
 | 
				
			||||||
 | 
					        message: undefined,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // @ts-ignore
 | 
				
			||||||
 | 
					    render: ({ topmost, message }: ILoadingOverlay) => {
 | 
				
			||||||
 | 
					        message = typeof message === "string" ? html`<span>${message}</span>` : message;
 | 
				
			||||||
 | 
					        const content = html` <ak-loading-overlay ?topmost=${topmost}
 | 
				
			||||||
 | 
					            >${message ?? ""}
 | 
				
			||||||
 | 
					        </ak-loading-overlay>`;
 | 
				
			||||||
 | 
					        return html`<ak-storybook-demo-container
 | 
				
			||||||
 | 
					            .content=${content}
 | 
				
			||||||
 | 
					        ></ak-storybook-demo-container>`;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const WithAMessage: StoryObj = {
 | 
				
			||||||
 | 
					    ...DefaultStory,
 | 
				
			||||||
 | 
					    args: { ...DefaultStory.args, message: html`<p>Overlay with a message</p>` },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { PFSize } from "@goauthentik/common/enums.js";
 | 
					import { PFSize } from "@goauthentik/common/enums.js";
 | 
				
			||||||
import { truncateWords } from "@goauthentik/common/utils";
 | 
					import { truncateWords } from "@goauthentik/common/utils";
 | 
				
			||||||
import "@goauthentik/components/ak-app-icon";
 | 
					import "@goauthentik/elements/AppIcon";
 | 
				
			||||||
import { AKElement, rootInterface } from "@goauthentik/elements/Base";
 | 
					import { AKElement, rootInterface } from "@goauthentik/elements/Base";
 | 
				
			||||||
import "@goauthentik/elements/Expand";
 | 
					import "@goauthentik/elements/Expand";
 | 
				
			||||||
import "@goauthentik/user/LibraryApplication/RACLaunchEndpointModal";
 | 
					import "@goauthentik/user/LibraryApplication/RACLaunchEndpointModal";
 | 
				
			||||||
@ -115,7 +115,6 @@ export class LibraryApplication extends AKElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const classes = { "pf-m-selectable": this.selected, "pf-m-selected": this.selected };
 | 
					        const classes = { "pf-m-selectable": this.selected, "pf-m-selected": this.selected };
 | 
				
			||||||
        const styles = this.background ? { background: this.background } : {};
 | 
					        const styles = this.background ? { background: this.background } : {};
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return html` <div
 | 
					        return html` <div
 | 
				
			||||||
            class="pf-c-card pf-m-hoverable pf-m-compact ${classMap(classes)}"
 | 
					            class="pf-c-card pf-m-hoverable pf-m-compact ${classMap(classes)}"
 | 
				
			||||||
            style=${styleMap(styles)}
 | 
					            style=${styleMap(styles)}
 | 
				
			||||||
@ -125,7 +124,11 @@ export class LibraryApplication extends AKElement {
 | 
				
			|||||||
                    href="${ifDefined(this.application.launchUrl ?? "")}"
 | 
					                    href="${ifDefined(this.application.launchUrl ?? "")}"
 | 
				
			||||||
                    target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
 | 
					                    target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                    <ak-app-icon size=${PFSize.Large} .app=${this.application}></ak-app-icon>
 | 
					                    <ak-app-icon
 | 
				
			||||||
 | 
					                        size=${PFSize.Large}
 | 
				
			||||||
 | 
					                        name=${this.application.name}
 | 
				
			||||||
 | 
					                        icon=${ifDefined(this.application.metaIcon || undefined)}
 | 
				
			||||||
 | 
					                    ></ak-app-icon>
 | 
				
			||||||
                </a>
 | 
					                </a>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <div class="pf-c-card__title">${this.renderLaunch()}</div>
 | 
					            <div class="pf-c-card__title">${this.renderLaunch()}</div>
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					import { Canvas, Description, Meta, Story, Title } from "@storybook/blocks";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import * as ApplicationEmptyStateStories from "./ApplicationEmptyState.stories";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Meta of={ApplicationEmptyStateStories} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Application List Empty State Indicator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A custom component for informing the user that they have no applications. If the user is
 | 
				
			||||||
 | 
					an administrator (set via an attribute), a link to the "Create a new application" button
 | 
				
			||||||
 | 
					will be provided
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Typescript
 | 
				
			||||||
 | 
					import "@goauthentik/user/LibraryPage/ApplicationEmptyState.js";
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<ak-library-application-empty-list></ak-library-application-empty-list>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Demo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### What an ordinary user sees
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={ApplicationEmptyStateStories.OrdinaryUser} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### What an Admin sees
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<Story of={ApplicationEmptyStateStories.AdminUser} />
 | 
				
			||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import { html } from "lit";
 | 
					import { html } from "lit";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "./ak-library-application-empty-list";
 | 
					import "../ak-library-application-empty-list";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    title: "Elements / Application Empty State",
 | 
					    title: "Users / <ak-library-application-empty-list>",
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const OrdinaryUser = () =>
 | 
					export const OrdinaryUser = () =>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user