Files
authentik/web/src/admin/users/UserApplicationTable.ts
Ken Sternberg 0a1d283ac8 web: provide storybook demos and docs for existing tests (#11651)
* Added tests and refinements as tests indicate.

* Building out the test suite.

* web: test the simple things. Fix what the tests revealed.

- Move `EmptyState.test.ts` into the `./tests` folder.
- Provide unit tests for:
  - Alert
  - Divider
  - Expand
  - Label
  - LoadingOverlay
- Give all tested items an Interface and a functional variant for rendering
- Give Label an alternative syntax for declaring alert levels
- Remove the slot name in LoadingOverlay
  - Change the slot call in `./enterprise/rac/index.ts` to not need the slot name as well
- Change the attribute names `topMost`, `textOpen`, and `textClosed` to `topmost`, `text-open`, and
  `text-closed`, respectively.
  - Change locations in the code where those are used to correspond

** Why interfaces: **

Provides another check on the input/output boundaries of our elements, gives Storybook and
WebdriverIO another validation to check, and guarantees any rendering functions cannot be passed
invalid property names.

** Why functions for rendering: **

Providing functions for rendering gets us one step closer to dynamically defining our forms-in-code
at runtime without losing any type safety.

** Why rename the attributes: **

A *very* subtle bug:
[Element:setAttribute()](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute)
automatically "converts an attribute name to all lower-case when called on an HTML element in an
HTML document." The three attributes renamed are all treated *as* attributes, either classic boolean
or stringly-typed attributes, and attempting to manipulate them with `setAttribute()` will fail.

All of these attributes are presentational; none of them end up in a transaction with the back-end,
so kebab-to-camel conversions are not a concern.

Also, ["topmost" is one word](https://www.merriam-webster.com/dictionary/topmost).

** Why remove the slot name: **

Because there was only one slot.  A name is not needed.

* Fix minor spelling error.

* First pass at a custom, styled input object.

* .

* web: Demo the simple things. Fix things the Demo says need fixing.

- Move the Element's stories into a `./stories` folder
- Provide stories for (these are the same ones "provided tests for" in the [previous
  PR](https://github.com/goauthentik/authentik/pull/11633))
  - Alert
  - Divider
  - Expand
  - Label
  - LoadingOverlay
- Provide Storybook documentation for:
  - AppIcon
  - ActionButton
  - AggregateCard
  - AggregatePromiseCard
  - QuickActionsCard
  - Alert
  - Divider
  - EmptyState
  - Expand
  - Label
  - LoadingOverlay
  - ApplicationEmptyState
- Fix a bug in LoadingOverlay; naming error in nested slots caused any message attached to the
  overlay to not sow up correctly.
- Revise AppIcon to be independent of authentik; it just cares if the data has a name or an icon
  reference, it does not need to know about `Application` objects. As such, it's an *element*, not a
  *component*, and I've moved it into the right location, and updated the few places it is used to
  match.

* Prettier has opinions with which I sometimes diverge.

* Found a bug! Although pf-m-xl was defined as a legal size, there was no code to handle drawing something XL!

* Found a few typos and incorrect API descriptions.
2024-10-14 10:30:09 -07:00

85 lines
3.3 KiB
TypeScript

import { applicationListStyle } from "@goauthentik/admin/applications/ApplicationListPage";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/AppIcon";
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { Application, CoreApi, User } from "@goauthentik/api";
@customElement("ak-user-application-table")
export class UserApplicationTable extends Table<Application> {
@property({ attribute: false })
user?: User;
static get styles(): CSSResult[] {
return super.styles.concat(applicationListStyle);
}
async apiEndpoint(): Promise<PaginatedResponse<Application>> {
return new CoreApi(DEFAULT_CONFIG).coreApplicationsList({
...(await this.defaultEndpointConfig()),
forUser: this.user?.pk,
});
}
columns(): TableColumn[] {
return [
new TableColumn(""),
new TableColumn(msg("Name"), "name"),
new TableColumn(msg("Group"), "group"),
new TableColumn(msg("Provider")),
new TableColumn(msg("Provider Type")),
new TableColumn(msg("Actions")),
];
}
row(item: Application): TemplateResult[] {
return [
html`<ak-app-icon
name=${item.name}
icon=${ifDefined(item.metaIcon || undefined)}
></ak-app-icon>`,
html`<a href="#/core/applications/${item.slug}">
<div>${item.name}</div>
${item.metaPublisher ? html`<small>${item.metaPublisher}</small>` : html``}
</a>`,
html`${item.group || msg("-")}`,
item.provider
? html`<a href="#/core/providers/${item.providerObj?.pk}">
${item.providerObj?.name}
</a>`
: html`-`,
html`${item.providerObj?.verboseName || msg("-")}`,
html`<ak-forms-modal>
<span slot="submit"> ${msg("Update")} </span>
<span slot="header"> ${msg("Update Application")} </span>
<ak-application-form slot="form" .instancePk=${item.slug}>
</ak-application-form>
<button slot="trigger" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Edit")}>
<i class="fas fa-edit"></i>
</pf-tooltip>
</button>
</ak-forms-modal>
${item.launchUrl
? html`<a href=${item.launchUrl} target="_blank" class="pf-c-button pf-m-plain">
<pf-tooltip position="top" content=${msg("Open")}>
<i class="fas fa-share-square"></i>
</pf-tooltip>
</a>`
: html``}`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-user-application-table": UserApplicationTable;
}
}