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.
This commit is contained in:
Ken Sternberg 2023-12-27 11:27:15 -08:00
parent 6fb543b94f
commit 1cba9e88cb
2 changed files with 185 additions and 0 deletions

View File

@ -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<AkDualSelectControls> = {
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` <div style="background: #fff; padding: 2em">
<style>
li {
display: block;
}
p {
margin-top: 1em;
}
</style>
<ak-message-container></ak-message-container>
${testItem}
<p>Messages received from the button:</p>
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
</div>`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const displayMessage = (result: any) => {
const doc = new DOMParser().parseFromString(`<li><i>Event</i>: ${result}</li>`, "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` <ak-dual-select-controls></ak-dual-select-controls>`),
};
export const AddActive: Story = {
render: () => container(html` <ak-dual-select-controls add-active></ak-dual-select-controls>`),
};
export const RemoveActive: Story = {
render: () =>
container(html` <ak-dual-select-controls remove-active></ak-dual-select-controls>`),
};
export const AddAllActive: Story = {
render: () =>
container(
html` <ak-dual-select-controls
enable-select-all
add-all-active
></ak-dual-select-controls>`,
),
};
export const RemoveAllActive: Story = {
render: () =>
container(
html` <ak-dual-select-controls
enable-select-all
remove-all-active
></ak-dual-select-controls>`,
),
};

View File

@ -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`
<div class="pf-c-dual-list-selector__controls-item">
<button
?aria-disabled=${this.disabled || !active}
?disabled=${this.disabled || !active}
aria-label=${label}
class="pf-c-button pf-m-plain"
type="button"
@click=${() => this.onClick(event)}
data-ouia-component-type="AK/Button"
>
<i class="fa ${direction}"></i>
</button>
</div>
</div>`;
}
render() {
// prettier-ignore
return html`
<div class="pf-c-dual-list-selector">
<div class="pf-c-dual-list-selector__controls">
${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")}
</div>
</div>
`;
}
}
export default AkDualSelectControls;