mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 17:53:39 +00:00
[CL-927] anon layout header actions slot (#17796)
* add a slot for consumers to show user actions in anon layout header * remove commented code * ensure logo stays top aligned * switch to dashed naming * fix ngif statements * remove empty selector * remove unnecessary containers * use smaller logo on smaller screens * remove commented code from extension layout * remove dupe slot * only take extension screenshots on small screens * take screenshot at 380 * take large and small screenshot * update story to use new control flow
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
[hideCardWrapper]="hideCardWrapper"
|
||||
[hideBackgroundIllustration]="hideBackgroundIllustration"
|
||||
>
|
||||
<router-outlet slot="header-actions" name="header-actions"></router-outlet>
|
||||
<router-outlet></router-outlet>
|
||||
<router-outlet slot="secondary" name="secondary"></router-outlet>
|
||||
<router-outlet slot="environment-selector" name="environment-selector"></router-outlet>
|
||||
|
||||
@@ -130,6 +130,15 @@ export class DefaultSecondaryOutletExampleComponent {}
|
||||
})
|
||||
export class DefaultEnvSelectorOutletExampleComponent {}
|
||||
|
||||
// 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-header-actions-outlet-example-component",
|
||||
template: "<p>Header Actions Outlet Example: <br> your header actions component goes here</p>",
|
||||
standalone: false,
|
||||
})
|
||||
export class DefaultHeaderActionsOutletExampleComponent {}
|
||||
|
||||
export const DefaultContentExample: Story = {
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
@@ -171,6 +180,11 @@ export const DefaultContentExample: Story = {
|
||||
component: DefaultEnvSelectorOutletExampleComponent,
|
||||
outlet: "environment-selector",
|
||||
},
|
||||
{
|
||||
path: "",
|
||||
component: DefaultHeaderActionsOutletExampleComponent,
|
||||
outlet: "header-actions",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -5,13 +5,19 @@
|
||||
'tw-min-h-full': clientType === 'browser' || clientType === 'desktop',
|
||||
}"
|
||||
>
|
||||
<a
|
||||
*ngIf="!hideLogo()"
|
||||
[routerLink]="['/']"
|
||||
class="tw-w-[200px] tw-block tw-mb-12 [&>*]:tw-align-top"
|
||||
>
|
||||
<bit-icon [icon]="logo" [ariaLabel]="'appLogoLabel' | i18n"></bit-icon>
|
||||
</a>
|
||||
<div class="tw-flex tw-justify-between tw-items-center tw-w-full tw-mb-12">
|
||||
@if (!hideLogo()) {
|
||||
<a
|
||||
[routerLink]="['/']"
|
||||
class="tw-w-32 sm:tw-w-[200px] tw-self-center sm:tw-self-start tw-block [&>*]:tw-align-top"
|
||||
>
|
||||
<bit-icon [icon]="logo" [ariaLabel]="'appLogoLabel' | i18n"></bit-icon>
|
||||
</a>
|
||||
}
|
||||
<div class="tw-ms-auto">
|
||||
<ng-content select="[slot=header-actions]"></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tw-text-center tw-mb-4 sm:tw-mb-6 tw-mx-auto" [ngClass]="maxWidthClass">
|
||||
@let iconInput = icon();
|
||||
@@ -25,7 +31,7 @@
|
||||
<bit-icon [icon]="iconInput"></bit-icon>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="title()">
|
||||
@if (title()) {
|
||||
<!-- Small screens -->
|
||||
<h1 bitTypography="h2" class="tw-mt-2 sm:tw-hidden">
|
||||
{{ title() }}
|
||||
@@ -34,9 +40,11 @@
|
||||
<h1 bitTypography="h1" class="tw-mt-2 tw-hidden sm:tw-block">
|
||||
{{ title() }}
|
||||
</h1>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<div *ngIf="subtitle()" class="tw-text-sm sm:tw-text-base">{{ subtitle() }}</div>
|
||||
@if (subtitle()) {
|
||||
<div class="tw-text-sm sm:tw-text-base">{{ subtitle() }}</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -57,18 +65,20 @@
|
||||
<ng-content select="[slot=secondary]"></ng-content>
|
||||
</div>
|
||||
|
||||
<footer *ngIf="!hideFooter()" class="tw-text-center tw-mt-4 sm:tw-mt-6">
|
||||
<div *ngIf="showReadonlyHostname()" bitTypography="body2">
|
||||
{{ "accessing" | i18n }} {{ hostname }}
|
||||
</div>
|
||||
<ng-container *ngIf="!showReadonlyHostname()">
|
||||
<ng-content select="[slot=environment-selector]"></ng-content>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!hideYearAndVersion">
|
||||
<div bitTypography="body2">© {{ year }} Bitwarden Inc.</div>
|
||||
<div bitTypography="body2">{{ version }}</div>
|
||||
</ng-container>
|
||||
</footer>
|
||||
@if (!hideFooter()) {
|
||||
<footer class="tw-text-center tw-mt-4 sm:tw-mt-6">
|
||||
@if (showReadonlyHostname()) {
|
||||
<div bitTypography="body2">{{ "accessing" | i18n }} {{ hostname }}</div>
|
||||
} @else {
|
||||
<ng-content select="[slot=environment-selector]"></ng-content>
|
||||
}
|
||||
|
||||
@if (!hideYearAndVersion) {
|
||||
<div bitTypography="body2">© {{ year }} Bitwarden Inc.</div>
|
||||
<div bitTypography="body2">{{ version }}</div>
|
||||
}
|
||||
</footer>
|
||||
}
|
||||
|
||||
@if (!hideBackgroundIllustration()) {
|
||||
<div
|
||||
|
||||
@@ -8,6 +8,7 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
|
||||
import { AvatarModule } from "../avatar";
|
||||
import { ButtonModule } from "../button";
|
||||
import { I18nMockService } from "../utils/i18n-mock.service";
|
||||
|
||||
@@ -23,6 +24,7 @@ type StoryArgs = AnonLayoutComponent & {
|
||||
showSecondary: boolean;
|
||||
useDefaultIcon: boolean;
|
||||
icon: Icon;
|
||||
includeHeaderActions: boolean;
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -30,7 +32,7 @@ export default {
|
||||
component: AnonLayoutComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [ButtonModule, RouterModule],
|
||||
imports: [ButtonModule, RouterModule, AvatarModule],
|
||||
providers: [
|
||||
{
|
||||
provide: PlatformUtilsService,
|
||||
@@ -76,6 +78,14 @@ export default {
|
||||
[hideFooter]="hideFooter"
|
||||
[hideBackgroundIllustration]="hideBackgroundIllustration"
|
||||
>
|
||||
@if (includeHeaderActions) {
|
||||
<div slot="header-actions" class="tw-flex tw-items-center tw-gap-2">
|
||||
<bit-avatar
|
||||
size="small"
|
||||
text="Bob Loblaw"
|
||||
></bit-avatar>
|
||||
</div>
|
||||
}
|
||||
<ng-container [ngSwitch]="contentLength">
|
||||
<div *ngSwitchCase="'thin'" class="tw-text-center"> <div class="tw-font-medium">Thin Content</div></div>
|
||||
<div *ngSwitchCase="'long'">
|
||||
@@ -116,7 +126,7 @@ export default {
|
||||
hideLogo: { control: "boolean" },
|
||||
hideFooter: { control: "boolean" },
|
||||
hideBackgroundIllustration: { control: "boolean" },
|
||||
|
||||
includeHeaderActions: { control: "boolean" },
|
||||
contentLength: {
|
||||
control: "radio",
|
||||
options: ["normal", "long", "thin"],
|
||||
@@ -138,6 +148,7 @@ export default {
|
||||
hideBackgroundIllustration: false,
|
||||
contentLength: "normal",
|
||||
showSecondary: false,
|
||||
includeHeaderActions: false,
|
||||
},
|
||||
} satisfies Meta<StoryArgs>;
|
||||
|
||||
@@ -188,6 +199,12 @@ export const SecondaryContent: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const WithHeaderActions: Story = {
|
||||
args: {
|
||||
includeHeaderActions: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const NoTitle: Story = { args: { title: undefined } };
|
||||
|
||||
export const NoSubtitle: Story = { args: { subtitle: undefined } };
|
||||
|
||||
Reference in New Issue
Block a user