From 356488809c5e19f3d17d7609bf4d6d9d07d2bca3 Mon Sep 17 00:00:00 2001 From: Ken Sternberg Date: Fri, 11 Aug 2023 15:48:31 -0700 Subject: [PATCH] It looks like my brilliant strategy has hit a snag. The idea is simple. Let's start with this picture: ``` ``` - ApplicationWizard has a Context for the ApplicationProviderPair (or whatever it's going to be). This context does not know about the steps; it just knows about: the "application" object, the "provider" object, and a discriminator to know *which* provider the user has selected. - ApplicationWizard has Steps that, among other things, provides Panels for: - Application - Pick Provider - Configure Provider - Submit ApplicationProviderPair to the back-end - The WizardFrame renders the CurrentPanel for the CurrentStep The CurrentPanel gets its data from the ApplicationWizard in the form of a Context. It then sends messages (events) to ApplicationWizard about the contents of each field as the user is filling out the form, so that the ApplicationWizard can record those in the ApplicationProviderPair for later submission. When a CurrentForm is valid, the ApplicationWizard updates the Steps object to show that the "Next button" on the Wizard is now available. In this way, the user can progress through the system. When they get to the last page, we can provide in the ApplicationWizard with the means to submit the form and/or send the user back to the page with the validation failure. Problem: The context is being updated in real-time, which is triggering re-renders of the form. This leads to focus problems as the fields that are not yet valid are triggering "focus grab" behavior. This is a classic problem with "controlled" inputs. What we really want is for the CurrentPanel to not re-render at all, but to behave like a normal, uncontrolled form, and let the browser do most of the work. We still want the [Next] button to enable when the form is valid enough to permit that. --- Other details: I've ripped out a lot of Jen's work, which is probably a mistake. It's still preserved elsewhere. I've also cleaned up the various wizardly things to try and look organized. It *looks* like it should work, it just... doesn't. Not yet. --- ...plicationWizardCss.ts => BasePanel.css.ts} | 0 ...licationWizardPageBase.ts => BasePanel.ts} | 5 +- .../wizard/TypeApplicationWizardPage.ts | 85 ------------------- .../wizard/ak-application-wizard-context.ts | 78 ----------------- .../wizard/ak-application-wizard.ts | 26 ++---- ...-application-wizard-application-details.ts | 4 +- ...ion-wizard-authentication-method-choice.ts | 4 +- web/src/admin/applications/wizard/design.md | 20 ----- .../link/TypeLinkApplicationWizardPage.ts | 30 ------- .../BaseProviderPanel.ts} | 4 +- ...pplication-wizard-authentication-method.ts | 12 +-- .../{ => methods}/ldap/LDAPOptionsAndHelp.ts | 0 ...plication-wizard-authentication-by-ldap.ts | 4 +- ...lication-wizard-authentication-by-oauth.ts | 4 +- .../proxy/AuthenticationByProxyPage.ts | 4 +- ...wizard-authentication-for-reverse-proxy.ts | 0 ...authentication-for-single-forward-proxy.ts | 0 .../{ => methods}/saml/SamlProviderOptions.ts | 0 ...rd-authentication-by-saml-configuration.ts | 4 +- ...on-wizard-authentication-by-saml-import.ts | 4 +- .../saml/saml-property-mappings-search.ts | 0 .../TypeOAuthAPIApplicationWizardPage.ts | 35 -------- .../oauth/TypeOAuthApplicationWizardPage.ts | 84 ------------------ .../TypeOAuthCodeApplicationWizardPage.ts | 57 ------------- .../TypeOAuthImplicitApplicationWizardPage.ts | 15 ---- .../proxy/TypeProxyApplicationWizardPage.ts | 64 -------------- .../saml/TypeSAMLApplicationWizardPage.ts | 66 -------------- .../TypeSAMLConfigApplicationWizardPage.ts | 57 ------------- .../TypeSAMLImportApplicationWizardPage.ts | 57 ------------- .../{ApplicationWizardSteps.ts => steps.ts} | 14 ++- ...ak-application-context-display-for-test.ts | 2 +- .../ak-wizard-main/ak-wizard-frame.ts | 28 ++++-- .../ak-wizard-main/ak-wizard-main.ts | 7 +- web/src/components/ak-wizard-main/index.ts | 5 +- .../stories/ak-wizard-main.stories.ts | 7 +- web/src/components/ak-wizard-main/types.ts | 10 +-- 36 files changed, 67 insertions(+), 729 deletions(-) rename web/src/admin/applications/wizard/{ApplicationWizardCss.ts => BasePanel.css.ts} (100%) rename web/src/admin/applications/wizard/{ApplicationWizardPageBase.ts => BasePanel.ts} (88%) delete mode 100644 web/src/admin/applications/wizard/TypeApplicationWizardPage.ts delete mode 100644 web/src/admin/applications/wizard/ak-application-wizard-context.ts delete mode 100644 web/src/admin/applications/wizard/design.md delete mode 100644 web/src/admin/applications/wizard/link/TypeLinkApplicationWizardPage.ts rename web/src/admin/applications/wizard/{ApplicationWizardProviderPageBase.ts => methods/BaseProviderPanel.ts} (77%) rename web/src/admin/applications/wizard/{auth-method => methods}/ak-application-wizard-authentication-method.ts (59%) rename web/src/admin/applications/wizard/{ => methods}/ldap/LDAPOptionsAndHelp.ts (100%) rename web/src/admin/applications/wizard/{ => methods}/ldap/ak-application-wizard-authentication-by-ldap.ts (97%) rename web/src/admin/applications/wizard/{ => methods}/oauth/ak-application-wizard-authentication-by-oauth.ts (98%) rename web/src/admin/applications/wizard/{ => methods}/proxy/AuthenticationByProxyPage.ts (98%) rename web/src/admin/applications/wizard/{ => methods}/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts (100%) rename web/src/admin/applications/wizard/{ => methods}/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts (100%) rename web/src/admin/applications/wizard/{ => methods}/saml/SamlProviderOptions.ts (100%) rename web/src/admin/applications/wizard/{ => methods}/saml/ak-application-wizard-authentication-by-saml-configuration.ts (98%) rename web/src/admin/applications/wizard/{ => methods}/saml/ak-application-wizard-authentication-by-saml-import.ts (89%) rename web/src/admin/applications/wizard/{ => methods}/saml/saml-property-mappings-search.ts (100%) delete mode 100644 web/src/admin/applications/wizard/oauth/TypeOAuthAPIApplicationWizardPage.ts delete mode 100644 web/src/admin/applications/wizard/oauth/TypeOAuthApplicationWizardPage.ts delete mode 100644 web/src/admin/applications/wizard/oauth/TypeOAuthCodeApplicationWizardPage.ts delete mode 100644 web/src/admin/applications/wizard/oauth/TypeOAuthImplicitApplicationWizardPage.ts delete mode 100644 web/src/admin/applications/wizard/proxy/TypeProxyApplicationWizardPage.ts delete mode 100644 web/src/admin/applications/wizard/saml/TypeSAMLApplicationWizardPage.ts delete mode 100644 web/src/admin/applications/wizard/saml/TypeSAMLConfigApplicationWizardPage.ts delete mode 100644 web/src/admin/applications/wizard/saml/TypeSAMLImportApplicationWizardPage.ts rename web/src/admin/applications/wizard/{ApplicationWizardSteps.ts => steps.ts} (70%) diff --git a/web/src/admin/applications/wizard/ApplicationWizardCss.ts b/web/src/admin/applications/wizard/BasePanel.css.ts similarity index 100% rename from web/src/admin/applications/wizard/ApplicationWizardCss.ts rename to web/src/admin/applications/wizard/BasePanel.css.ts diff --git a/web/src/admin/applications/wizard/ApplicationWizardPageBase.ts b/web/src/admin/applications/wizard/BasePanel.ts similarity index 88% rename from web/src/admin/applications/wizard/ApplicationWizardPageBase.ts rename to web/src/admin/applications/wizard/BasePanel.ts index 19db5ecbb..f469a029d 100644 --- a/web/src/admin/applications/wizard/ApplicationWizardPageBase.ts +++ b/web/src/admin/applications/wizard/BasePanel.ts @@ -4,9 +4,10 @@ import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter"; import { consume } from "@lit-labs/context"; import { query, state } from "@lit/reactive-element/decorators.js"; -import { styles as AwadStyles } from "./ApplicationWizardCss"; -import type { WizardState } from "./ak-application-wizard-context"; +import { styles as AwadStyles } from "./BasePanel.css"; + import { applicationWizardContext } from "./ak-application-wizard-context-name"; +import type { WizardState } from "./types"; export class ApplicationWizardPageBase extends CustomEmitterElement(AKElement) { static get styles() { diff --git a/web/src/admin/applications/wizard/TypeApplicationWizardPage.ts b/web/src/admin/applications/wizard/TypeApplicationWizardPage.ts deleted file mode 100644 index ff55cb67c..000000000 --- a/web/src/admin/applications/wizard/TypeApplicationWizardPage.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { WizardPage } from "@goauthentik/elements/wizard/WizardPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { CSSResult, TemplateResult, html } from "lit"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -import { TypeCreate } from "@goauthentik/api"; - -@customElement("ak-application-wizard-type") -export class TypeApplicationWizardPage extends WizardPage { - applicationTypes: TypeCreate[] = [ - { - component: "ak-application-wizard-type-oauth", - name: msg("OAuth2/OIDC"), - description: msg("Modern applications, APIs and Single-page applications."), - modelName: "", - }, - { - component: "ak-application-wizard-type-saml", - name: msg("SAML"), - description: msg( - "XML-based SSO standard. Use this if your application only supports SAML.", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-proxy", - name: msg("Proxy"), - description: msg("Legacy applications which don't natively support SSO."), - modelName: "", - }, - { - component: "ak-application-wizard-type-ldap", - name: msg("LDAP"), - description: msg( - "Provide an LDAP interface for applications and users to authenticate against.", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-link", - name: msg("Link"), - description: msg( - "Provide an LDAP interface for applications and users to authenticate against.", - ), - modelName: "", - }, - ]; - - sidebarLabel = () => msg("Authentication method"); - - static get styles(): CSSResult[] { - return [PFBase, PFButton, PFForm, PFRadio]; - } - - render(): TemplateResult { - return html`
- ${this.applicationTypes.map((type) => { - return html`
- { - this.host.steps = [ - "ak-application-wizard-initial", - "ak-application-wizard-type", - type.component, - ]; - this.host.isValid = true; - }} - /> - - ${type.description} -
`; - })} -
`; - } -} diff --git a/web/src/admin/applications/wizard/ak-application-wizard-context.ts b/web/src/admin/applications/wizard/ak-application-wizard-context.ts deleted file mode 100644 index 0dba5171b..000000000 --- a/web/src/admin/applications/wizard/ak-application-wizard-context.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter"; - -import { provide } from "@lit-labs/context"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { property } from "@lit/reactive-element/decorators/property.js"; -import { LitElement, html } from "lit"; - -import { - Application, - LDAPProvider, - OAuth2Provider, - ProxyProvider, - RadiusProvider, - SAMLProvider, - SCIMProvider, -} from "@goauthentik/api"; - -import applicationWizardContext from "./ak-application-wizard-context-name"; - -// my-context.ts - -type OneOfProvider = - | Partial - | Partial - | Partial - | Partial - | Partial - | Partial; - -export interface WizardState { - step: number; - providerType: string; - application: Partial; - provider: OneOfProvider; -} - -type WizardStateEvent = WizardState & { target?: HTMLInputElement }; - -@customElement("ak-application-wizard-context") -export class AkApplicationWizardContext extends CustomListenerElement(LitElement) { - /** - * Providing a context at the root element - */ - @provide({ context: applicationWizardContext }) - @property({ attribute: false }) - wizardState: WizardState = { - step: 0, - providerType: "", - application: {}, - provider: {}, - }; - - constructor() { - super(); - this.handleUpdate = this.handleUpdate.bind(this); - } - - connectedCallback() { - super.connectedCallback(); - this.addCustomListener("ak-wizard-update", this.handleUpdate); - } - - disconnectedCallback() { - this.removeCustomListener("ak-wizard-update", this.handleUpdate); - super.disconnectedCallback(); - } - - handleUpdate(event: CustomEvent) { - delete event.detail.target; - this.wizardState = event.detail; - } - - render() { - return html``; - } -} - -export default AkApplicationWizardContext; diff --git a/web/src/admin/applications/wizard/ak-application-wizard.ts b/web/src/admin/applications/wizard/ak-application-wizard.ts index b2ac9575a..d5c3beb26 100644 --- a/web/src/admin/applications/wizard/ak-application-wizard.ts +++ b/web/src/admin/applications/wizard/ak-application-wizard.ts @@ -11,12 +11,15 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; -import { steps } from "./ApplicationWizardSteps"; import applicationWizardContext from "./ak-application-wizard-context-name"; +import { steps } from "./steps"; import { WizardState, WizardStateEvent } from "./types"; // my-context.ts +// All this thing is doing is recording the input the user makes to the forms. It should NOT be +// triggering re-renders; that's the wizard frame's jobs. + @customElement("ak-application-wizard") export class ApplicationWizard extends CustomListenerElement(AKElement) { static get styles(): CSSResult[] { @@ -38,19 +41,8 @@ export class ApplicationWizard extends CustomListenerElement(AKElement) { @state() steps = steps; - @property({ type: Boolean }) - open = false; - @property() - createText = msg("Create"); - - @property({ type: Boolean }) - showButton = true; - - @property({ attribute: false }) - finalHandler: () => Promise = () => { - return Promise.resolve(); - }; + prompt = msg("Create"); constructor() { super(); @@ -83,8 +75,6 @@ export class ApplicationWizard extends CustomListenerElement(AKElement) { method.disabled = false; this.steps = newSteps; } - - console.log(newWizardState); this.wizardState = newWizardState; } @@ -94,12 +84,8 @@ export class ApplicationWizard extends CustomListenerElement(AKElement) { .steps=${this.steps} header=${msg("New application")} description=${msg("Create a new application.")} + prompt=${this.prompt} > - ${this.showButton - ? html`` - : html``} `; } diff --git a/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts b/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts index 27e1346b9..4ecdba6c1 100644 --- a/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts +++ b/web/src/admin/applications/wizard/application/ak-application-wizard-application-details.ts @@ -12,10 +12,10 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.j import { TemplateResult, html } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; -import ApplicationWizardPageBase from "../ApplicationWizardPageBase"; +import BasePanel from "../BasePanel"; @customElement("ak-application-wizard-application-details") -export class ApplicationWizardApplicationDetails extends ApplicationWizardPageBase { +export class ApplicationWizardApplicationDetails extends BasePanel { handleChange(ev: Event) { if (!ev.target) { console.warn(`Received event with no target: ${ev}`); diff --git a/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts b/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts index 65c3cce6a..5b64efacb 100644 --- a/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts +++ b/web/src/admin/applications/wizard/auth-method-choice/ak-application-wizard-authentication-method-choice.ts @@ -12,11 +12,11 @@ import { map } from "lit/directives/map.js"; import type { TypeCreate } from "@goauthentik/api"; -import ApplicationWizardPageBase from "../ApplicationWizardPageBase"; +import BasePanel from "../BasePanel"; import providerTypesList from "./ak-application-wizard-authentication-method-choice.choices"; @customElement("ak-application-wizard-authentication-method-choice") -export class ApplicationWizardAuthenticationMethodChoice extends ApplicationWizardPageBase { +export class ApplicationWizardAuthenticationMethodChoice extends BasePanel { constructor() { super(); this.handleChoice = this.handleChoice.bind(this); diff --git a/web/src/admin/applications/wizard/design.md b/web/src/admin/applications/wizard/design.md deleted file mode 100644 index 17d44e025..000000000 --- a/web/src/admin/applications/wizard/design.md +++ /dev/null @@ -1,20 +0,0 @@ -The design of the wizard is actually very simple. There is an orchestrator in the Context object; -it takes messages from the current page and grants permissions to proceed based on the content of -the Context object after a message. - -The fields of the Context object are: - -```Javascript -{ - step: number // The page currently being visited - providerType: The provider type chosen in step 2. Dictates which view to show in step 3 - application: // The data collected from the ApplicationDetails page - provider: // the data collected from the ProviderDetails page. - - -``` - -The orchestrator leans on the per-page forms to tell it when a page is "valid enough to proceed". - -When it reaches the last page, the transaction is triggered. If there are errors, the user is -invited to "go back to the page where the error occurred" and try again. diff --git a/web/src/admin/applications/wizard/link/TypeLinkApplicationWizardPage.ts b/web/src/admin/applications/wizard/link/TypeLinkApplicationWizardPage.ts deleted file mode 100644 index c91133a45..000000000 --- a/web/src/admin/applications/wizard/link/TypeLinkApplicationWizardPage.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -@customElement("ak-application-wizard-type-link") -export class TypeLinkApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Application Link"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - this.host.state["link"] = data.link; - return true; - }; - - renderForm(): TemplateResult { - return html` -
- - -

- ${msg("URL which will be opened when a user clicks on the application.")} -

-
-
- `; - } -} diff --git a/web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts b/web/src/admin/applications/wizard/methods/BaseProviderPanel.ts similarity index 77% rename from web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts rename to web/src/admin/applications/wizard/methods/BaseProviderPanel.ts index b5db2e215..2558a3b6d 100644 --- a/web/src/admin/applications/wizard/ApplicationWizardProviderPageBase.ts +++ b/web/src/admin/applications/wizard/methods/BaseProviderPanel.ts @@ -1,6 +1,6 @@ -import ApplicationWizardPageBase from "./ApplicationWizardPageBase"; +import BasePanel from "../BasePanel"; -export class ApplicationWizardProviderPageBase extends ApplicationWizardPageBase { +export class ApplicationWizardProviderPageBase extends BasePanel { handleChange(ev: InputEvent) { if (!ev.target) { console.warn(`Received event with no target: ${ev}`); diff --git a/web/src/admin/applications/wizard/auth-method/ak-application-wizard-authentication-method.ts b/web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts similarity index 59% rename from web/src/admin/applications/wizard/auth-method/ak-application-wizard-authentication-method.ts rename to web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts index ab727ba07..b63741326 100644 --- a/web/src/admin/applications/wizard/auth-method/ak-application-wizard-authentication-method.ts +++ b/web/src/admin/applications/wizard/methods/ak-application-wizard-authentication-method.ts @@ -1,16 +1,16 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import ApplicationWizardPageBase from "../ApplicationWizardPageBase"; +import BasePanel from "../BasePanel"; import { providerRendererList } from "../auth-method-choice/ak-application-wizard-authentication-method-choice.choices"; -import "../ldap/ak-application-wizard-authentication-by-ldap"; -import "../oauth/ak-application-wizard-authentication-by-oauth"; -import "../proxy/ak-application-wizard-authentication-for-reverse-proxy"; -import "../proxy/ak-application-wizard-authentication-for-single-forward-proxy"; +import "./ldap/ak-application-wizard-authentication-by-ldap"; +import "./oauth/ak-application-wizard-authentication-by-oauth"; +import "./proxy/ak-application-wizard-authentication-for-reverse-proxy"; +import "./proxy/ak-application-wizard-authentication-for-single-forward-proxy"; // prettier-ignore @customElement("ak-application-wizard-authentication-method") -export class ApplicationWizardApplicationDetails extends ApplicationWizardPageBase { +export class ApplicationWizardApplicationDetails extends BasePanel { render() { const handler = providerRendererList.get(this.wizard.providerType); if (!handler) { diff --git a/web/src/admin/applications/wizard/ldap/LDAPOptionsAndHelp.ts b/web/src/admin/applications/wizard/methods/ldap/LDAPOptionsAndHelp.ts similarity index 100% rename from web/src/admin/applications/wizard/ldap/LDAPOptionsAndHelp.ts rename to web/src/admin/applications/wizard/methods/ldap/LDAPOptionsAndHelp.ts diff --git a/web/src/admin/applications/wizard/ldap/ak-application-wizard-authentication-by-ldap.ts b/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts similarity index 97% rename from web/src/admin/applications/wizard/ldap/ak-application-wizard-authentication-by-ldap.ts rename to web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts index c9e0d8d0a..7b9aa4d3f 100644 --- a/web/src/admin/applications/wizard/ldap/ak-application-wizard-authentication-by-ldap.ts +++ b/web/src/admin/applications/wizard/methods/ldap/ak-application-wizard-authentication-by-ldap.ts @@ -18,7 +18,7 @@ import { ifDefined } from "lit/directives/if-defined.js"; import { FlowsInstancesListDesignationEnum } from "@goauthentik/api"; import type { LDAPProvider } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; import { bindModeOptions, cryptoCertificateHelp, @@ -31,7 +31,7 @@ import { } from "./LDAPOptionsAndHelp"; @customElement("ak-application-wizard-authentication-by-ldap") -export class ApplicationWizardApplicationDetails extends ApplicationWizardProviderPageBase { +export class ApplicationWizardApplicationDetails extends BaseProviderPanel { render() { const provider = this.wizard.provider as LDAPProvider | undefined; diff --git a/web/src/admin/applications/wizard/oauth/ak-application-wizard-authentication-by-oauth.ts b/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts similarity index 98% rename from web/src/admin/applications/wizard/oauth/ak-application-wizard-authentication-by-oauth.ts rename to web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts index 02bd54ac5..cf6ceb9de 100644 --- a/web/src/admin/applications/wizard/oauth/ak-application-wizard-authentication-by-oauth.ts +++ b/web/src/admin/applications/wizard/methods/oauth/ak-application-wizard-authentication-by-oauth.ts @@ -33,10 +33,10 @@ import type { PaginatedScopeMappingList, } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; @customElement("ak-application-wizard-authentication-by-oauth") -export class ApplicationWizardAuthenticationByOauth extends ApplicationWizardProviderPageBase { +export class ApplicationWizardAuthenticationByOauth extends BaseProviderPanel { @state() showClientSecret = false; diff --git a/web/src/admin/applications/wizard/proxy/AuthenticationByProxyPage.ts b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts similarity index 98% rename from web/src/admin/applications/wizard/proxy/AuthenticationByProxyPage.ts rename to web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts index 1f7ecdf2e..c3ac7f44f 100644 --- a/web/src/admin/applications/wizard/proxy/AuthenticationByProxyPage.ts +++ b/web/src/admin/applications/wizard/methods/proxy/AuthenticationByProxyPage.ts @@ -21,11 +21,11 @@ import { SourcesApi, } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; type MaybeTemplateResult = TemplateResult | typeof nothing; -export class AkTypeProxyApplicationWizardPage extends ApplicationWizardProviderPageBase { +export class AkTypeProxyApplicationWizardPage extends BaseProviderPanel { constructor() { super(); new PropertymappingsApi(DEFAULT_CONFIG) diff --git a/web/src/admin/applications/wizard/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts b/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts similarity index 100% rename from web/src/admin/applications/wizard/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts rename to web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-reverse-proxy.ts diff --git a/web/src/admin/applications/wizard/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts b/web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts similarity index 100% rename from web/src/admin/applications/wizard/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts rename to web/src/admin/applications/wizard/methods/proxy/ak-application-wizard-authentication-for-single-forward-proxy.ts diff --git a/web/src/admin/applications/wizard/saml/SamlProviderOptions.ts b/web/src/admin/applications/wizard/methods/saml/SamlProviderOptions.ts similarity index 100% rename from web/src/admin/applications/wizard/saml/SamlProviderOptions.ts rename to web/src/admin/applications/wizard/methods/saml/SamlProviderOptions.ts diff --git a/web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-configuration.ts b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts similarity index 98% rename from web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-configuration.ts rename to web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts index 2313749c3..f25c995ef 100644 --- a/web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-configuration.ts +++ b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-configuration.ts @@ -21,7 +21,7 @@ import { SAMLProvider, } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; import { digestAlgorithmOptions, signatureAlgorithmOptions, @@ -29,7 +29,7 @@ import { } from "./SamlProviderOptions"; @customElement("ak-application-wizard-authentication-by-saml-configuration") -export class ApplicationWizardProviderSamlConfiguration extends ApplicationWizardProviderPageBase { +export class ApplicationWizardProviderSamlConfiguration extends BaseProviderPanel { propertyMappings?: PaginatedSAMLPropertyMappingList; constructor() { diff --git a/web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-import.ts b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts similarity index 89% rename from web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-import.ts rename to web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts index 49d03ef7f..dda096a67 100644 --- a/web/src/admin/applications/wizard/saml/ak-application-wizard-authentication-by-saml-import.ts +++ b/web/src/admin/applications/wizard/methods/saml/ak-application-wizard-authentication-by-saml-import.ts @@ -9,10 +9,10 @@ import { html } from "lit"; import { FlowsInstancesListDesignationEnum } from "@goauthentik/api"; -import ApplicationWizardProviderPageBase from "../ApplicationWizardProviderPageBase"; +import BaseProviderPanel from "../BaseProviderPanel"; @customElement("ak-application-wizard-authentication-by-saml-import") -export class ApplicationWizardProviderSamlImport extends ApplicationWizardProviderPageBase { +export class ApplicationWizardProviderSamlImport extends BaseProviderPanel { render() { return html`
diff --git a/web/src/admin/applications/wizard/saml/saml-property-mappings-search.ts b/web/src/admin/applications/wizard/methods/saml/saml-property-mappings-search.ts similarity index 100% rename from web/src/admin/applications/wizard/saml/saml-property-mappings-search.ts rename to web/src/admin/applications/wizard/methods/saml/saml-property-mappings-search.ts diff --git a/web/src/admin/applications/wizard/oauth/TypeOAuthAPIApplicationWizardPage.ts b/web/src/admin/applications/wizard/oauth/TypeOAuthAPIApplicationWizardPage.ts deleted file mode 100644 index a2e08c2aa..000000000 --- a/web/src/admin/applications/wizard/oauth/TypeOAuthAPIApplicationWizardPage.ts +++ /dev/null @@ -1,35 +0,0 @@ -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardPage } from "@goauthentik/elements/wizard/WizardPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { CSSResult, TemplateResult, html } from "lit"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -@customElement("ak-application-wizard-type-oauth-api") -export class TypeOAuthAPIApplicationWizardPage extends WizardPage { - static get styles(): CSSResult[] { - return [PFBase, PFButton, PFForm, PFRadio]; - } - - sidebarLabel = () => msg("Method details"); - - render(): TemplateResult { - return html` -

- ${msg( - "This configuration can be used to authenticate to authentik with other APIs other otherwise programmatically.", - )} -

-

- ${msg( - "By default, all service accounts can authenticate as this application, as long as they have a valid token of the type app-password.", - )} -

-
`; - } -} diff --git a/web/src/admin/applications/wizard/oauth/TypeOAuthApplicationWizardPage.ts b/web/src/admin/applications/wizard/oauth/TypeOAuthApplicationWizardPage.ts deleted file mode 100644 index bb19272e9..000000000 --- a/web/src/admin/applications/wizard/oauth/TypeOAuthApplicationWizardPage.ts +++ /dev/null @@ -1,84 +0,0 @@ -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardPage } from "@goauthentik/elements/wizard/WizardPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { CSSResult, TemplateResult, html } from "lit"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -import { TypeCreate } from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-oauth") -export class TypeOAuthApplicationWizardPage extends WizardPage { - applicationTypes: TypeCreate[] = [ - { - component: "ak-application-wizard-type-oauth-code", - name: msg("Web application"), - description: msg( - "Applications which handle the authentication server-side (for example, Python, Go, Rust, Java, PHP)", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-oauth-implicit", - name: msg("Single-page applications"), - description: msg( - "Single-page applications which handle authentication in the browser (for example, Javascript, Angular, React, Vue)", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-oauth-implicit", - name: msg("Native application"), - description: msg( - "Applications which redirect users to a non-web callback (for example, Android, iOS)", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-oauth-api", - name: msg("API"), - description: msg( - "Authentication without user interaction, or machine-to-machine authentication.", - ), - modelName: "", - }, - ]; - - static get styles(): CSSResult[] { - return [PFBase, PFButton, PFForm, PFRadio]; - } - - sidebarLabel = () => msg("Application type"); - - render(): TemplateResult { - return html`
- ${this.applicationTypes.map((type) => { - return html`
- { - this.host.steps = [ - "ak-application-wizard-initial", - "ak-application-wizard-type", - "ak-application-wizard-type-oauth", - type.component, - ]; - this.host.state["oauth-type"] = type.component; - this.host.isValid = true; - }} - /> - - ${type.description} -
`; - })} -
`; - } -} diff --git a/web/src/admin/applications/wizard/oauth/TypeOAuthCodeApplicationWizardPage.ts b/web/src/admin/applications/wizard/oauth/TypeOAuthCodeApplicationWizardPage.ts deleted file mode 100644 index 458def24b..000000000 --- a/web/src/admin/applications/wizard/oauth/TypeOAuthCodeApplicationWizardPage.ts +++ /dev/null @@ -1,57 +0,0 @@ -import "@goauthentik/admin/common/ak-flow-search/ak-flow-search-no-default"; -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import "@goauthentik/elements/forms/SearchSelect"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; -import "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -import { - ClientTypeEnum, - FlowsInstancesListDesignationEnum, - OAuth2ProviderRequest, - ProvidersApi, -} from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-oauth-code") -export class TypeOAuthCodeApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Method details"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - this.host.addActionBefore(msg("Create provider"), async (): Promise => { - const req: OAuth2ProviderRequest = { - name: this.host.state["name"] as string, - clientType: ClientTypeEnum.Confidential, - authorizationFlow: data.authorizationFlow as string, - }; - const provider = await new ProvidersApi(DEFAULT_CONFIG).providersOauth2Create({ - oAuth2ProviderRequest: req, - }); - this.host.state["provider"] = provider; - return true; - }); - return true; - }; - - renderForm(): TemplateResult { - return html`
- - -

- ${msg("Flow used when users access this application.")} -

-
-
`; - } -} diff --git a/web/src/admin/applications/wizard/oauth/TypeOAuthImplicitApplicationWizardPage.ts b/web/src/admin/applications/wizard/oauth/TypeOAuthImplicitApplicationWizardPage.ts deleted file mode 100644 index c1fa0f3cf..000000000 --- a/web/src/admin/applications/wizard/oauth/TypeOAuthImplicitApplicationWizardPage.ts +++ /dev/null @@ -1,15 +0,0 @@ -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -@customElement("ak-application-wizard-type-oauth-implicit") -export class TypeOAuthImplicitApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Method details"); - - render(): TemplateResult { - return html`
some stuff idk
`; - } -} diff --git a/web/src/admin/applications/wizard/proxy/TypeProxyApplicationWizardPage.ts b/web/src/admin/applications/wizard/proxy/TypeProxyApplicationWizardPage.ts deleted file mode 100644 index 43db7c56c..000000000 --- a/web/src/admin/applications/wizard/proxy/TypeProxyApplicationWizardPage.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -import { - FlowDesignationEnum, - FlowsApi, - ProvidersApi, - ProxyProviderRequest, -} from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-proxy") -export class TypeProxyApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Proxy details"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - let name = this.host.state["name"] as string; - // Check if a provider with the name already exists - const providers = await new ProvidersApi(DEFAULT_CONFIG).providersAllList({ - search: name, - }); - if (providers.results.filter((provider) => provider.name == name)) { - name += "-1"; - } - this.host.addActionBefore(msg("Create provider"), async (): Promise => { - // Get all flows and default to the implicit authorization - const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ - designation: FlowDesignationEnum.Authorization, - ordering: "slug", - }); - const req: ProxyProviderRequest = { - name: name, - authorizationFlow: flows.results[0].pk, - externalHost: data.externalHost as string, - }; - const provider = await new ProvidersApi(DEFAULT_CONFIG).providersProxyCreate({ - proxyProviderRequest: req, - }); - this.host.state["provider"] = provider; - return true; - }); - return true; - }; - - renderForm(): TemplateResult { - return html`
- - -

- ${msg("External domain you will be accessing the domain from.")} -

-
-
`; - } -} diff --git a/web/src/admin/applications/wizard/saml/TypeSAMLApplicationWizardPage.ts b/web/src/admin/applications/wizard/saml/TypeSAMLApplicationWizardPage.ts deleted file mode 100644 index 2ef4d6972..000000000 --- a/web/src/admin/applications/wizard/saml/TypeSAMLApplicationWizardPage.ts +++ /dev/null @@ -1,66 +0,0 @@ -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardPage } from "@goauthentik/elements/wizard/WizardPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { CSSResult, TemplateResult, html } from "lit"; - -import PFButton from "@patternfly/patternfly/components/Button/button.css"; -import PFForm from "@patternfly/patternfly/components/Form/form.css"; -import PFRadio from "@patternfly/patternfly/components/Radio/radio.css"; -import PFBase from "@patternfly/patternfly/patternfly-base.css"; - -import { TypeCreate } from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-saml") -export class TypeOAuthApplicationWizardPage extends WizardPage { - applicationTypes: TypeCreate[] = [ - { - component: "ak-application-wizard-type-saml-import", - name: msg("Import SAML Metadata"), - description: msg( - "Import the metadata document of the applicaation you want to configure.", - ), - modelName: "", - }, - { - component: "ak-application-wizard-type-saml-config", - name: msg("Manual configuration"), - description: msg("Manually configure SAML"), - modelName: "", - }, - ]; - - static get styles(): CSSResult[] { - return [PFBase, PFButton, PFForm, PFRadio]; - } - - sidebarLabel = () => msg("Application type"); - - render(): TemplateResult { - return html`
- ${this.applicationTypes.map((type) => { - return html`
- { - this.host.steps = [ - "ak-application-wizard-initial", - "ak-application-wizard-type", - "ak-application-wizard-type-saml", - type.component, - ]; - this.host.state["saml-type"] = type.component; - this.host.isValid = true; - }} - /> - - ${type.description} -
`; - })} -
`; - } -} diff --git a/web/src/admin/applications/wizard/saml/TypeSAMLConfigApplicationWizardPage.ts b/web/src/admin/applications/wizard/saml/TypeSAMLConfigApplicationWizardPage.ts deleted file mode 100644 index ad269ac63..000000000 --- a/web/src/admin/applications/wizard/saml/TypeSAMLConfigApplicationWizardPage.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -import { FlowDesignationEnum, FlowsApi, ProvidersApi, SAMLProviderRequest } from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-saml-config") -export class TypeSAMLApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("SAML details"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - let name = this.host.state["name"] as string; - // Check if a provider with the name already exists - const providers = await new ProvidersApi(DEFAULT_CONFIG).providersAllList({ - search: name, - }); - if (providers.results.filter((provider) => provider.name == name)) { - name += "-1"; - } - this.host.addActionBefore(msg("Create provider"), async (): Promise => { - // Get all flows and default to the implicit authorization - const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ - designation: FlowDesignationEnum.Authorization, - ordering: "slug", - }); - const req: SAMLProviderRequest = { - name: name, - authorizationFlow: flows.results[0].pk, - acsUrl: data.acsUrl as string, - }; - const provider = await new ProvidersApi(DEFAULT_CONFIG).providersSamlCreate({ - sAMLProviderRequest: req, - }); - this.host.state["provider"] = provider; - return true; - }); - return true; - }; - - renderForm(): TemplateResult { - return html`
- - -

- ${msg( - "URL that authentik will redirect back to after successful authentication.", - )} -

-
-
`; - } -} diff --git a/web/src/admin/applications/wizard/saml/TypeSAMLImportApplicationWizardPage.ts b/web/src/admin/applications/wizard/saml/TypeSAMLImportApplicationWizardPage.ts deleted file mode 100644 index c3ddfda79..000000000 --- a/web/src/admin/applications/wizard/saml/TypeSAMLImportApplicationWizardPage.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { KeyUnknown } from "@goauthentik/elements/forms/Form"; -import "@goauthentik/elements/forms/HorizontalFormElement"; -import { WizardFormPage } from "@goauthentik/elements/wizard/WizardFormPage"; - -import { msg } from "@lit/localize"; -import { customElement } from "@lit/reactive-element/decorators/custom-element.js"; -import { TemplateResult, html } from "lit"; - -import { - FlowDesignationEnum, - FlowsApi, - ProvidersApi, - ProvidersSamlImportMetadataCreateRequest, -} from "@goauthentik/api"; - -@customElement("ak-application-wizard-type-saml-import") -export class TypeSAMLImportApplicationWizardPage extends WizardFormPage { - sidebarLabel = () => msg("Import SAML metadata"); - - nextDataCallback = async (data: KeyUnknown): Promise => { - let name = this.host.state["name"] as string; - // Check if a provider with the name already exists - const providers = await new ProvidersApi(DEFAULT_CONFIG).providersAllList({ - search: name, - }); - if (providers.results.filter((provider) => provider.name == name)) { - name += "-1"; - } - this.host.addActionBefore(msg("Create provider"), async (): Promise => { - // Get all flows and default to the implicit authorization - const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({ - designation: FlowDesignationEnum.Authorization, - ordering: "slug", - }); - const req: ProvidersSamlImportMetadataCreateRequest = { - name: name, - authorizationFlow: flows.results[0].slug, - file: data["metadata"] as Blob, - }; - const provider = await new ProvidersApi( - DEFAULT_CONFIG, - ).providersSamlImportMetadataCreate(req); - this.host.state["provider"] = provider; - return true; - }); - return true; - }; - - renderForm(): TemplateResult { - return html`
- - - -
`; - } -} diff --git a/web/src/admin/applications/wizard/ApplicationWizardSteps.ts b/web/src/admin/applications/wizard/steps.ts similarity index 70% rename from web/src/admin/applications/wizard/ApplicationWizardSteps.ts rename to web/src/admin/applications/wizard/steps.ts index 2c720165b..1b7a5a072 100644 --- a/web/src/admin/applications/wizard/ApplicationWizardSteps.ts +++ b/web/src/admin/applications/wizard/steps.ts @@ -1,16 +1,15 @@ -import { WizardStep, makeWizardId } from "@goauthentik/components/ak-wizard-main"; +import { WizardStep } from "@goauthentik/components/ak-wizard-main"; import { msg } from "@lit/localize"; import { html } from "lit"; import "./application/ak-application-wizard-application-details"; import "./auth-method-choice/ak-application-wizard-authentication-method-choice"; -import "./auth-method/ak-application-wizard-authentication-method"; +import "./methods/ak-application-wizard-authentication-method"; export const steps: WizardStep[] = [ { - id: makeWizardId("application"), - nextStep: makeWizardId("auth-method-choice"), + id: "application", label: "Application Details", renderer: () => html``, @@ -19,9 +18,7 @@ export const steps: WizardStep[] = [ valid: true, }, { - id: makeWizardId("auth-method-choice"), - backStep: makeWizardId("application"), - nextStep: makeWizardId("auth-method"), + id: "auth-method-choice", label: "Authentication Method", renderer: () => html``, @@ -31,8 +28,7 @@ export const steps: WizardStep[] = [ valid: true, }, { - id: makeWizardId("auth-method"), - backStep: makeWizardId("auth-method-choice"), + id: "auth-method", label: "Authentication Details", renderer: () => html``, diff --git a/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts b/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts index 3f8e0ce26..6abd7563e 100644 --- a/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts +++ b/web/src/admin/applications/wizard/stories/ak-application-context-display-for-test.ts @@ -3,8 +3,8 @@ import { customElement } from "@lit/reactive-element/decorators/custom-element.j import { state } from "@lit/reactive-element/decorators/state.js"; import { LitElement, html } from "lit"; -import type { WizardState } from "../ak-application-wizard-context"; import applicationWizardContext from "../ak-application-wizard-context-name"; +import type { WizardState } from "../types"; @customElement("ak-application-context-display-for-test") export class ApplicationContextDisplayForTest extends LitElement { diff --git a/web/src/components/ak-wizard-main/ak-wizard-frame.ts b/web/src/components/ak-wizard-main/ak-wizard-frame.ts index 6368a9010..712e9c777 100644 --- a/web/src/components/ak-wizard-main/ak-wizard-frame.ts +++ b/web/src/components/ak-wizard-main/ak-wizard-frame.ts @@ -63,6 +63,20 @@ export class AkWizardFrame extends CustomEmitterElement(ModalButton) { this.open = false; } + get maxStep() { + return this.steps.length - 1; + } + + get nextStep() { + const idx = this.steps.findIndex((step) => step === this.currentStep); + return idx < this.maxStep ? this.steps[idx + 1] : undefined; + } + + get backStep() { + const idx = this.steps.findIndex((step) => step === this.currentStep); + return idx > 0 ? this.steps[idx - 1] : undefined; + } + renderModalInner() { // prettier-ignore return html`
@@ -134,32 +148,30 @@ export class AkWizardFrame extends CustomEmitterElement(ModalButton) { renderFooter() { return html`
- ${this.currentStep.nextStep ? this.renderFooterNextButton() : nothing} - ${this.currentStep.backStep ? this.renderFooterBackButton() : nothing} + ${this.nextStep ? this.renderFooterNextButton(this.nextStep) : nothing} + ${this.backStep ? this.renderFooterBackButton(this.backStep) : nothing} ${this.canCancel ? this.renderFooterCancelButton() : nothing}
`; } - renderFooterNextButton() { + renderFooterNextButton(nextStep: WizardStep) { return html``; } - renderFooterBackButton() { + renderFooterBackButton(backStep: WizardStep) { return html` diff --git a/web/src/components/ak-wizard-main/ak-wizard-main.ts b/web/src/components/ak-wizard-main/ak-wizard-main.ts index 80f1c15d0..f4be5734a 100644 --- a/web/src/components/ak-wizard-main/ak-wizard-main.ts +++ b/web/src/components/ak-wizard-main/ak-wizard-main.ts @@ -20,8 +20,11 @@ import type { WizardStep } from "./types"; * * @element ak-wizard-main * - * This is the entry point for the wizard. - * + * This is the entry point for the wizard. Its tasks are: + * - keep the collection of steps + * - maintain the open/close status of the modal + * - listens for navigation events + * - if a navigation event is valid, switch to the panel requested */ @customElement("ak-wizard-main") diff --git a/web/src/components/ak-wizard-main/index.ts b/web/src/components/ak-wizard-main/index.ts index 30bb39ff1..572b3befe 100644 --- a/web/src/components/ak-wizard-main/index.ts +++ b/web/src/components/ak-wizard-main/index.ts @@ -1,5 +1,4 @@ import "./ak-wizard-main"; -import type { WizardStep, WizardStepId } from "./types"; -import { makeWizardId } from "./types"; +import type { WizardStep } from "./types"; -export { WizardStepId, WizardStep, makeWizardId }; +export { WizardStep }; diff --git a/web/src/components/ak-wizard-main/stories/ak-wizard-main.stories.ts b/web/src/components/ak-wizard-main/stories/ak-wizard-main.stories.ts index 235f55faf..e35f9de5e 100644 --- a/web/src/components/ak-wizard-main/stories/ak-wizard-main.stories.ts +++ b/web/src/components/ak-wizard-main/stories/ak-wizard-main.stories.ts @@ -6,7 +6,6 @@ import { TemplateResult, html } from "lit"; import "../ak-wizard-main"; import AkWizard from "../ak-wizard-main"; import type { WizardStep } from "../types"; -import { makeWizardId } from "../types"; const metadata: Meta = { title: "Components / Wizard / Basic", @@ -38,22 +37,20 @@ const container = (testItem: TemplateResult) => const dummySteps: WizardStep[] = [ { - id: makeWizardId("0"), + id: "0", label: "Test Step1", renderer: () => html`

This space intentionally left blank today

`, disabled: false, valid: true, - nextStep: makeWizardId("1"), nextButtonLabel: "Next", backButtonLabel: undefined, }, { - id: makeWizardId("1"), + id: "1", label: "Test Step 2", renderer: () => html`

This space also intentionally left blank

`, disabled: false, valid: true, - backStep: makeWizardId("0"), nextButtonLabel: undefined, backButtonLabel: "Back", }, diff --git a/web/src/components/ak-wizard-main/types.ts b/web/src/components/ak-wizard-main/types.ts index a1911fb0d..f295a77f4 100644 --- a/web/src/components/ak-wizard-main/types.ts +++ b/web/src/components/ak-wizard-main/types.ts @@ -1,15 +1,7 @@ import { TemplateResult } from "lit"; -type PhantomType = { _type: Type } & Data; - -export type WizardStepId = PhantomType<"WizardId", string>; - -export const makeWizardId = (id: string): WizardStepId => id as WizardStepId; - export interface WizardStep { - id: WizardStepId; - nextStep?: WizardStepId; - backStep?: WizardStepId; + id: string; label: string; valid: boolean; renderer: () => TemplateResult;