From 73751e5cd950e719655a61b538b88c61a9a70856 Mon Sep 17 00:00:00 2001 From: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:24:48 -0800 Subject: [PATCH] web: refactor status label to separate component (#7407) * web: break circular dependency between AKElement & Interface. This commit changes the way the root node of the web application shell is discovered by child components, such that the base class shared by both no longer results in a circular dependency between the two models. I've run this in isolation and have seen no failures of discovery; the identity token exists as soon as the Interface is constructed and is found by every item on the page. * web: fix broken typescript references This built... and then it didn't? Anyway, the current fix is to provide type information the AkInterface for the data that consumers require. * A quality of life thing: `` There's an idiom throughout the UI: ``` HTML ${item.enabled ? msg("Yes") : msg("No")} ``` There are two problems with this. - Repeating the conditional multiple times is error-prone - The color scheme doesn't communicate much. There are uses for ak-label that aren't like this, but I'm focusing on this particular use case, which occurs about 20 times throughout the UI. Since it's so common, let's isolate the most common case: `` gives you the "good" status, and `` gives you the "bad" status, which is the default (no arguments to the function). There wasn't much clarity in the system for when to use orange vs red vs grey, but looking through the use cases, it became clear that Red meant fail/inaccessible, Orange meant "Warning, but not blocking," and Grey just means "info: this thing is off". So let's define that with meaning: there are three types, error, warning, and info. Which corresponds to debugging levels, but whatever, nerds grok that stuff. So that example at the top becomes `````` ... and we can now more clearly understand what that conveys. There is some heavy tension in this case: this is an easier and quicker-to-write solution to informing the user of a binary status in an iconic way, but the developer has to remember that it exists. Story provided, and changes to the existing uses of the existing idiom provided. * Added the 'compact label' story to storybook. --- .../ApplicationCheckAccessForm.ts | 6 +- web/src/admin/blueprints/BlueprintListPage.ts | 6 +- .../crypto/CertificateKeyPairListPage.ts | 12 +- web/src/admin/flows/FlowImportForm.ts | 6 +- web/src/admin/groups/GroupListPage.ts | 8 +- web/src/admin/groups/GroupViewPage.ts | 11 +- web/src/admin/groups/MemberSelectModal.ts | 6 +- web/src/admin/groups/RelatedGroupList.ts | 6 +- web/src/admin/groups/RelatedUserList.ts | 6 +- .../outposts/ServiceConnectionListPage.ts | 5 +- web/src/admin/policies/BoundPoliciesList.ts | 6 +- web/src/admin/policies/PolicyTestForm.ts | 6 +- .../providers/proxy/ProxyProviderViewPage.ts | 15 +-- web/src/admin/tenants/TenantListPage.ts | 7 +- web/src/admin/tokens/TokenListPage.ts | 6 +- web/src/admin/users/GroupSelectModal.ts | 6 +- web/src/admin/users/UserListPage.ts | 6 +- web/src/admin/users/UserViewPage.ts | 16 +-- web/src/components/ak-status-label.ts | 116 ++++++++++++++++++ .../stories/ak-status-label.stories.ts | 101 +++++++++++++++ web/src/elements/oauth/UserRefreshList.ts | 6 +- .../user-settings/tokens/UserTokenList.ts | 6 +- 22 files changed, 277 insertions(+), 92 deletions(-) create mode 100644 web/src/components/ak-status-label.ts create mode 100644 web/src/components/stories/ak-status-label.stories.ts diff --git a/web/src/admin/applications/ApplicationCheckAccessForm.ts b/web/src/admin/applications/ApplicationCheckAccessForm.ts index 59292e17d..7f1c18e17 100644 --- a/web/src/admin/applications/ApplicationCheckAccessForm.ts +++ b/web/src/admin/applications/ApplicationCheckAccessForm.ts @@ -1,5 +1,5 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import { Form } from "@goauthentik/elements/forms/Form"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/SearchSelect"; @@ -57,9 +57,7 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
- - ${this.result?.passing ? msg("Yes") : msg("No")} - +
diff --git a/web/src/admin/blueprints/BlueprintListPage.ts b/web/src/admin/blueprints/BlueprintListPage.ts index f038cc957..188bb4be5 100644 --- a/web/src/admin/blueprints/BlueprintListPage.ts +++ b/web/src/admin/blueprints/BlueprintListPage.ts @@ -2,7 +2,7 @@ import "@goauthentik/admin/blueprints/BlueprintForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { uiConfig } from "@goauthentik/common/ui/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; @@ -144,9 +144,7 @@ export class BlueprintListPage extends TablePage { ${description ? html`${description}` : html``}`, html`${BlueprintStatus(item)}`, html`${item.lastApplied.toLocaleString()}`, - html` - ${item.enabled ? msg("Yes") : msg("No")} - `, + html``, html` ${msg("Update")} ${msg("Update Blueprint")} diff --git a/web/src/admin/crypto/CertificateKeyPairListPage.ts b/web/src/admin/crypto/CertificateKeyPairListPage.ts index 990f3446e..c76feff3a 100644 --- a/web/src/admin/crypto/CertificateKeyPairListPage.ts +++ b/web/src/admin/crypto/CertificateKeyPairListPage.ts @@ -2,6 +2,7 @@ import "@goauthentik/admin/crypto/CertificateGenerateForm"; import "@goauthentik/admin/crypto/CertificateKeyPairForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; +import "@goauthentik/components/ak-status-label"; import { PFColor } from "@goauthentik/elements/Label"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; @@ -117,11 +118,12 @@ export class CertificateKeyPairListPage extends TablePage { return [ html`
${item.name}
${item.managed ? html`${managedSubText}` : html``}`, - html` - ${item.privateKeyAvailable - ? msg(str`Yes (${item.privateKeyType?.toUpperCase()})`) - : msg("No")} - `, + html` + `, html` ${item.certExpiry?.toLocaleString()} `, html` ${msg("Update")} diff --git a/web/src/admin/flows/FlowImportForm.ts b/web/src/admin/flows/FlowImportForm.ts index d04f0c8e9..310269ff8 100644 --- a/web/src/admin/flows/FlowImportForm.ts +++ b/web/src/admin/flows/FlowImportForm.ts @@ -1,6 +1,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { SentryIgnoredError } from "@goauthentik/common/errors"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import { Form } from "@goauthentik/elements/forms/Form"; import "@goauthentik/elements/forms/HorizontalFormElement"; @@ -46,9 +46,7 @@ export class FlowImportForm extends Form {
- - ${this.result?.success ? msg("Yes") : msg("No")} - +
diff --git a/web/src/admin/groups/GroupListPage.ts b/web/src/admin/groups/GroupListPage.ts index f634b0378..c35f17f8c 100644 --- a/web/src/admin/groups/GroupListPage.ts +++ b/web/src/admin/groups/GroupListPage.ts @@ -1,7 +1,7 @@ import "@goauthentik/admin/groups/GroupForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; @@ -81,10 +81,8 @@ export class GroupListPage extends TablePage { html`${item.name}`, html`${item.parentName || msg("-")}`, html`${Array.from(item.users || []).length}`, - html` - ${item.isSuperuser ? msg("Yes") : msg("No")} - `, - html` + html``, + html` ${msg("Update")} ${msg("Update Group")} diff --git a/web/src/admin/groups/GroupViewPage.ts b/web/src/admin/groups/GroupViewPage.ts index 3e6cbddcb..a2f704363 100644 --- a/web/src/admin/groups/GroupViewPage.ts +++ b/web/src/admin/groups/GroupViewPage.ts @@ -3,10 +3,10 @@ import "@goauthentik/app/admin/groups/RelatedUserList"; import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/components/events/ObjectChangelog"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/CodeMirror"; -import { PFColor } from "@goauthentik/elements/Label"; import "@goauthentik/elements/PageHeader"; import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/buttons/ActionButton"; @@ -116,11 +116,10 @@ export class GroupViewPage extends AKElement {
- +
diff --git a/web/src/admin/groups/MemberSelectModal.ts b/web/src/admin/groups/MemberSelectModal.ts index 1d5a53cc7..f573644b2 100644 --- a/web/src/admin/groups/MemberSelectModal.ts +++ b/web/src/admin/groups/MemberSelectModal.ts @@ -1,7 +1,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; import { first } from "@goauthentik/common/utils"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/buttons/SpinnerButton"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; @@ -48,9 +48,7 @@ export class MemberSelectTable extends TableModal { return [ html`
${item.username}
${item.name}`, - html` - ${item.isActive ? msg("Yes") : msg("No")} - `, + html` `, html`${first(item.lastLogin?.toLocaleString(), msg("-"))}`, ]; } diff --git a/web/src/admin/groups/RelatedGroupList.ts b/web/src/admin/groups/RelatedGroupList.ts index d0b965648..19adf1276 100644 --- a/web/src/admin/groups/RelatedGroupList.ts +++ b/web/src/admin/groups/RelatedGroupList.ts @@ -3,7 +3,7 @@ import "@goauthentik/admin/groups/GroupForm"; import "@goauthentik/admin/users/GroupSelectModal"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import { Form } from "@goauthentik/elements/forms/Form"; @@ -145,9 +145,7 @@ export class RelatedGroupList extends Table { return [ html`${item.name}`, html`${item.parentName || msg("-")}`, - html` - ${item.isSuperuser ? msg("Yes") : msg("No")} - `, + html``, html` ${msg("Update")} ${msg("Update Group")} diff --git a/web/src/admin/groups/RelatedUserList.ts b/web/src/admin/groups/RelatedUserList.ts index 0023c5515..2474ee2fe 100644 --- a/web/src/admin/groups/RelatedUserList.ts +++ b/web/src/admin/groups/RelatedUserList.ts @@ -8,8 +8,8 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { MessageLevel } from "@goauthentik/common/messages"; import { uiConfig } from "@goauthentik/common/ui/config"; import { first } from "@goauthentik/common/utils"; +import "@goauthentik/components/ak-status-label"; import { rootInterface } from "@goauthentik/elements/Base"; -import { PFColor } from "@goauthentik/elements/Label"; import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/buttons/Dropdown"; import "@goauthentik/elements/forms/DeleteBulkForm"; @@ -195,9 +195,7 @@ export class RelatedUserList extends Table {
${item.username}
${item.name} `, - html` - ${item.isActive ? msg("Yes") : msg("No")} - `, + html``, html`${first(item.lastLogin?.toLocaleString(), msg("-"))}`, html` ${msg("Update")} diff --git a/web/src/admin/outposts/ServiceConnectionListPage.ts b/web/src/admin/outposts/ServiceConnectionListPage.ts index 49ef585a4..a212358f5 100644 --- a/web/src/admin/outposts/ServiceConnectionListPage.ts +++ b/web/src/admin/outposts/ServiceConnectionListPage.ts @@ -4,6 +4,7 @@ import "@goauthentik/admin/outposts/ServiceConnectionKubernetesForm"; import "@goauthentik/admin/outposts/ServiceConnectionWizard"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; +import "@goauthentik/components/ak-status-label"; import { PFColor } from "@goauthentik/elements/Label"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; @@ -83,9 +84,7 @@ export class OutpostServiceConnectionListPage extends TablePage - ${item.local ? msg("Yes") : msg("No")} - `, + html``, html`${itemState?.healthy ? html`${ifDefined(itemState.version)}` : html`${msg("Unhealthy")}`}`, diff --git a/web/src/admin/policies/BoundPoliciesList.ts b/web/src/admin/policies/BoundPoliciesList.ts index b7470a422..c9e29bf67 100644 --- a/web/src/admin/policies/BoundPoliciesList.ts +++ b/web/src/admin/policies/BoundPoliciesList.ts @@ -4,7 +4,7 @@ import "@goauthentik/admin/policies/PolicyWizard"; import "@goauthentik/admin/users/UserForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import { PFSize } from "@goauthentik/elements/Spinner"; import "@goauthentik/elements/Tabs"; import "@goauthentik/elements/forms/DeleteBulkForm"; @@ -147,9 +147,7 @@ export class BoundPoliciesList extends Table { return [ html`
${item.order}
`, html`${this.getPolicyUserGroupRow(item)}`, - html` - ${item.enabled ? msg("Yes") : msg("No")} - `, + html``, html`${item.timeout}`, html` ${this.getObjectEditButton(item)} diff --git a/web/src/admin/policies/PolicyTestForm.ts b/web/src/admin/policies/PolicyTestForm.ts index aa874f37e..50771abbd 100644 --- a/web/src/admin/policies/PolicyTestForm.ts +++ b/web/src/admin/policies/PolicyTestForm.ts @@ -1,8 +1,8 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { first } from "@goauthentik/common/utils"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/CodeMirror"; import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror"; -import { PFColor } from "@goauthentik/elements/Label"; import { Form } from "@goauthentik/elements/forms/Form"; import "@goauthentik/elements/forms/HorizontalFormElement"; import "@goauthentik/elements/forms/SearchSelect"; @@ -58,9 +58,7 @@ export class PolicyTestForm extends Form {
- - ${this.result?.passing ? msg("Yes") : msg("No")} - +
diff --git a/web/src/admin/providers/proxy/ProxyProviderViewPage.ts b/web/src/admin/providers/proxy/ProxyProviderViewPage.ts index a0ce2792d..bdce04194 100644 --- a/web/src/admin/providers/proxy/ProxyProviderViewPage.ts +++ b/web/src/admin/providers/proxy/ProxyProviderViewPage.ts @@ -4,6 +4,7 @@ import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { convertToSlug } from "@goauthentik/common/utils"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/components/events/ObjectChangelog"; import MDCaddyStandalone from "@goauthentik/docs/providers/proxy/_caddy_standalone.md"; import MDNginxIngress from "@goauthentik/docs/providers/proxy/_nginx_ingress.md"; @@ -15,7 +16,6 @@ import MDTraefikStandalone from "@goauthentik/docs/providers/proxy/_traefik_stan import MDHeaderAuthentication from "@goauthentik/docs/providers/proxy/header_authentication.md"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/CodeMirror"; -import { PFColor } from "@goauthentik/elements/Label"; import "@goauthentik/elements/Markdown"; import "@goauthentik/elements/Markdown"; import { Replacer } from "@goauthentik/elements/Markdown"; @@ -330,15 +330,10 @@ export class ProxyProviderViewPage extends AKElement {
- - ${this.provider.basicAuthEnabled - ? msg("Yes") - : msg("No")} - +
diff --git a/web/src/admin/tenants/TenantListPage.ts b/web/src/admin/tenants/TenantListPage.ts index 2edaeb2d4..1e4cd5d8a 100644 --- a/web/src/admin/tenants/TenantListPage.ts +++ b/web/src/admin/tenants/TenantListPage.ts @@ -1,7 +1,8 @@ import "@goauthentik/admin/tenants/TenantForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/buttons/SpinnerButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; import "@goauthentik/elements/forms/ModalForm"; @@ -82,9 +83,7 @@ export class TenantListPage extends TablePage { row(item: Tenant): TemplateResult[] { return [ html`${item.domain}`, - html` - ${item._default ? msg("Yes") : msg("No")} - `, + html``, html` ${msg("Update")} ${msg("Update Tenant")} diff --git a/web/src/admin/tokens/TokenListPage.ts b/web/src/admin/tokens/TokenListPage.ts index ec4d1018d..953cd17dd 100644 --- a/web/src/admin/tokens/TokenListPage.ts +++ b/web/src/admin/tokens/TokenListPage.ts @@ -2,7 +2,7 @@ import "@goauthentik/admin/tokens/TokenForm"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { intentToLabel } from "@goauthentik/common/labels"; import { uiConfig } from "@goauthentik/common/ui/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/buttons/Dropdown"; import "@goauthentik/elements/buttons/TokenCopyButton"; import "@goauthentik/elements/forms/DeleteBulkForm"; @@ -109,9 +109,7 @@ export class TokenListPage extends TablePage { ? html`${msg("Token is managed by authentik.")}` : html``}`, html`${item.userObj?.username}`, - html` - ${item.expiring ? msg("Yes") : msg("No")} - `, + html``, html`${item.expiring ? item.expires?.toLocaleString() : msg("-")}`, html`${intentToLabel(item.intent ?? IntentEnum.Api)}`, html` diff --git a/web/src/admin/users/GroupSelectModal.ts b/web/src/admin/users/GroupSelectModal.ts index 350095c9b..eac99d4ae 100644 --- a/web/src/admin/users/GroupSelectModal.ts +++ b/web/src/admin/users/GroupSelectModal.ts @@ -1,6 +1,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/buttons/SpinnerButton"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { TableColumn } from "@goauthentik/elements/table/Table"; @@ -54,9 +54,7 @@ export class GroupSelectModal extends TableModal { html`
${item.name}
`, - html` - ${item.isSuperuser ? msg("Yes") : msg("No")} - `, + html` `, html`${(item.users || []).length}`, ]; } diff --git a/web/src/admin/users/UserListPage.ts b/web/src/admin/users/UserListPage.ts index 266ebf438..1d861b568 100644 --- a/web/src/admin/users/UserListPage.ts +++ b/web/src/admin/users/UserListPage.ts @@ -10,8 +10,8 @@ import { userTypeToLabel } from "@goauthentik/common/labels"; import { MessageLevel } from "@goauthentik/common/messages"; import { DefaultUIConfig, uiConfig } from "@goauthentik/common/ui/config"; import { first } from "@goauthentik/common/utils"; +import "@goauthentik/components/ak-status-label"; import { rootInterface } from "@goauthentik/elements/Base"; -import { PFColor } from "@goauthentik/elements/Label"; import { PFSize } from "@goauthentik/elements/Spinner"; import "@goauthentik/elements/TreeView"; import "@goauthentik/elements/buttons/ActionButton"; @@ -251,9 +251,7 @@ export class UserListPage extends TablePage {
${item.username}
${item.name === "" ? msg("") : item.name}  ${userTypeToLabel(item.type)}`, - html` - ${item.isActive ? msg("Yes") : msg("No")} - `, + html``, html`${first(item.lastLogin?.toLocaleString(), msg("-"))}`, html` ${msg("Update")} diff --git a/web/src/admin/users/UserViewPage.ts b/web/src/admin/users/UserViewPage.ts index d752f645a..25915b64a 100644 --- a/web/src/admin/users/UserViewPage.ts +++ b/web/src/admin/users/UserViewPage.ts @@ -14,11 +14,11 @@ import "@goauthentik/app/elements/rbac/ObjectPermissionsPage"; import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_REFRESH } from "@goauthentik/common/constants"; import { userTypeToLabel } from "@goauthentik/common/labels"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/components/events/ObjectChangelog"; import "@goauthentik/components/events/UserEvents"; import { AKElement, rootInterface } from "@goauthentik/elements/Base"; import "@goauthentik/elements/CodeMirror"; -import { PFColor } from "@goauthentik/elements/Label"; import "@goauthentik/elements/PageHeader"; import { PFSize } from "@goauthentik/elements/Spinner"; import "@goauthentik/elements/Tabs"; @@ -185,9 +185,10 @@ export class UserViewPage extends AKElement {
- +
@@ -207,9 +208,10 @@ export class UserViewPage extends AKElement {
- +
diff --git a/web/src/components/ak-status-label.ts b/web/src/components/ak-status-label.ts new file mode 100644 index 000000000..f2ff005bf --- /dev/null +++ b/web/src/components/ak-status-label.ts @@ -0,0 +1,116 @@ +import { AKElement } from "@goauthentik/elements/Base"; + +import { msg } from "@lit/localize"; +import { css, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { classMap } from "lit/directives/class-map.js"; + +import PFLabel from "@patternfly/patternfly/components/Label/label.css"; +import PFBase from "@patternfly/patternfly/patternfly-base.css"; + +const statusNames = ["error", "warning", "info"] as const; +type StatusName = (typeof statusNames)[number]; + +const statusToDetails = new Map([ + ["error", ["pf-m-red", "fa-times"]], + ["warning", ["pf-m-orange", "fa-exclamation-triangle"]], + ["info", ["pf-m-gray", "fa-info-circle"]], +]); + +const styles = css` + :host { + --pf-c-label--m-gray--BackgroundColor: var(--pf-global--palette--black-100); + --pf-c-label--m-gray__icon--Color: var(--pf-global--primary-color--100); + --pf-c-label--m-gray__content--Color: var(--pf-global--info-color--200); + --pf-c-label--m-gray__content--before--BorderColor: var(--pf-global--palette--black-400); + --pf-c-label--m-gray__content--link--hover--before--BorderColor: var( + --pf-global--primary-color--100 + ); + --pf-c-label--m-gray__content--link--focus--before--BorderColor: var( + --pf-global--primary-color--100 + ); + } + + .pf-c-label.pf-m-gray { + --pf-c-label--BackgroundColor: var(--pf-c-label--m-gray--BackgroundColor); + --pf-c-label__icon--Color: var(--pf-c-label--m-gray__icon--Color); + --pf-c-label__content--Color: var(--pf-c-label--m-gray__content--Color); + --pf-c-label__content--before--BorderColor: var( + --pf-c-label--m-gray__content--before--BorderColor + ); + --pf-c-label__content--link--hover--before--BorderColor: var( + --pf-c-label--m-gray__content--link--hover--before--BorderColor + ); + --pf-c-label__content--link--focus--before--BorderColor: var( + --pf-c-label--m-gray__content--link--focus--before--BorderColor + ); + } +`; + +/** + * A boolean status indicator + * + * Based on the Patternfly "label" pattern, this component exists to display "Yes" or "No", but this + * is configurable. + * + * When the boolean attribute `good` is present, the background will be green and the icon will be a + * ✓. If the `good` attribute is not present, the background will be a warning color and an + * alternative symbol. Which color and symbol depends on the `type` of the negative status we want + * to show: + * + * - type="error" (default): A Red ✖ + * - type="warning" An orange ⚠ + * - type="info" A grey ⓘ + * + * By default, the messages for "good" and "other" are "Yes" and "No" respectively, but these can be + * customized with the attributes `good-label` and `bad-label`. + */ + +@customElement("ak-status-label") +export class AkStatusLabel extends AKElement { + static get styles() { + return [PFBase, PFLabel, styles]; + } + + @property({ type: Boolean }) + good = false; + + @property({ type: String, attribute: "good-label" }) + goodLabel = msg("Yes"); + + @property({ type: String, attribute: "bad-label" }) + badLabel = msg("No"); + + @property({ type: Boolean }) + compact = false; + + @property({ type: String }) + type: StatusName = "error"; + + render() { + const details = statusToDetails.get(this.type); + if (!details) { + throw new Error(`Bad status type [${this.type}] passed to ak-status-label`); + } + + const [label, color, icon] = this.good + ? [this.goodLabel, "pf-m-green", "fa-check"] + : [this.badLabel, ...details]; + + const classes = { + "pf-c-label": true, + [color]: true, + "pf-m-compact": this.compact, + }; + + return html` + + + ${label} + + `; + } +} + +export default AkStatusLabel; diff --git a/web/src/components/stories/ak-status-label.stories.ts b/web/src/components/stories/ak-status-label.stories.ts new file mode 100644 index 000000000..ab525a20e --- /dev/null +++ b/web/src/components/stories/ak-status-label.stories.ts @@ -0,0 +1,101 @@ +import "@goauthentik/elements/messages/MessageContainer"; +import { Meta } from "@storybook/web-components"; + +import { TemplateResult, html } from "lit"; + +import "../ak-status-label"; +import AkStatusLabel from "../ak-status-label"; + +const metadata: Meta = { + title: "Components / App Status Label", + component: "ak-status-label", + parameters: { + docs: { + description: { + component: "A status label display", + }, + }, + }, +}; + +export default metadata; + +const container = (testItem: TemplateResult) => + html`
+ + ${testItem} +
`; + +export const AppIcon = () => { + // prettier-ignore + return container(html` +
+
Good
+ + + +
+
Bad (Default)
+ + + +
+
Programmatically Good
+ + + +
+
Programmatically Bad
+ + + +
+
Good Warning
+ + + +
+
Bad Warning
+ + + +
+
Good Info
+ + + +
+
Bad Info
+ + + +
+
Good With Alternative Message
+ + + +
+
Bad with Alternative Message
+ + + +
+
Good, Compact
+ + + +
+
Bad, Compact
+ + + +
+
+ `); +}; diff --git a/web/src/elements/oauth/UserRefreshList.ts b/web/src/elements/oauth/UserRefreshList.ts index 288bf032b..2c5915f74 100644 --- a/web/src/elements/oauth/UserRefreshList.ts +++ b/web/src/elements/oauth/UserRefreshList.ts @@ -1,6 +1,6 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { uiConfig } from "@goauthentik/common/ui/config"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/forms/DeleteBulkForm"; import { PaginatedResponse } from "@goauthentik/elements/table/Table"; import { Table, TableColumn } from "@goauthentik/elements/table/Table"; @@ -85,9 +85,7 @@ export class UserOAuthRefreshList extends Table { row(item: TokenModel): TemplateResult[] { return [ html` ${item.provider?.name} `, - html` - ${item.revoked ? msg("Yes") : msg("No")} - `, + html``, html`${item.expires?.toLocaleString()}`, html`${item.scope.join(", ")}`, ]; diff --git a/web/src/user/user-settings/tokens/UserTokenList.ts b/web/src/user/user-settings/tokens/UserTokenList.ts index f32b06173..82cadeee1 100644 --- a/web/src/user/user-settings/tokens/UserTokenList.ts +++ b/web/src/user/user-settings/tokens/UserTokenList.ts @@ -2,7 +2,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { intentToLabel } from "@goauthentik/common/labels"; import { uiConfig } from "@goauthentik/common/ui/config"; import { me } from "@goauthentik/common/users"; -import { PFColor } from "@goauthentik/elements/Label"; +import "@goauthentik/components/ak-status-label"; import "@goauthentik/elements/buttons/Dropdown"; import "@goauthentik/elements/buttons/ModalButton"; import "@goauthentik/elements/buttons/TokenCopyButton"; @@ -97,9 +97,7 @@ export class UserTokenList extends Table {
- - ${item.expiring ? msg("Yes") : msg("No")} - +