mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 14:23:32 +00:00
[CL-738] Migrate disclosure component (#17206)
This commit is contained in:
@@ -428,7 +428,7 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
|
||||
|
||||
await this.vaultPopupSectionService.updateSectionOpenStoredState(
|
||||
this.collapsibleKey()!,
|
||||
this.disclosure.open,
|
||||
this.disclosure.open(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
import { Directive, HostBinding, HostListener, input } from "@angular/core";
|
||||
import { Directive, computed, input } from "@angular/core";
|
||||
|
||||
import { DisclosureComponent } from "./disclosure.component";
|
||||
|
||||
/**
|
||||
* Directive that connects a trigger element (like a button) to a disclosure component.
|
||||
* Automatically handles click events to toggle the disclosure open/closed state and
|
||||
* manages ARIA attributes for accessibility.
|
||||
*/
|
||||
@Directive({
|
||||
selector: "[bitDisclosureTriggerFor]",
|
||||
exportAs: "disclosureTriggerFor",
|
||||
host: {
|
||||
"[attr.aria-expanded]": "ariaExpanded()",
|
||||
"[attr.aria-controls]": "ariaControls()",
|
||||
"(click)": "toggle()",
|
||||
},
|
||||
})
|
||||
export class DisclosureTriggerForDirective {
|
||||
/**
|
||||
@@ -12,15 +22,11 @@ export class DisclosureTriggerForDirective {
|
||||
*/
|
||||
readonly disclosure = input.required<DisclosureComponent>({ alias: "bitDisclosureTriggerFor" });
|
||||
|
||||
@HostBinding("attr.aria-expanded") get ariaExpanded() {
|
||||
return this.disclosure().open;
|
||||
}
|
||||
protected readonly ariaExpanded = computed(() => this.disclosure().open());
|
||||
|
||||
@HostBinding("attr.aria-controls") get ariaControls() {
|
||||
return this.disclosure().id;
|
||||
}
|
||||
protected readonly ariaControls = computed(() => this.disclosure().id);
|
||||
|
||||
@HostListener("click") click() {
|
||||
this.disclosure().open = !this.disclosure().open;
|
||||
protected toggle() {
|
||||
this.disclosure().open.update((open) => !open);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
HostBinding,
|
||||
Input,
|
||||
Output,
|
||||
booleanAttribute,
|
||||
} from "@angular/core";
|
||||
import { ChangeDetectionStrategy, Component, computed, model } from "@angular/core";
|
||||
|
||||
let nextId = 0;
|
||||
|
||||
/**
|
||||
* The `bit-disclosure` component is used in tandem with the `bitDisclosureTriggerFor` directive to create an accessible content area whose visibility is controlled by a trigger button.
|
||||
|
||||
*
|
||||
* To compose a disclosure and trigger:
|
||||
|
||||
*
|
||||
* 1. Create a trigger component (see "Supported Trigger Components" section below)
|
||||
* 2. Create a `bit-disclosure`
|
||||
* 3. Set a template reference on the `bit-disclosure`
|
||||
@@ -26,45 +19,32 @@ let nextId = 0;
|
||||
* <button
|
||||
* type="button"
|
||||
* bitIconButton="bwi-sliders"
|
||||
* [buttonType]="'muted'"
|
||||
* buttonType="muted"
|
||||
* [bitDisclosureTriggerFor]="disclosureRef"
|
||||
* [label]="'Settings' | i18n"
|
||||
* ></button>
|
||||
* <bit-disclosure #disclosureRef open>click button to hide this content</bit-disclosure>
|
||||
* <bit-disclosure #disclosureRef [(open)]="isOpen">click button to hide this content</bit-disclosure>
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
|
||||
@Component({
|
||||
selector: "bit-disclosure",
|
||||
template: `<ng-content></ng-content>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: {
|
||||
"[class]": "classList()",
|
||||
"[id]": "id",
|
||||
},
|
||||
})
|
||||
export class DisclosureComponent {
|
||||
/** Emits the visibility of the disclosure content */
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref
|
||||
@Output() openChange = new EventEmitter<boolean>();
|
||||
|
||||
private _open?: boolean;
|
||||
/**
|
||||
* Optionally init the disclosure in its opened state
|
||||
* Controls the visibility of the disclosure content.
|
||||
*/
|
||||
// TODO: Skipped for signal migration because:
|
||||
// Accessor inputs cannot be migrated as they are too complex.
|
||||
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
|
||||
// eslint-disable-next-line @angular-eslint/prefer-signals
|
||||
@Input({ transform: booleanAttribute }) set open(isOpen: boolean) {
|
||||
this._open = isOpen;
|
||||
this.openChange.emit(isOpen);
|
||||
}
|
||||
get open(): boolean {
|
||||
return !!this._open;
|
||||
}
|
||||
readonly open = model<boolean>(false);
|
||||
|
||||
@HostBinding("class") get classList() {
|
||||
return this.open ? "" : "tw-hidden";
|
||||
}
|
||||
/**
|
||||
* Autogenerated id.
|
||||
*/
|
||||
readonly id = `bit-disclosure-${nextId++}`;
|
||||
|
||||
@HostBinding("id") id = `bit-disclosure-${nextId++}`;
|
||||
protected readonly classList = computed(() => (this.open() ? "" : "tw-hidden"));
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { DisclosureComponent, DisclosureTriggerForDirective } from "@bitwarden/c
|
||||
<Title />
|
||||
<Description />
|
||||
|
||||
<Canvas of={stories.DisclosureWithIconButton} />
|
||||
<Canvas of={stories.DisclosureOpen} />
|
||||
|
||||
## Supported Trigger Components
|
||||
|
||||
|
||||
@@ -36,13 +36,30 @@ export default {
|
||||
|
||||
type Story = StoryObj<DisclosureComponent>;
|
||||
|
||||
export const DisclosureWithIconButton: Story = {
|
||||
export const DisclosureOpen: Story = {
|
||||
args: {
|
||||
open: true,
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: /*html*/ `
|
||||
<button type="button" label="Settings" bitIconButton="bwi-sliders" [buttonType]="'muted'" [bitDisclosureTriggerFor]="disclosureRef">
|
||||
<button type="button" label="Settings" bitIconButton="bwi-sliders" buttonType="muted" [bitDisclosureTriggerFor]="disclosureRef">
|
||||
</button>
|
||||
<bit-disclosure #disclosureRef class="tw-text-main tw-block" open>click button to hide this content</bit-disclosure>
|
||||
<bit-disclosure #disclosureRef class="tw-text-main tw-block" [(open)]="open">click button to hide this content</bit-disclosure>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
export const DisclosureClosed: Story = {
|
||||
args: {
|
||||
open: false,
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: /*html*/ `
|
||||
<button type="button" label="Settings" bitIconButton="bwi-sliders" buttonType="muted" [bitDisclosureTriggerFor]="disclosureRef">
|
||||
</button>
|
||||
<bit-disclosure #disclosureRef class="tw-text-main tw-block" [(open)]="open">click button to hide this content</bit-disclosure>
|
||||
`,
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user