Skip to content

Commit

Permalink
web: provide better feedback on Application Library page about search…
Browse files Browse the repository at this point in the history
… results (goauthentik#9386)

* web: fix esbuild issue with style sheets

Getting ESBuild, Lit, and Storybook to all agree on how to read and parse stylesheets is a serious
pain. This fix better identifies the value types (instances) being passed from various sources in
the repo to the three *different* kinds of style processors we're using (the native one, the
polyfill one, and whatever the heck Storybook does internally).

Falling back to using older CSS instantiating techniques one era at a time seems to do the trick.
It's ugly, but in the face of the aggressive styling we use to avoid Flashes of Unstyled Content
(FLoUC), it's the logic with which we're left.

In standard mode, the following warning appears on the console when running a Flow:

```
Autofocus processing was blocked because a document already has a focused element.
```

In compatibility mode, the following **error** appears on the console when running a Flow:

```
crawler-inject.js:1106 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
    at initDomMutationObservers (crawler-inject.js:1106:18)
    at crawler-inject.js:1114:24
    at Array.forEach (<anonymous>)
    at initDomMutationObservers (crawler-inject.js:1114:10)
    at crawler-inject.js:1549:1
initDomMutationObservers @ crawler-inject.js:1106
(anonymous) @ crawler-inject.js:1114
initDomMutationObservers @ crawler-inject.js:1114
(anonymous) @ crawler-inject.js:1549
```

Despite this error, nothing seems to be broken and flows work as anticipated.

* web: improve state management of Fuze application search

This commit rewrites a bit (just a bit, really!) of the relationship between
`ak-library-application-impl` and `ak-library-application-search`.

The "show only apps with launch URLs filter" has been moved up to the retrieval layer; there was no
reason for the renderer to repeatedly call a *required* filter; just call it on the list of
applications once and be done.

The search component exchanges the two-state guesswork and custom events for a concrete three-state
solution and *private* events. The search handler now sends the events "reset," "updated," and the
new "updated and empty," which we could not previously track.

By limiting the Impl layer to only those apps with launchUrls, we can now distinguish between "all
apps," and "filtered apps," and understand that when "all apps" is empty we have no apps, and when
"filtered apps" is empty the search has returned nothing.

I also tried to add a lot more comments.

In keeping with ES2020, I've put `.js` extensions on all the local imports.

In keeping with a variety of [best practice
recommendations](https://webcomponents.today/best-practices/), I've renamed web component files to
match the custom element they deploy:

```
ak-library-application-search-empty.ts
19:@CustomElement("ak-library-application-search-empty")

ak-library-impl.ts
44:@CustomElement("ak-library-impl")

ak-library.ts
30:@CustomElement("ak-library")

ak-library-application-list.ts
34:@CustomElement("ak-library-application-list")

ak-library-application-empty-list.ts
22:@CustomElement("ak-library-application-empty-list")

ak-library-application-search.ts
46:@CustomElement("ak-library-application-search")
```

The only effect(s) external to the changes in this vertical is that the Route() had to be updated,
and I have done that.

* web: updated the improved search to Google's Lit standards for events.
  • Loading branch information
kensternberg-authentik authored Jun 26, 2024
1 parent 0caa8cf commit eff85e4
Show file tree
Hide file tree
Showing 12 changed files with 337 additions and 200 deletions.
4 changes: 2 additions & 2 deletions web/src/user/LibraryPage/ApplicationEmptyState.stories.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { html } from "lit";

import "./ApplicationEmptyState";
import "./ak-library-application-empty-list";

export default {
title: "ApplicationEmptyState",
title: "Elements / Application Empty State",
};

export const OrdinaryUser = () =>
Expand Down
155 changes: 0 additions & 155 deletions web/src/user/LibraryPage/LibraryPageImpl.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";

import type { Application } from "@goauthentik/api";

import type { AppGroupEntry, AppGroupList } from "./types";
import type { AppGroupEntry, AppGroupList } from "./types.js";

type Pair = [string, string];

Expand All @@ -31,6 +31,13 @@ const LAYOUTS = new Map<string, [string, string]>([
],
]);

/**
* @element ak-library-application-list
* @class LibraryPageApplicationList
*
* Renders the current library list of a User's Applications.
*
*/
@customElement("ak-library-application-list")
export class LibraryPageApplicationList extends AKElement {
static get styles() {
Expand Down
33 changes: 33 additions & 0 deletions web/src/user/LibraryPage/ak-library-application-search-empty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { AKElement } from "@goauthentik/elements/Base";

import { msg } from "@lit/localize";
import { html } from "lit";
import { customElement } from "lit/decorators.js";

import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";

/**
* Library Page Application List Empty
*
* Display a message if there are no applications defined in the current instance. If the user is an
* administrator, provide a link to the "Create a new application" page.
*/

@customElement("ak-library-application-search-empty")
export class LibraryPageApplicationSearchEmpty extends AKElement {
static get styles() {
return [PFBase, PFEmptyState, PFContent, PFSpacing];
}

render() {
return html` <div class="pf-c-empty-state pf-m-full-height">
<div class="pf-c-empty-state__content">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">${msg("Search returned no results.")}</h1>
</div>
</div>`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,30 @@ import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";

import type { Application } from "@goauthentik/api";

import { SEARCH_ITEM_SELECTED, SEARCH_UPDATED } from "./constants";
import { customEvent } from "./helpers";

@customElement("ak-library-list-search")
export class LibraryPageApplicationList extends AKElement {
import {
LibraryPageSearchEmpty,
LibraryPageSearchReset,
LibraryPageSearchSelected,
LibraryPageSearchUpdated,
} from "./events.js";

/**
* @element ak-library-list-search
*
* @class LibraryPageApplicationSearch
*
* @classdesc
*
* The interface between our list of applications shown to the user, an input box, and the Fuse
* fuzzy search library.
*
* @fires LibraryPageSearchUpdated
* @fires LibraryPageSearchEmpty
* @fires LibraryPageSearchReset
*
*/
@customElement("ak-library-application-search")
export class LibraryPageApplicationSearch extends AKElement {
static get styles() {
return [
PFBase,
Expand Down Expand Up @@ -75,11 +94,7 @@ export class LibraryPageApplicationList extends AKElement {
}

onSelected(apps: FuseResult<Application>[]) {
this.dispatchEvent(
customEvent(SEARCH_UPDATED, {
apps: apps.map((app) => app.item),
}),
);
this.dispatchEvent(new LibraryPageSearchUpdated(apps.map((app) => app.item)));
}

connectedCallback() {
Expand All @@ -102,7 +117,7 @@ export class LibraryPageApplicationList extends AKElement {
updateURLParams({
search: this.query,
});
this.onSelected([]);
this.dispatchEvent(new LibraryPageSearchReset());
}

onInput(ev: InputEvent) {
Expand All @@ -113,8 +128,13 @@ export class LibraryPageApplicationList extends AKElement {
updateURLParams({
search: this.query,
});

const apps = this.fuse.search(this.query);
if (apps.length < 1) return;
if (apps.length < 1) {
this.dispatchEvent(new LibraryPageSearchEmpty());
return;
}

this.onSelected(apps);
}

Expand All @@ -125,7 +145,7 @@ export class LibraryPageApplicationList extends AKElement {
return;
}
case "Enter": {
this.dispatchEvent(customEvent(SEARCH_ITEM_SELECTED));
this.dispatchEvent(new LibraryPageSearchSelected());
return;
}
}
Expand Down
Loading

0 comments on commit eff85e4

Please sign in to comment.