web: migrate dropdowns to wizards (#2633)

* web/admin: add basic wizards for providers

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: add dark mode for wizard

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: migrate policies to wizard

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* start source

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* policies: sanitze_dict when returning log messages during tests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* Revert "web/admin: migrate policies to wizard"

This reverts commit d8b7f62d3e.

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	web/src/locales/zh-Hans.po
#	web/src/locales/zh-Hant.po
#	web/src/locales/zh_TW.po

* web: rewrite wizard to be element based

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* further cleanup

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* update sources

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: migrate property mappings

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* migrate stages

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* migrate misc dropdowns

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* migrate outpost integrations

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens L
2022-04-02 19:48:17 +02:00
committed by GitHub
parent 7a93614e4b
commit 508cec2fd5
36 changed files with 4872 additions and 2903 deletions

View File

@ -0,0 +1,33 @@
import { t } from "@lingui/macro";
import { customElement } from "lit/decorators.js";
import { Form } from "../forms/Form";
import { WizardPage } from "./WizardPage";
@customElement("ak-wizard-page-form")
export class FormWizardPage extends WizardPage {
_isValid = true;
isValid(): boolean {
return this._isValid;
}
nextCallback = async () => {
const form = this.querySelector<Form<unknown>>("*");
if (!form) {
return Promise.reject(t`No form found`);
}
const formPromise = form.submit(new Event("submit"));
if (!formPromise) {
return Promise.reject(t`Form didn't return a promise for submitting`);
}
return formPromise
.then(() => {
return true;
})
.catch(() => {
return false;
});
};
}

View File

@ -0,0 +1,174 @@
import { t } from "@lingui/macro";
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
import { property } from "@lit/reactive-element/decorators/property.js";
import { CSSResult, TemplateResult, html } from "lit";
import { state } from "lit/decorators.js";
import PFWizard from "@patternfly/patternfly/components/Wizard/wizard.css";
import { ModalButton } from "../buttons/ModalButton";
import { WizardPage } from "./WizardPage";
@customElement("ak-wizard")
export class Wizard extends ModalButton {
@property()
header?: string;
@property()
description?: string;
static get styles(): CSSResult[] {
return super.styles.concat(PFWizard);
}
@property({ attribute: false })
steps: string[] = [];
@state()
_currentStep?: WizardPage;
set currentStep(value: WizardPage | undefined) {
this._currentStep = value;
if (this._currentStep) {
this._currentStep.activeCallback();
this._currentStep.requestUpdate();
}
}
get currentStep(): WizardPage | undefined {
return this._currentStep;
}
setSteps(...steps: string[]): void {
this.steps = steps;
this.requestUpdate();
}
finalHandler?: () => Promise<void>;
renderModalInner(): TemplateResult {
const firstPage = this.querySelector<WizardPage>(`[slot=${this.steps[0]}]`);
if (!this.currentStep && firstPage) {
this.currentStep = firstPage;
}
this.currentStep?.requestUpdate();
const currentIndex = this.currentStep ? this.steps.indexOf(this.currentStep.slot) : 0;
return html`<div class="pf-c-wizard">
<div class="pf-c-wizard__header">
<button
class="pf-c-button pf-m-plain pf-c-wizard__close"
type="button"
aria-label="${t`Close`}"
>
<i class="fas fa-times" aria-hidden="true"></i>
</button>
<h1 class="pf-c-title pf-m-3xl pf-c-wizard__title">${this.header}</h1>
<p class="pf-c-wizard__description">${this.description}</p>
</div>
<div class="pf-c-wizard__outer-wrap">
<div class="pf-c-wizard__inner-wrap">
<nav class="pf-c-wizard__nav">
<ol class="pf-c-wizard__nav-list">
${this.steps.map((step, idx) => {
const currentIdx = this.currentStep
? this.steps.indexOf(this.currentStep.slot)
: 0;
return html`
<li class="pf-c-wizard__nav-item">
<button
class="pf-c-wizard__nav-link ${idx === currentIdx
? "pf-m-current"
: ""}"
?disabled=${currentIdx < idx}
@click=${() => {
const stepEl = this.querySelector<WizardPage>(
`[slot=${step}]`,
);
if (stepEl) {
this.currentStep = stepEl;
}
}}
>
${this.querySelector<WizardPage>(
`[slot=${step}]`,
)?.sidebarLabel()}
</button>
</li>
`;
})}
</ol>
</nav>
<main class="pf-c-wizard__main">
<div class="pf-c-wizard__main-body">
<slot name=${this.currentStep?.slot || this.steps[0]}></slot>
</div>
</main>
</div>
<footer class="pf-c-wizard__footer">
<button
class="pf-c-button pf-m-primary"
type="submit"
?disabled=${!this._currentStep?.isValid()}
@click=${async () => {
const cb = await this.currentStep?.nextCallback();
if (!cb) {
return;
}
if (currentIndex === this.steps.length - 1) {
if (this.finalHandler) {
await this.finalHandler();
}
this.open = false;
} else {
const nextPage = this.querySelector<WizardPage>(
`[slot=${this.steps[currentIndex + 1]}]`,
);
if (nextPage) {
this.currentStep = nextPage;
}
}
}}
>
${currentIndex === this.steps.length - 1 ? t`Finish` : t`Next`}
</button>
${(this.currentStep ? this.steps.indexOf(this.currentStep.slot) : 0) > 0
? html`
<button
class="pf-c-button pf-m-secondary"
type="button"
@click=${() => {
const prevPage = this.querySelector<WizardPage>(
`[slot=${this.steps[currentIndex - 1]}]`,
);
if (prevPage) {
this.currentStep = prevPage;
}
}}
>
${t`Back`}
</button>
`
: html``}
<div class="pf-c-wizard__footer-cancel">
<button
class="pf-c-button pf-m-link"
type="button"
@click=${() => {
this.open = false;
const firstPage = this.querySelector<WizardPage>(
`[slot=${this.steps[0]}]`,
);
if (firstPage) {
this.currentStep = firstPage;
}
}}
>
${t`Cancel`}
</button>
</div>
</footer>
</div>
</div>`;
}
}

View File

@ -0,0 +1,46 @@
import { LitElement, PropertyDeclaration, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { Wizard } from "./Wizard";
@customElement("ak-wizard-page")
export class WizardPage extends LitElement {
@property()
sidebarLabel: () => string = () => {
return "UNNAMED";
};
isValid(): boolean {
return this._isValid;
}
get host(): Wizard {
return this.parentElement as Wizard;
}
_isValid = false;
activeCallback: () => Promise<void> = () => {
return Promise.resolve();
};
nextCallback: () => Promise<boolean> = async () => {
return true;
};
requestUpdate(
name?: PropertyKey,
oldValue?: unknown,
options?: PropertyDeclaration<unknown, unknown>,
): void {
this.querySelectorAll("*").forEach((el) => {
if ("requestUpdate" in el) {
(el as LitElement).requestUpdate();
}
});
return super.requestUpdate(name, oldValue, options);
}
render(): TemplateResult {
return html`<slot></slot>`;
}
}