35f96df66eae6cffcb20889750cd669227ffdd23
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 35f96df66e |
web: Testing and documenting the simple things
This commit adds unit tests for the Alerts and EmptyState elements. It includes a new test/documentation feature; elements can now be fully documented with text description and active controls. It *removes* the `babel` imports entirely. Either we don't need them, or the components that do need them are importing them automatically. [An outstanding bug in WebDriverIO](https://github.com/webdriverio/webdriverio/issues/12056) unfortunately means that the tests cannot be run in parallel for the time being.While one test is running, the compiler for other tests becomes unreliable. They're currently working on this issue. I have set the `maxInstances` to **1**. I have updated the `<ak-alert>` component just a bit, providing an attribute alternative to the `Level` property; now instead of passing it a `<ak-alert level=${Levels.Warning}>` properties, you can just say `<ak-alert warning>` and it'll work just fine. The old way is still the default behavior. The default behavior for `EmptyState` was a little confusing; I've re-arranged it for clarity. Since I touched it, I also added the `interface` and `HTMLElementTagNameMap` declarations. Added documentation to all the elements I've touched (so far). |
|||
| 5805ac83f7 |
web: clean up and remove redundant alias '@goauthentik/app' (#8889)
* 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: clean up and remove redundant alias '@goauthentik/app'
The path alias `@goauthentik/app` has been a thorn in our side for a long time, as it conflicts with
or is redundant with all the *other* aliases in `tsconfig.json`, such as `@goauthentik/elements` and
`@goauthentik/locales`.
This commit *replaces* `@goauthentik/app` with `@goauthentik/authentik` for a single use case: the
locale codes file in the project root. That also helps reserve the subproject name `authentik` in
case we ever do go the monorepo root.
Other than that, all the rest have been removed with the following mechanical refactor:
```
perl -pi.bak -e 's{\@goauthentik/app/}{\@goauthentik/}' $(rg -l '@goauthentik/app/' ./src/)
```
* web: separate the sizing enum from a specific component implementation (#8890)
The PFSizes enum is used by more than just the Spinner, but has been left inside the Spinner for all
this time, making refactoring the Spinner for Patternfly 5 a little harder (okay, an annoying amount
harder) than it should be.
This commit moves this UI-specific, widely-use enum into its own folder in `common`, and refactors
everything else to use it. As is often the case, the refactor is mechanical:
```
perl -pi.bak -e 's{import \{ PFSize \} from "\@goauthentik/elements/Spinner";}{import \{ PFSize \}
from "\@goauthentik/common/enums.js";}' \\
$(rg -l 'import.*PFSize')
```
**Note:** This commit is dependent upon the ["clean up and remove redundant alias `@goauthentik/app`" PR](https://github.com/goauthentik/authentik/pull/8889)
|
|||
| 2ba66f4f91 |
web: upgrade to lit 3 (#8781)
* Holding for a moment...
* web: replace rollup with esbuild
This commit replaces rollup with esbuild.
The biggest fix was to alter the way CSS is imported into our system;
esbuild delivers it to the browser as text, rather than as a bundle
with metadata that, frankly, we never use. ESBuild will bundle the
CSS for us just fine, and interpreting those strings *as* CSS turned
out to be a small hurdle. Code has been added to AKElement and
Interface to ensure that all CSS referenced by an element has been
converted to a Browser CSSStyleSheet before being presented to the
browser.
A similar fix has been provided for the markdown imports. The
biggest headache there was that the re-arrangement of our documentation
broke Jen's existing parser for fixing relative links. I've provided
a corresponding hack that provides the necessary detail, but since
the Markdown is being presented to the browser as text, we have to
provide a hint in the markdown component for where any relative
links should go, and we're importing and processing the markdown
at runtime. This doesn't seem to be a big performance hit.
The entire build process is driven by the new build script, `build.mjs`,
which starts the esbuild process as a service connected to the build
script and then runs the commands sent to it as fast as possible.
The biggest "hack" in it is actually the replacement for rollup's
`rollup-copy-plugin`, which is clever enough I'm surprised it doesn't
exist as a standalone file-copy package in its own right.
I've also used a filesystem watch library to encode a "watcher"
mechanism into the build script. `node build.mjs --watch` will
work on MacOS; I haven't tested it elsewhere, at least not yet.
`node build.mjs --proxy` does what the old rollup.proxy.js script
did.
The savings are substantial. It takes less than two seconds to build
the whole UI, a huge savings off the older ~45-50 seconds I routinely
saw on my old Mac. It's also about 9% smaller.
The trade-offs appear to be small: processing the CSS as StyleSheets,
and the Markdown as HTML, at run-time is a small performance hit,
but I didn't notice it in amongst everything else the UI does as
it starts up.
Manual chunking is gone; esbuild's support for that is quite difficult
to get right compared to Rollup's, although there's been a bit of
yelling at ESbuild over it. Codemirror is built into its own chunk;
it's just not _named_ distinctly anymore.
The one thing I haven't been able to test yet is whether or not the
polyfills and runtim shims work as expected on older browsers.
* web: continue with performance and build fixes
This commit introduces a couple of fixes enabled by esbuild and other
features.
1. build-locales
`build-locales` is a new NodeJS script in the `./scripts` folder
that does pretty much what it says in the name: it translates Xliff
files into `.ts` files. It has two DevExp advantages over the old
build system.
First, it will check the build times of the xlf files and
their ts equivalents, and will only run the actual build-locales
command if the XLF files are newer than their TS equivalents.
Second, it captures the stderr output from the build-locales command
and summarizes it. Instead of the thousands of lines of "this
string has no translation equivalent," now it just reports the
number of missed translations per locale.
2. check-spelling
This is a simple wrapper around the `codespell` command, mostly
just to reduce the visual clutter of `package.json`, but also to
permit it to run just about anywhere without needed hard-coded
paths to the dictionaries, using a fairly classic trick with git.
3. pseudolocalize and import-maps
These scripts were in TypeScript, but for our purposes I've
saved their constructed equivalents instead. This saves on
visual clutter in the `package.json` script, and reduced the
time they have to run during full builds. They're small enough
I feel confident they won't need too much looking over.
Also, two lint bugs in Markdown.ts have been fixed.
* Removed a few lines that weren't in use.
* build-locales was sufficiently complex it needed some comments.
* web: formalize that horrible unixy git status checker into a proper function.
* Added types for , the Markdown processor for in-line documentation.
* web: upgrade to Lit3
This commit replaces our Lit2 implementation with a Lit3 implementation.
This upgrade required two major shifts within our code, both of them consequential.
First, the restructuring of the way the get/set decorators for properties and states meant that a
lot of the code we were using needed to be refactored. More than that, a lot of those custom
accessors were implemented to trigger side-effects, such as when a providerID is set or changed
triggering the ProviderView to fetch the requsted Provider. The Lit2 and Lit3 documentation both say
[there is a better way to handle
this](https://lit.dev/docs/v2/components/properties/#:~:text=In%20most%20cases%2C%20you%20do%20not%20need%20to%20create%20custom%20property%20accessors)
by detecting the change in the `willUpdate()` point of an elements Lifecycle and triggering the side
effect there instead. I've done this in several places with a pattern of detecting the change, and
then naming the corresponding change as `fetchRequestedThing()`. The resulting code is cleaner and
uses fewer controversial features.
The other is that the type signature for `LitElement.createRenderRoot()` has changed to be either an
HTMLElement or a DocumentFragment. This required some serious refactoring of type changes through
Base and Interface codes. Noteably, the custom `AdoptedStyleSheetsElement` interface has been
superseded by the supplied and standardized
[DocumentOrShadowRoot](
|
|||
| f728bbb14b |
sources/ldap: add check command to verify ldap connectivity (#7263)
* sources/ldap: add check command to verify ldap connectivity Signed-off-by: Jens Langhammer <jens@goauthentik.io> * default to checking all sources Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start adding an API for ldap connectivity Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add webui for ldap source connection status Signed-off-by: Jens Langhammer <jens@goauthentik.io> * better show sync status, clear previous tasks Signed-off-by: Jens Langhammer <jens@goauthentik.io> * set timeout on redis lock for ldap sync Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix py lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix web lint Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> |
|||
| c7182bf513 |
Revert "web: Updates to the Context and Tasks libraries from lit. (#7168)"
This reverts commit
|
|||
| 15be83c06c |
web: Updates to the Context and Tasks libraries from lit. (#7168)
* Updates to the Context and Tasks libraries from lit. * web: fix for bad merge * Still trying to solve that f*&!ing merge bug. * fix build Signed-off-by: Jens Langhammer <jens@goauthentik.io> * Updates to the Context and Tasks libraries from lit. * web: fix for bad merge * Still trying to solve that f*&!ing merge bug. * fix build Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io> |
|||
| 42c3cfa65d |
web/admin: user details few tooltip buttons (#6899)
* Web: Detangling some circular dependencies in Admin and User Admin, User, and Flow should not dependend upon each other, at least not in a circular way. If Admin and User depend on Flow, that's fine, but Flow should not correspondingly depend upon elements of either; if they have something in common, let's put them in `@goauthentik/common` or find some other smart place to store them. This commit refactors the intentToLabel and actionToLabel functions into `@goauthentik/common/labels` and converts them to static tables for maintenance purposes. * web: "Consistency is the hobgoblin of small minds" - Ralph Waldo Emerson * web: I found these confusing to look at, so I added comments. * web: remove admin-to-user component reference(s) There was only one: AppIcon. This has been moved to `components`. Touching the LibraryApplications page triggered a cyclomatic complexity check. Extracting the expansion block and streamlining the class and style declarations with lit directives helped. * web: remove admin from elements This commit removes the two references from `elements` to `admin`: the list of UserEvents and a reference to the FlowSearch type, used by the Forms manager to decide how to extract a value. For FlowSearch, a different convention for detecting the type was implemented (instances of the object have a unique fieldname for the value holder). UserEvents and ObjectChangelog have been moved to `components` as they're clearly dependent upon the API. This defers work on removing Admin from Components, as that is (again) references going the wrong way, but that can happen later. * web: remove admin-to-user component reference(s) (#6856) There was only one: AppIcon. This has been moved to `components`. Touching the LibraryApplications page triggered a cyclomatic complexity check. Extracting the expansion block and streamlining the class and style declarations with lit directives helped. * This was supposed to be merged. * web: remove `./element`⇢`./user` references The offender here is UserDevicesList, which despite being in `elements` is only used by the admin/user/UserViewPage. The problem is that UserDevicesList, despite being in `admin`, inherits from `user`, so moving it would have created a new admin⇢user reference, and the whole point of this exercise is to get rid of references that point "up" from the foundational pieces to the views, or that refer to components in sibling applications. After examining UserDevicesList, I realized that *every feature* of MFADevicesList had been overridden: the rows, the columns, the toolbar, and the endpoint all had custom overrides. Nothing was left of MFADevicesList after that. Even the property that the web component used had been completely changed. The only thing they had in common was that they both inherited from `Table<Device>`. Refactoring UserDevicesList so that it inherited directly from `Table<Device>` and then moving it into `./admin/users` was the obvious and correct step. Both used the same label table, so that went into the `common/labels` folder. Along the way, I cleaned up a few minor details. Just little things, like the repeated invocation of: ``` new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorAdminMETHODDestroy({ id: device.pk }); ``` This is repeated five times, once for each Method. By creating these: ``` const api = new AuthenticatorsApi(DEFAULT_CONFIG); const id = { id: device.pk }; ``` The method invocation could be just `api.authenticatorsMETHODDestroy(id)`, which is easier on the eyes. See the MFADevicesPage for the full example. Similarly, ``` return [ new TableColumn(msg("Name"), ""), new TableColumn(msg("Type"), ""), new TableColumn("") ]; ``` is more straightforward as: ``` const headers = [msg("Name"), msg("Type"), ""]; return headers.map((th) => new TableColumn(th, "")); ``` We've labeled what we're working with, and web developers ought to know that `th` is the HTML code for `table header`. I've had to alter what files are scanned in pre-commit mode; it doesn't handle renamed files very well, and at the moment a file that is renamed is not scanned, as its "new" name is not straightforwardly displayed, not even by `git porcelain`. * web: make the table of column headers look like a table * web: detangle `common` from `elements`. And just like that, `common` no longer has a reference to `elements`. I don't mind this little bit of code duplication if it removes a cycle. What it does point out is that there are bits of `common` that are predicated on the presence of the browser, and that there are bits of `elements` that, if they rely on `common`, can't be disentangled from the application as a whole. Which seems to me that we have two different things going on in common: things about an application, and things about elements that are independent of the application. I'll think about those later. ``` $ rg 'import.*@goauthentik' ./common/ | perl -ne 'm{"(@goauthentik[^"]*)"} && print "$1\n"' | sort | cut -d '/' -f1-2 | uniq | sort @goauthentik/api @goauthentik/common $ ``` * web: odd bug; merge-related? Gonna investigate. * web: build failure thanks to local cache; fixed * web: detangle `components` from `admin`. This was the last inappropriate reference: something from `./components` referencing something in `./admin`, in this case the `ak-event-info` component. Used by both Users and Admin, moving it into `./components` was the obvious correct step. `ak-event-info` is a lookup table relating specific events in the event log to rich, textual representations; in the special case of model changes and email info, even more rich content is available in a dl/dt format. I've tableized the model changes and email info renderer, and I've extracted every event's textual representation into its own method, converting the `switch/case` rendering statement into a `switch/case` dispatch switch. This has the virtue of isolating each unique case and making the dispatch switch short and coherent. The conversion was done mechanistically; I gave the refactorer (Tide, in this case) instructions to duplicate the switch block and then convert every case into a method with a name patterned on the `case`. Going back to the original switch block, it was easy to duplicate the pattern matching and convert it into a dispatch switch. And with this, there are zero cycles in the references between the different "packageable" sections of the UI. The only thing left to do is figure out how to redistribute `./elements` and `./components` in a way that makes sense for each. * Changed function name from 'emailMessageBody' to 'githubIssueMessageBody' to better reflect its usage. * web: added comments about length and purpose of githubIssueMessageBody. * Update web/src/common/labels.ts Co-authored-by: Jens L. <jens@goauthentik.io> Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com> * Unwanted change. * web/add tooltip buttons to user details page This commit wraps the command buttons on the UserDetailsPage with tooltips providing greater copy explaining what each button does. It also ensures that every button is a minimum of 11ems in width (The longest phrase, 'Reset Password', results in a width of 10.75ems; this makes them all consistent.) The technique for giving the `ak-action-button` objects a mininum width uses the CSS `::part()` syntax, which is new. CanIUse shows that it's at 95.3% of global usage; our weak points remain Opera Mini and UC Browser for Android. Oh, and IE. But the various Powers That Be™ agree we're no longer tracking or caring about IE. * I added some text, so it's my responibility to add the language files. * fix text Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rework Signed-off-by: Jens Langhammer <jens@goauthentik.io> * web: enforce a max-width on the container for the buttons so that they don't look funky on ultrawide monitors. * wbe: re-ran and confirmed prettier. --------- Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com> Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens L. <jens@goauthentik.io> |
|||
| 12c4ac704f |
web: basic cleanup of buttons (#6107)
* web: basic cleanup of buttons
This commit adds Storybook features to the Authentik four-stage button.
The four-stage button is used to:
- trigger an action
- show that the action is running
- show when the action has succeeded, then reset
- show when the action has failed, then reset
It is used mostly for fetching data from the server. The variants are:
- ak-spinner-button: The basic form takes a single property argument, `callAction` a function that
returns a Promise (an asynchronous function).
- ak-action-button: Takes an API request function (which are all asynchronous) and adapts it to the
`callAction`. The only difference in behavior with the Spinner button is that on failure the error
message will be displayed by a notification.
- ak-token-copy-button: A specialized button that, on success, pushes the content of the retrieved
object into the clipboard.
Cleanup consisted of:
- removing a lot of the in-line code from the HTML, decluttering it and making more explicit what
the behaviors of each button type are on success and on failure.
- Replacing the ad-hoc Promise management with Lit's own `Task` handler. The `Task` handler knows
how to notify a Lit-Element of its own internal state change, making it ideal for objects like
this button that need to change their appearance as a Promise'd task progresses from idle →
running → (success or failure).
- Providing JSDoc strings for all of the properties, slots, attributes, elements, and events.
- Adding 'pointer-events: none' during the running phases of the action, to prevent the user from
clicking the button multiple times and launching multiple queries.
- Emitting an event for every stage of the operation:
- `ak-button-click` when the button is clicked.
- `ak-button-success` when the action completes. The payload is included in `Event.detail.result`
- `ak-button-failure` when the action fails. The error message is included in `Event.detail.error`
- `ak-button-reset` when the button completes a notification and goes back to idle
**Storybook**
Since the API requests for both `ak-spinner-button` and `ak-action-button` require only that a
promise be returned, Storybooking them was straightforward. `ak-token-copy-button` is a
special-purpose derivative with an internal functionality that can't be easily mocked (yet), so
there's no Storybook for it.
All of the stories provide the required asynchronous function, in this cose one that waits three
seconds before emitting either a `response` or `reject` Promise.
`ak-action-button`'s Story has event handler code so that pressing on the button will result in a
message being written to a display block under the button.
I've added a new pair of class mixins, `CustomEmitterElement` and `CustomListenerElement`. These
each add an additional method to the classes they're mixed into; one provides a very easy way to
emit a custom event and one provides a way to receive the custom event while sweeping all of the
custom event type handling under the rug.
`emitCustomEvent` replaces this:
``` JavaScript
this.dispatchEvent(
new CustomEvent('ak-button-click', {
composed: true,
bubbles: true,
detail: {
target: this,
result: "Some result, huh?"
},
})
);
```
... with this:
``` JavaScript
this.dispatchCustomEvent('ak-button-click', { result: "Some result, huh?" });
```
The `CustomListenerElement` handler just ensures that the handler being passed to it takes a
CustomEvent, and then makes sure that any actual event passed to the handler has been type-guarded
to ensure it is a custom event.
**Observations**
*Composition vs Inheritance, Part 1*
The four-state button has three implementations. All three inherit from `BaseTaskButton`:
- `spinner`
- provides a default `callAction()`
- `action`
- provides a different name for `callAction`
- overrides `onError` to display a Notification.
- `token-copy`
- provides a custom `callAction`
- overrides `onSuccess` to copy the results to the keyboard
- overrides `onError` to display a Notification, with special handling for asynchronous
processing.
The *results* of all of these could be handled higher up as event handlers, and the button could be
just a thing that displays the states. As it is, the BaseStateToken has only one reason to change
(the Promise changes its state), so I'm satisfied that this is a suitable evolution of the product,
and that it does what it says it does.
*Developer Ergonomics*
The one thing that stands out to me time and again is just how *confusing* all of the Patternfly
stuff tends to be; not because it's not logical, but because it overwhelms the human 7±2 ability to
remember details like this without any imperative to memorize all of them. I would like to get them
under control by marshalling them under a semantic CSS regime, but I'm blocked by some basic
disconnects in the current development environment. We can't shake out the CSS as much as we'd like
because there's no ESPrima equivalent for Typescript, and the smallest bundle purgeCSS is capable of
making for just *one* button is about 55KB. That's a bit too much. It's a great system for getting
off the ground, but long-term it needs more love than we (can) give it.
* Prettier has opinions.
* Removed extraneous debugging code.
* Added comments to the BaseTaskButton parent class.
* web: fixed two build errors (typing) in the stories.
* web: prettier's got opinions
* web: refactor the buttons
This commit adds URL mocking to Storybook, which in turn allows us to
commit a Story for ak-token-copy-button.
I have confirmed that the button's algorithm for writing to the
clipboard works on Safari, Chrome, and Firefox. I don't know
what's up with IE.
* ONE BYTE in .storybook/main blocked integration.
With the repair of lit-analyze, it's time to fix the rule set
to at least let us pass for the moment.
* Still looking for the list of exceptions in lit-analyze that will let us pass once more.
* web: repair error in EnterpriseLicenseForm
This commit continues to find the right configuration for
lit-analyze. During the course of this repair, I discovered
a bug in the EnterpriseLicenseForm; the original usage could
result in the _string_ `undefined` being passed back as a
value. To handle the case where the value truly is undefined,
the `ifDefined()` directive must be used in the HTML template.
I have also instituted a case-by-case stylistic decision to allow
the HTML, and only the HTML, to be longer that 100 characters
when doing so reduces the visual "noise" of a function.
|