web: replace 'description-list' with list of descriptions (#7392)

* 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.

* web: description lists as functions

One thing I hate is clutter.  Just tell me what you're going to do.  "Description Lists" in our code are
renderings of Patternfly's DescriptionList; we use only four of
their idioms: horizontal, compact, 2col, and 3col.  With that in mind, I've stripped out the DescriptionList
rendering code from UserViewPage and replaced it with a list of "Here's what to render" and a function call
to render them.  The calling code is still responsible for having the right styles available, as this is
not a component or an attempt at isolation; it is *just* a function (at this point).

* web: fix issue that prevented the classMap from being rendered properly

* web: added comments to the description list.

* web: analyze & prettier had opinions

* web: Fix description-list demo

This commit re-instals the demo for the "description list" of user fields.

* web: prettier had opinions.

* any -> unknown

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Ken Sternberg
2023-12-13 07:16:57 -08:00
committed by GitHub
parent b181c551a5
commit 026e80bd10
2 changed files with 181 additions and 156 deletions

View File

@ -0,0 +1,94 @@
import { TemplateResult, html, nothing } from "lit";
import { classMap } from "lit/directives/class-map.js";
import { map } from "lit/directives/map.js";
export type DescriptionDesc = string | TemplateResult | undefined | typeof nothing;
export type DescriptionPair = [string, DescriptionDesc];
export type DescriptionRecord = { term: string; desc: DescriptionDesc };
interface DescriptionConfig {
horizontal: boolean;
compact: boolean;
twocolumn: boolean;
threecolumn: boolean;
}
const isDescriptionRecordCollection = (v: Array<unknown>): v is DescriptionRecord[] =>
v.length > 0 && typeof v[0] === "object" && !Array.isArray(v[0]);
function renderDescriptionGroup([term, description]: DescriptionPair) {
return html` <div class="pf-c-description-list__group">
<dt class="pf-c-description-list__term">
<span class="pf-c-description-list__text">${term}</span>
</dt>
<dd class="pf-c-description-list__description">
<div class="pf-c-description-list__text">${description ?? nothing}</div>
</dd>
</div>`;
}
function recordToPair({ term, desc }: DescriptionRecord): DescriptionPair {
return [term, desc];
}
function alignTermType(terms: DescriptionRecord[] | DescriptionPair[] = []) {
if (isDescriptionRecordCollection(terms)) {
return terms.map(recordToPair);
}
return terms ?? [];
}
/**
* renderDescriptionList
*
* This function renders the most common form of the PatternFly description list used in our code.
* It expects either an array of term/description pairs or an array of `{ term: string, description:
* string | TemplateResult }`.
*
* An optional dictionary of configuration options is available. These enable the Patternfly
* "horizontal," "compact", "2 column on large," or "3 column on large" layouts that are (so far)
* the layouts used in Authentik's (and Gravity's, for that matter) code.
*
* This is not a web component and it does not bring its own styling ; calling code will still have
* to provide the styling necessary. It is only a function to replace the repetitious boilerplate of
* routine description lists. Its output is a standard TemplateResult that will be fully realized
* within the context of the DOM or ShadowDOM in which it is called.
*/
const defaultConfig = {
horizontal: false,
compact: false,
twocolumn: false,
threecolumn: false,
};
export function renderDescriptionList(
terms: DescriptionRecord[],
config?: DescriptionConfig,
): TemplateResult;
export function renderDescriptionList(
terms: DescriptionPair[],
config?: DescriptionConfig,
): TemplateResult;
export function renderDescriptionList(
terms: DescriptionRecord[] | DescriptionPair[] = [],
config: DescriptionConfig = defaultConfig,
) {
const checkedTerms = alignTermType(terms);
const classes = classMap({
"pf-m-horizontal": config.horizontal,
"pf-m-compact": config.compact,
"pf-m-2-col-on-lg": config.twocolumn,
"pf-m-3-col-on-lg": config.threecolumn,
});
return html`
<dl class="pf-c-description-list ${classes}">
${map(checkedTerms, renderDescriptionGroup)}
</dl>
`;
}
export default renderDescriptionList;