From 1cba9e88cb961805b6991af089ea0d2c08d43986 Mon Sep 17 00:00:00 2001 From: Ken Sternberg Date: Wed, 27 Dec 2023 11:27:15 -0800 Subject: [PATCH] web: provide a "select / select all" tool for the dual list multiselect **This commit** Provides one of several of the sub-controls needed to make the multi-list multi-select thing work. This is the simplest control, and I decided to go with it first because it's all presentation; all it does is show the buttons and send events from those buttons. A Storybook component is provided to show how well it works. --- .../ak-dual-select-controls.stories.ts | 101 ++++++++++++++++++ .../ak-dual-select/ak-dual-select-controls.ts | 84 +++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 web/src/elements/ak-dual-select/ak-dual-select-controls.stories.ts create mode 100644 web/src/elements/ak-dual-select/ak-dual-select-controls.ts diff --git a/web/src/elements/ak-dual-select/ak-dual-select-controls.stories.ts b/web/src/elements/ak-dual-select/ak-dual-select-controls.stories.ts new file mode 100644 index 000000000..7c903a618 --- /dev/null +++ b/web/src/elements/ak-dual-select/ak-dual-select-controls.stories.ts @@ -0,0 +1,101 @@ +import "@goauthentik/elements/messages/MessageContainer"; +import { Meta, StoryObj } from "@storybook/web-components"; + +import { TemplateResult, html } from "lit"; + +import "./ak-dual-select-controls"; +import { AkDualSelectControls } from "./ak-dual-select-controls"; + +const metadata: Meta = { + title: "Elements / Dual Select / Control Panel", + component: "ak-dual-select-controls", + parameters: { + docs: { + description: { + component: "The vertical panel separating two dual-select elements.", + }, + }, + }, + argTypes: { + addActive: { + type: "boolean", + description: + "Highlighted if the sample panel has something to move to the result panel.", + }, + removeActive: { + type: "boolean", + description: + "Highlighted if the result panel has something to move to the sample panel.", + }, + selectAll: { + type: "boolean", + description: "Enable if you want both the 'move all visible' buttons.", + }, + }, +}; + +export default metadata; + +const container = (testItem: TemplateResult) => + html`
+ + + ${testItem} +

Messages received from the button:

+
    +
    `; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const displayMessage = (result: any) => { + const doc = new DOMParser().parseFromString(`
  • Event: ${result}
  • `, "text/xml"); + const target = document.querySelector("#action-button-message-pad"); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + target!.appendChild(doc.firstChild!); +}; + +window.addEventListener("ak-dual-select-add", () => displayMessage("add")); +window.addEventListener("ak-dual-select-remove", () => displayMessage("remove")); +window.addEventListener("ak-dual-select-add-all", () => displayMessage("add all")); +window.addEventListener("ak-dual-select-remove-all", () => displayMessage("remove all")); + +type Story = StoryObj; + +export const Default: Story = { + render: () => container(html` `), +}; + +export const AddActive: Story = { + render: () => container(html` `), +}; + +export const RemoveActive: Story = { + render: () => + container(html` `), +}; + +export const AddAllActive: Story = { + render: () => + container( + html` `, + ), +}; + +export const RemoveAllActive: Story = { + render: () => + container( + html` `, + ), +}; diff --git a/web/src/elements/ak-dual-select/ak-dual-select-controls.ts b/web/src/elements/ak-dual-select/ak-dual-select-controls.ts new file mode 100644 index 000000000..0b84f32ae --- /dev/null +++ b/web/src/elements/ak-dual-select/ak-dual-select-controls.ts @@ -0,0 +1,84 @@ +import { AKElement } from "@goauthentik/elements/Base"; +import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; + +import { msg } from "@lit/localize"; +import { html, nothing } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import PFButton from "@patternfly/patternfly/components/Button/button.css"; +import PFDualListSelector from "@patternfly/patternfly/components/DualListSelector/dual-list-selector.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +const styles = [PFBase, PFButton, PFDualListSelector]; + +@customElement("ak-dual-select-controls") +export class AkDualSelectControls extends CustomEmitterElement(AKElement) { + static get styles() { + return styles; + } + + @property({ attribute: "add-active", type: Boolean }) + addActive = false; + + @property({ attribute: "remove-active", type: Boolean }) + removeActive = false; + + @property({ attribute: "add-all-active", type: Boolean }) + addAllActive = false; + + @property({ attribute: "remove-all-active", type: Boolean }) + removeAllActive = false; + + @property({ attribute: "disabled", type: Boolean }) + disabled = false; + + @property({ attribute: "enable-select-all", type: Boolean }) + selectAll = false; + + constructor() { + super(); + this.onClick = this.onClick.bind(this); + } + + onClick(eventName: string) { + this.dispatchCustomEvent(eventName); + } + + renderButton(label: string, event: string, active: boolean, direction: string) { + return html` +
    + +
    + `; + } + + render() { + // prettier-ignore + return html` +
    +
    + ${this.renderButton(msg("Add"), "ak-dual-select-add", this.addActive, "fa-angle-right")} + ${this.selectAll + ? html` + ${this.renderButton(msg("Add All"), "ak-dual-select-add-all", this.addAllActive, "fa-angle-double-right")} + ${this.renderButton(msg("Remove All"), "ak-dual-select-remove-all", this.removeAllActive, "fa-angle-double-left")} + ` + : nothing} + ${this.renderButton(msg("Remove"), "ak-dual-select-remove", this.removeActive, "fa-angle-left")} +
    +
    + `; + } +} + +export default AkDualSelectControls;