Skip to content

Commit

Permalink
web: Make viewing the in-page documentation optional
Browse files Browse the repository at this point in the history
After talking with @GirlBossRush, I realized that my architecture would be simplified by separating
the style controller from the renderer.  Lit uses Controllers just for that purpose, so:

1. A renderer that either puts down the button or puts down the documentation

2. A controller that monitors the button for its state and, when that state changes, updates the
   host's CSS to fit within the page correctly when the button is rotated.

3. Some helper styles the container needs to help the button display correctly.

Run the thing and click on the button.

This changes the look and feel of the application to a small degree. Screenshots may need to be
updated.

None.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes
  • Loading branch information
kensternberg-authentik committed Jan 24, 2025
1 parent 2ddb7e1 commit 977e73b
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 48 deletions.
27 changes: 8 additions & 19 deletions web/src/admin/applications/ApplicationListPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import MDApplication from "@goauthentik/docs/add-secure-apps/applications/index.
import "@goauthentik/elements/AppIcon.js";
import { WithBrandConfig } from "@goauthentik/elements/Interface/brandProvider";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/SidebarHelpToggle.js";
import "@goauthentik/elements/SidebarHelp/SidebarHelp.js";
import { SidebarHelpController } from "@goauthentik/elements/SidebarHelp/SidebarHelpController.js";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/DeleteBulkForm";
import "@goauthentik/elements/forms/ModalForm";
Expand All @@ -19,6 +20,7 @@ import { CSSResult, TemplateResult, css, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";

import SidebarHelp from "@goauthentik/elements/SidebarHelp/SidebarHelp.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";

import { Application, CoreApi } from "@goauthentik/api";
Expand All @@ -39,21 +41,6 @@ export const applicationListStyle = css`
.pf-c-sidebar.pf-m-gutter > .pf-c-sidebar__main > * + * {
margin-left: calc(var(--pf-c-sidebar__main--child--MarginLeft) / 2);
}
ak-sidebar-help-toggle {
display: none;
}
@media screen and (min-width: 768px) {
ak-sidebar-help-toggle {
display: block;
}
}
ak-sidebar-help-toggle.pf-m-width-default {
background-color: inherit;
max-width: 3rem;
}
`;

@customElement("ak-application-list")
Expand All @@ -79,6 +66,8 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
@property()
order = "name";

sidebarHelpController = new SidebarHelpController(this);

async apiEndpoint(): Promise<PaginatedResponse<Application>> {
return new CoreApi(DEFAULT_CONFIG).coreApplicationsList({
...(await this.defaultEndpointConfig()),
Expand All @@ -87,7 +76,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
}

static get styles(): CSSResult[] {
return super.styles.concat(PFCard, applicationListStyle);
return super.styles.concat(PFCard, SidebarHelp, applicationListStyle);
}

columns(): TableColumn[] {
Expand All @@ -106,12 +95,12 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
}

renderSidebarAfter(): TemplateResult {
return html`<ak-sidebar-help-toggle
return html`<ak-sidebar-help
label=${msg("Applications Documentation")}
.content=${MDApplication}
class="pf-c-sidebar__panel"
active-style="pf-m-width-25"
></ak-sidebar-help-toggle>`;
></ak-sidebar-help>`;
}

renderToolbarSelected(): TemplateResult {
Expand Down
5 changes: 5 additions & 0 deletions web/src/elements/SidebarHelp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
The SidebarHelp feature uses some fairly fiddly CSS to rotate the "Documentation" button in a way
that doesn't take up too much screen real-estate, because the rotation is purely visual; the layout
flow is still driven by the size of the button as if it were horizontal. Using the SidebarHelp means
enabling a special controller to adjust the width of the container to the _height_ of the button
when the button is rotated into place.
14 changes: 14 additions & 0 deletions web/src/elements/SidebarHelp/SidebarHelp.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ak-sidebar-help-toggle {
display: none;
}

@media screen and (min-width: 768px) {
ak-sidebar-help-toggle {
display: block;
}
}

ak-sidebar-help-toggle.pf-m-width-default {
background-color: inherit;
max-width: 3rem;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/Markdown";
import { bound } from "@goauthentik/elements/decorators/bound";

import { msg } from "@lit/localize";
import { css, html } from "lit";
Expand All @@ -11,10 +12,28 @@ import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
import PFFlex from "@patternfly/patternfly/utilities/Flex/flex.css";
import PFSpacing from "@patternfly/patternfly/utilities/Spacing/spacing.css";

import { bound } from "./decorators/bound";
import { SidebarHelpToggleEvent } from "./events.js";

@customElement("ak-sidebar-help-toggle")
export class ToggledSidebarHelp extends AKElement {
/**
* A "Display documentation for this page" element.
*
* Based on the Patternfly "sidebar" pattern, this shows a vertically rotated button with a label to
* indicate that it leads to documentation; when pressed, the button is replaced with the
* documentation, rendered as a Markdown document.
*
* The SidebarHelp feature uses some fairly fiddly CSS to rotate the "Documentation" button in a way
* that doesn't take up too much screen real-estate, because the rotation is purely visual; the
* layout flow is still driven by the size of the button as if it were horizontal. Using the
* SidebarHelp means enabling a special SidebarHelpController on the container to adjust the width
* of the container to the *height* of the button when the button is rotated into place.
*
* @element ak-sidebar-help
*
* The events fired by this component are not for general use.
*/

@customElement("ak-sidebar-help")
export class SidebarHelp extends AKElement {
static get styles() {
return [
PFCard,
Expand All @@ -25,8 +44,8 @@ export class ToggledSidebarHelp extends AKElement {
css`
.vert {
transform-origin: bottom left;
rotate: 90deg;
translate: 0 -100%;
rotate: 90deg;
}
.ak-fit-text {
width: fit-content;
Expand All @@ -35,12 +54,21 @@ export class ToggledSidebarHelp extends AKElement {
];
}

/*
* @attr The content of the documentation to be shown
*/
@property({ attribute: false })
content: string = "";

/*
* @attr The style to use when the content is visible
*/
@property({ attribute: "active-style" })
activeStyle = "pf-m-width-25";

/*
* @attr The label on the button when the content is not visible.
*/
@property()
label: string = msg("Documentation");

Expand All @@ -56,16 +84,7 @@ export class ToggledSidebarHelp extends AKElement {
}

render() {
// The eslint-disable commands are necessary because we're sending signals up the stack that
// the component's dimensions are being set in a very specific and concrete way. This is
// probably not a good use; it violates the principle that a parent class ought to dictate
// the layout of its components.

if (!this.showing) {
// eslint-disable-next-line wc/no-self-class
this.classList.remove(this.activeStyle);
// eslint-disable-next-line wc/no-self-class
this.classList.add("pf-m-width-default");
return html`<button
type="button"
id="toggle"
Expand All @@ -76,10 +95,6 @@ export class ToggledSidebarHelp extends AKElement {
</button>`;
}

// eslint-disable-next-line wc/no-self-class
this.classList.remove("pf-m-width-default");
// eslint-disable-next-line wc/no-self-class
this.classList.add(this.activeStyle);
return html`
<div class="pf-c-card">
<div class="pf-u-display-flex pf-u-justify-content-flex-end">
Expand All @@ -99,17 +114,6 @@ export class ToggledSidebarHelp extends AKElement {
}

updated() {
// Setting this up after a `requestAnimationFrame` means the button's dimensions are
// well-calculated, and the space the button needs can be adjusted accordingly.
requestAnimationFrame(() => {
if (this.showing) {
this.style.removeProperty("width");
} else {
if (this.button) {
const { width } = this.button.getBoundingClientRect();
this.style.setProperty("width", `${width}px`);
}
}
});
this.dispatchEvent(new SidebarHelpToggleEvent(this));
}
}
51 changes: 51 additions & 0 deletions web/src/elements/SidebarHelp/SidebarHelpController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { bound } from "@goauthentik/elements/decorators/bound";

import { LitElement, ReactiveController, ReactiveControllerHost } from "lit";

import { SidebarHelpToggleEvent } from "./events";

type ReactiveLitElement = LitElement & ReactiveControllerHost;

const DEFAULT_STYLE = "pf-m-width-default";

/**
* A "Display documentation for this page" helper. Attach this controller to any element that
* contains one or more SidebarHelp entries. It adjusts the width of the sidebar controller when
* hidden to that of the button's *height*, since the button has been rotated 90° around a
* corner-oriented axis.
*
* The events consumed by this component are not for general use.
*/

export class SidebarHelpController implements ReactiveController {
host: ReactiveLitElement;

constructor(host: ReactiveLitElement) {
(this.host = host).addController(this);
}

@bound
toggleHelpToggle(ev: SidebarHelpToggleEvent) {
const { source } = ev;
if (!source.showing) {
source.classList.remove(source.activeStyle);
source.classList.add(DEFAULT_STYLE);
const { width } = source.button.getBoundingClientRect();
source.style.setProperty("width", `${width}px`);
return;
}
requestAnimationFrame(() => {
source.style.removeProperty("width");
source.classList.remove(DEFAULT_STYLE);
source.classList.add(source.activeStyle);
});
}

hostConnected() {
this.host.addEventListener(SidebarHelpToggleEvent.eventName, this.toggleHelpToggle);
}

hostDisconnected() {
this.host.removeEventListener(SidebarHelpToggleEvent.eventName, this.toggleHelpToggle);
}
}
16 changes: 16 additions & 0 deletions web/src/elements/SidebarHelp/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { SidebarHelp } from "./SidebarHelp.js";

export class SidebarHelpToggleEvent extends Event {
static readonly eventName = "ak-sidebar-help-toggle-request";
source: SidebarHelp;
constructor(source: SidebarHelp) {
super(SidebarHelpToggleEvent.eventName, { bubbles: true, composed: true });
this.source = source;
}
}

declare global {
interface GlobalEventHandlersEventMap {
[SidebarHelpToggleEvent.eventName]: SidebarHelpToggleEvent;
}
}

0 comments on commit 977e73b

Please sign in to comment.