* remove wrong help text for multi select
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* make labelling for create and and bind existing more consistent
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix oobe missing label
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix application library empty state not shown
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix missing formatting for title on access denied stage
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
---------
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* 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: provide `show password` on login page
Provide a `show password` icon, text, and button for the password field both in the
IdentificationStage and the PasswordStage. Essentially the same code for both, although the id of
the password field is unique to each.
Requested by Cloudflare. Seems to be a common thing anyway.
Should it be an administrative option that this facility is available? From where should I derive
that information? I suspect the answer is "a site attribute," but I'd like to get confirmation.
* web: comment doesn't need to be exposed. It's sufficient where it is .
* web: fix button rendering issues
During testing, the buttons did not change as expected. We are using pure DOM
state to control the look of the button, and avoiding using `.requestUpdate()`
to avoid losing customer input, so depending upon Lit to re-render just the
button was an error.
This commit goes old-school and updates the button's label and icon using
standard DOM features, although we do lean into Lit-html`s `render()`
function to create the DOM component for the icon.
* web: provide `show password` on login page
Provide a `show password` icon, text, and button for the password field both in the
IdentificationStage and the PasswordStage. Essentially the same code for both, although the id of
the password field is unique to each.
Provide a configuration detail server-side to allow administrator to enable or disable the 'show
password' feature. Off by default.
Requested by Cloudflare. Seems to be a common thing anyway. Making it configurable wasn't in
Cloudfare's request, but it seemed logical to add.
* ensure the tests pass; quibbling over the wording of the admin field continues.
* Removed some manually identified fluff.
* web: break out `show password`-enabled input field into its own component
Provides a `show password` field, but as a LightDOM-oriented web component. This form of
input[type="password"] is for flows only, as it has a number of specializations for understanding a
flow's validating round-trip, possible error messages within the challenge, and is left within the
LightDOM both to support compatibility issues and to avoid using `elementInterals`, which is a DOM
feature not supported by some older browsers.
Avoids having to maintain two different instances of the same logic, both for permitting 'show
password', and for handling it.
* web: update PasswordStageForm according to lit-analyzer
With lit-analyzer in the mix and functional, we're seeing new complaints about
inconsistent typing in lit objects, and this was one of them.
* Another lit-analyze error found.
* 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: enhance search select
Patternfly doesn't even *have* a setting for "selected but not hovered," so I had to invent one. I
borrowed a trick from MUI and used the light blue "info" color, making it darker on
"selected+hovered."
This commit starts the revision process for search select. The goal is to have it broken down into
four major components: The inline-DOM component, which just shows the current value (or placeholder,
if none) and creates the portal for the floating component, then have a higher-level component for
the SearchSelect behavior, and a sidecar to manage the keyboard interactivity.
This is the portaled component: the actual list.
* web: enhance search select. Break menu and Input items into separate handlers.
* web: search select: added keyboard controller.
* web: search select - the isolation continues
This commit brings us to the position of having an independently rendered menu that listens for
click events on its contents, records the value internally *and* sends it upstream as an event.
This commit also includes a KeyboardController reactor that listens for keyboard events on a list of
objects, moving the focus up and down and sending a both a "selected" event when the user presses
Enter or Space, and a "close" event when the user presses Escape.
A lot of this is just infrastructure. None of these *do* very much; they're just tools for making
SearchSelect better.
AkSearchSelectView is next: it's responsible for rendering the input and menu items, and then for
forwarding the `value` component up to whoever cares.
`ak-search-select` will ultimately be responsible for fetching the data and mapping the string
tokens from AkSearchSelectView back into the objects that Authentik cares about.
* web: search select - a functioning search select
So search select is now separated into the following components:
- SearchSelectView: Takes the renderables and the selected() Value and draws the Value in a
box, then forwards the Options to a portaled drop-down component.
- SearchSelectMenuPosition: A web component that renders the Menu into the <BODY> element and
positions it with respect to an anchor provided by SearchSelectView.
- SearchSelectMenu: Renders the Menu and listens for events indicating an Item has been selected.
Sends events through a reference to the View.
- SearchKeyboardController: A specialized listener that keeps an independent list of indices and
tabstops, and listens for keyboard events to move the index forward or backward, as well as for
Event or Space for "select" and Escape for "close". Doesn't actually _do_ these things; they're
just semantics implied by the event names, it just sends an event up to the host, which can do
what it wants with them.
What's not done:
- SearchSelect: The interface with the API. Maps to and from API values to renderable Options.
One thing of note: of the 35 uses of SearchSelect in our product, 28 of them have `renderElement`
annotations of a single field. Six of them use the same annotation (renderFlow), and only one (in
EventMatcherPolicyForm) is at all complex. The 28 are:
- 7: group.name;
- 1: item.label;
- 5: item.name;
- 1: policy.name;
- 1: role.name;
- 1: source.name;
- 3: stage.name;
- 9: user.username;
I propose to modify `.renderElement` to take a string of `keyof T`, where T is the type passed to the
SearchSelect; it will simply look that up in the object passed in and use that as the Label.
`.renderDescription` is more or less similar, except it has _no_ special cases:
- 6: html`${flow.name}`;
- 1: html`${source.verboseName}`;
- 9: html`${user.name}`;
- 2: html`${flow.slug}`;
Given that, it makes sense to modify this as well to take a field key as a look up and render it,
making all that function calling somewhat moot.
Selected has a similar issue; passing it a value that is _not_ a function would be a signal to find
this specific element in the corresponding 'pk'. Or we could pass a tuple of [keyof T] and value,
so we didn't have to hard-code 'pk' into the thing.
- 1 return item.pk === this.instance?.createUsersGroup;
- 1 return item.pk === this.instance?.filterGroup;
- 2 return item.pk === this.instance?.group;
- 1 return item.pk === this.instance?.parent;
- 1 return item.pk === this.instance?.searchGroup;
- 1 return item.pk === this.instance?.syncParentGroup;
- 1 return item.pk === this.instance?.policy;
- 1 return item.pk === this.instance?.source;
- 1 return item.pk === this.instance?.passwordStage;
- 1 return item.pk === this.instance?.stage;
- 1 return item.pk === this.instance?.user;
- 2 return item.pk === this.previewUser?.pk;
- 5 return item.pk === this.instance?.configureFlow;
- 1 return item.pk === this.instance?.mapping;
- 1 return item.pk === this.instance?.nameIdMapping;
- 1 return item.pk === this.instance?.user;
- 1 return item.pk === this.instance?.webhookMapping;
- 1 return item.component === this.instance?.action;
- 1 return item.path === this.instance?.path;
- 1 return item.name === this.instance?.model;
- 1 return item.name === this.instance?.app;
- 1 return user.pk.toString() === this.request?.toString();
- 2 return this.request?.user.toString() === user.pk.toString();
And of course, `.value` kinda sorta has the same thing going on:
- 6: flow?.pk;
- 3: group ? group.pk : undefined;
- 4: group?.pk;
- 1: item?.component;
- 2: item?.name;
- 1: item?.path;
- 4: item?.pk;
- 1: policy?.pk;
- 1: role?.pk;
- 1: source?.pk;
- 3: stage?.pk;
- 8: user?.pk;
- 1: user?.username;
All in all, the _protocol_ for SearchSelect could be streamlined. A _lot_. And still retain the
existing power.
* Old take; not keeping.
* Didn't need this either.
* web: search select - a functioning search select with API interface
So many edge cases!
Because the propagation here is sometimes KeyboardEvent -> MenuEvent -> SearchSelectEvent, I had to
rename some of the events to prevent them from creating infinite loops of event handling. This
resulted in having to define separate events for Input, Close, and Select.
I struggled like heck to get the `<input>` object to show the value after updating. Ultimately, I
had to special case the `updated()` method to make sure it was showing the currently chosen display
value. Looking through Stack Overflow, there's a lot of contention about the meaning of the `value`
field on HTMLInputElements.
The API layer distinguishes between a "search" event, which triggers the query to run, and the
"select" event, which triggers the component to pick an object as _the_ `.value`.
The API layer handles the conversion to GroupedItems and makes sure that the View receives either
FlatSelect or GroupedSelect options collections (see ./types, but in practice users should never
care too much about this.)
* web: completed the search select update
* web: search-select reveals a weakness in our plans
While testing SearchSelect, I realized that the protocol for our "custom input elements" was
neither specified nor documented. I have attempted to fix that, and am finding edge cases
and buggy implementations that required addressing.
I've described the protocol by creating a class that implements it: AkControlElement. It
extends the constructor to always provide the "this is an data-ak-control element," and
provides a `json()` method that throws an exception in the base class, so it must always
be overriden and never called as super().
I've also fixed ak-dual-select so it carries its name properly into the Forms parser.
* web: search select (and friends)
This commit finalizes the search select quest! Headline: Search Select is now keyboard-friendly
*and* CSS friendly; the styling needed for position is small enough to fit in a `styleMap`, and the
styling for the menu itself can be safely locked into a web component.
Primarily, I was forgetting to map the value to its displayValue whenever the value was changed from
an external source. It should have been an easy catch, but I missed it the first dozen times
through.
* Not using this yet. ESLint-9 experiment that was loosely left here for some reason.
* Added lots of comments.
* Added new comments, fixed error message.
* Removing a console.log
* Fixed an incorrect comment.
* Added comments about workaround.
* web: focus fixes.
Fixes several issues with the drop-down, including primarily how "loss of focus"
does not result in the pop-up being banished. Also, the type definition for the
attribute `hidden` is inconsistent between Typescript, the attribute, and the
related property; I've chosen to route around that problem by using a custom
attribute and setting `hidden` in the template, where `lit-analyze` has a workable
definition and allows it to pass. Finally, on `open` the focus is passed to the
current value, if any.