1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

[BEEEP] [PM-10132] Upgrade storybook to v8 (#10288)

Upgrade storybook to version v8 which is a major upgrade. Storybook provides an
upgrade wizard which did most of the work.

- Ran npx storybook upgrade.
- Manually updated `remark-gfm` since the newer mdx requires v 4.
- Migrated all old stories still using `Story` to `StoryObj`.
This commit is contained in:
Oscar Hinton
2024-08-16 09:28:29 +02:00
committed by GitHub
parent 92f87dad9a
commit 604e22334a
18 changed files with 3875 additions and 8220 deletions

View File

@@ -1,7 +1,7 @@
import { importProvidersFrom } from "@angular/core";
import { FormBuilder, FormsModule, ReactiveFormsModule } from "@angular/forms";
import { action } from "@storybook/addon-actions";
import { applicationConfig, Meta, moduleMetadata, Story } from "@storybook/angular";
import { applicationConfig, Meta, moduleMetadata, StoryObj } from "@storybook/angular";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
@@ -55,6 +55,9 @@ export default {
},
} as Meta;
// TODO: This is a workaround since this story does weird things.
type Story = StoryObj<any>;
const actionsData = {
onValueChanged: action("onValueChanged"),
onSubmit: action("onSubmit"),
@@ -99,9 +102,8 @@ const itemsFactory = (n: number, type: AccessItemType) => {
const sampleMembers = itemsFactory(10, AccessItemType.Member);
const sampleGroups = itemsFactory(6, AccessItemType.Group);
const StandaloneAccessSelectorTemplate: Story<AccessSelectorComponent> = (
args: AccessSelectorComponent,
) => ({
// TODO: These renders are badly handled but storybook has made it more difficult to use multiple renders in a single story.
const StandaloneAccessSelectorRender = (args: any) => ({
props: {
items: [],
valueChanged: actionsData.onValueChanged,
@@ -109,25 +111,23 @@ const StandaloneAccessSelectorTemplate: Story<AccessSelectorComponent> = (
...args,
},
template: `
<bit-access-selector
(ngModelChange)="valueChanged($event)"
[ngModel]="initialValue"
[items]="items"
[disabled]="disabled"
[columnHeader]="columnHeader"
[showGroupColumn]="showGroupColumn"
[selectorLabelText]="selectorLabelText"
[selectorHelpText]="selectorHelpText"
[emptySelectionText]="emptySelectionText"
[permissionMode]="permissionMode"
[showMemberRoles]="showMemberRoles"
></bit-access-selector>
`,
<bit-access-selector
(ngModelChange)="valueChanged($event)"
[ngModel]="initialValue"
[items]="items"
[disabled]="disabled"
[columnHeader]="columnHeader"
[showGroupColumn]="showGroupColumn"
[selectorLabelText]="selectorLabelText"
[selectorHelpText]="selectorHelpText"
[emptySelectionText]="emptySelectionText"
[permissionMode]="permissionMode"
[showMemberRoles]="showMemberRoles"
></bit-access-selector>
`,
});
const DialogAccessSelectorTemplate: Story<AccessSelectorComponent> = (
args: AccessSelectorComponent,
) => ({
const DialogAccessSelectorRender = (args: any) => ({
props: {
items: [],
valueChanged: actionsData.onValueChanged,
@@ -164,7 +164,7 @@ const DialogAccessSelectorTemplate: Story<AccessSelectorComponent> = (
aria-label="Delete"></button>
</ng-container>
</bit-dialog>
`,
`,
});
const dialogAccessItems = itemsFactory(10, AccessItemType.Collection);
@@ -190,153 +190,115 @@ const memberCollectionAccessItems = itemsFactory(3, AccessItemType.Collection).c
},
]);
export const Dialog = DialogAccessSelectorTemplate.bind({});
Dialog.args = {
permissionMode: "edit",
showMemberRoles: false,
showGroupColumn: true,
columnHeader: "Collection",
selectorLabelText: "Select Collections",
selectorHelpText: "Some helper text describing what this does",
emptySelectionText: "No collections added",
disabled: false,
initialValue: [],
items: dialogAccessItems,
};
Dialog.story = {
parameters: {
docs: {
storyDescription: `
Example of an access selector for modifying the collections a member has access to inside of a dialog.
`,
},
export const Dialog: Story = {
args: {
permissionMode: "edit",
showMemberRoles: false,
showGroupColumn: true,
columnHeader: "Collection",
selectorLabelText: "Select Collections",
selectorHelpText: "Some helper text describing what this does",
emptySelectionText: "No collections added",
disabled: false,
initialValue: [] as any[],
items: dialogAccessItems,
},
render: DialogAccessSelectorRender,
};
export const MemberCollectionAccess = StandaloneAccessSelectorTemplate.bind({});
MemberCollectionAccess.args = {
permissionMode: "edit",
showMemberRoles: false,
showGroupColumn: true,
columnHeader: "Collection",
selectorLabelText: "Select Collections",
selectorHelpText: "Some helper text describing what this does",
emptySelectionText: "No collections added",
disabled: false,
initialValue: [],
items: memberCollectionAccessItems,
};
MemberCollectionAccess.story = {
parameters: {
docs: {
storyDescription: `
Example of an access selector for modifying the collections a member has access to.
Includes examples of a readonly group and member that cannot be edited.
`,
},
export const MemberCollectionAccess: Story = {
args: {
permissionMode: "edit",
showMemberRoles: false,
showGroupColumn: true,
columnHeader: "Collection",
selectorLabelText: "Select Collections",
selectorHelpText: "Some helper text describing what this does",
emptySelectionText: "No collections added",
disabled: false,
initialValue: [],
items: memberCollectionAccessItems,
},
render: StandaloneAccessSelectorRender,
};
export const MemberGroupAccess = StandaloneAccessSelectorTemplate.bind({});
MemberGroupAccess.args = {
permissionMode: "readonly",
showMemberRoles: false,
columnHeader: "Groups",
selectorLabelText: "Select Groups",
selectorHelpText: "Some helper text describing what this does",
emptySelectionText: "No groups added",
disabled: false,
initialValue: [{ id: "3g" }, { id: "0g" }],
items: itemsFactory(4, AccessItemType.Group).concat([
{
id: "admin",
type: AccessItemType.Group,
listName: "Admin Group",
labelName: "Admin Group",
},
]),
};
MemberGroupAccess.story = {
parameters: {
docs: {
storyDescription: `
Example of an access selector for selecting which groups an individual member belongs too.
`,
},
export const MemberGroupAccess: Story = {
args: {
permissionMode: "readonly",
showMemberRoles: false,
columnHeader: "Groups",
selectorLabelText: "Select Groups",
selectorHelpText: "Some helper text describing what this does",
emptySelectionText: "No groups added",
disabled: false,
initialValue: [{ id: "3g" }, { id: "0g" }],
items: itemsFactory(4, AccessItemType.Group).concat([
{
id: "admin",
type: AccessItemType.Group,
listName: "Admin Group",
labelName: "Admin Group",
},
]),
},
render: StandaloneAccessSelectorRender,
};
export const GroupMembersAccess = StandaloneAccessSelectorTemplate.bind({});
GroupMembersAccess.args = {
permissionMode: "hidden",
showMemberRoles: true,
columnHeader: "Members",
selectorLabelText: "Select Members",
selectorHelpText: "Some helper text describing what this does",
emptySelectionText: "No members added",
disabled: false,
initialValue: [{ id: "2m" }, { id: "0m" }],
items: sampleMembers,
};
GroupMembersAccess.story = {
parameters: {
docs: {
storyDescription: `
Example of an access selector for selecting which members belong to an specific group.
`,
},
export const GroupMembersAccess: Story = {
args: {
permissionMode: "hidden",
showMemberRoles: true,
columnHeader: "Members",
selectorLabelText: "Select Members",
selectorHelpText: "Some helper text describing what this does",
emptySelectionText: "No members added",
disabled: false,
initialValue: [{ id: "2m" }, { id: "0m" }],
items: sampleMembers,
},
render: StandaloneAccessSelectorRender,
};
export const CollectionAccess = StandaloneAccessSelectorTemplate.bind({});
CollectionAccess.args = {
permissionMode: "edit",
showMemberRoles: false,
columnHeader: "Groups/Members",
selectorLabelText: "Select groups and members",
selectorHelpText:
"Permissions set for a member will replace permissions set by that member's group",
emptySelectionText: "No members or groups added",
disabled: false,
initialValue: [
{ id: "3g", permission: CollectionPermission.EditExceptPass },
{ id: "0m", permission: CollectionPermission.View },
],
items: sampleGroups.concat(sampleMembers).concat([
{
id: "admin-group",
type: AccessItemType.Group,
listName: "Admin Group",
labelName: "Admin Group",
readonly: true,
},
{
id: "admin-member",
type: AccessItemType.Member,
listName: "Admin Member (admin@email.com)",
labelName: "Admin Member",
status: OrganizationUserStatusType.Confirmed,
role: OrganizationUserType.Admin,
email: "admin@email.com",
readonly: true,
},
]),
};
GroupMembersAccess.story = {
parameters: {
docs: {
storyDescription: `
Example of an access selector for selecting which members/groups have access to a specific collection.
`,
},
export const CollectionAccess: Story = {
args: {
permissionMode: "edit",
showMemberRoles: false,
columnHeader: "Groups/Members",
selectorLabelText: "Select groups and members",
selectorHelpText:
"Permissions set for a member will replace permissions set by that member's group",
emptySelectionText: "No members or groups added",
disabled: false,
initialValue: [
{ id: "3g", permission: CollectionPermission.EditExceptPass },
{ id: "0m", permission: CollectionPermission.View },
],
items: sampleGroups.concat(sampleMembers).concat([
{
id: "admin-group",
type: AccessItemType.Group,
listName: "Admin Group",
labelName: "Admin Group",
readonly: true,
},
{
id: "admin-member",
type: AccessItemType.Member,
listName: "Admin Member (admin@email.com)",
labelName: "Admin Member",
status: OrganizationUserStatusType.Confirmed,
role: OrganizationUserType.Admin,
email: "admin@email.com",
readonly: true,
},
]),
},
render: StandaloneAccessSelectorRender,
};
const fb = new FormBuilder();
const ReactiveFormAccessSelectorTemplate: Story<AccessSelectorComponent> = (
args: AccessSelectorComponent,
) => ({
const ReactiveFormAccessSelectorRender = (args: any) => ({
props: {
items: [],
onSubmit: actionsData.onSubmit,
@@ -344,30 +306,32 @@ const ReactiveFormAccessSelectorTemplate: Story<AccessSelectorComponent> = (
},
template: `
<form [formGroup]="formObj" (ngSubmit)="onSubmit(formObj.controls.formItems.value)">
<bit-access-selector
formControlName="formItems"
[items]="items"
[columnHeader]="columnHeader"
[selectorLabelText]="selectorLabelText"
[selectorHelpText]="selectorHelpText"
[emptySelectionText]="emptySelectionText"
[permissionMode]="permissionMode"
[showMemberRoles]="showMemberRoles"
></bit-access-selector>
<button type="submit" bitButton buttonType="primary" class="tw-mt-5">Submit</button>
<bit-access-selector
formControlName="formItems"
[items]="items"
[columnHeader]="columnHeader"
[selectorLabelText]="selectorLabelText"
[selectorHelpText]="selectorHelpText"
[emptySelectionText]="emptySelectionText"
[permissionMode]="permissionMode"
[showMemberRoles]="showMemberRoles"
></bit-access-selector>
<button type="submit" bitButton buttonType="primary" class="tw-mt-5">Submit</button>
</form>
`,
});
export const ReactiveForm = ReactiveFormAccessSelectorTemplate.bind({});
ReactiveForm.args = {
formObj: fb.group({ formItems: [[{ id: "1g" }]] }),
permissionMode: "edit",
showMemberRoles: false,
columnHeader: "Groups/Members",
selectorLabelText: "Select groups and members",
selectorHelpText:
"Permissions set for a member will replace permissions set by that member's group",
emptySelectionText: "No members or groups added",
items: sampleGroups.concat(sampleMembers),
export const ReactiveForm: Story = {
args: {
formObj: fb.group({ formItems: [[{ id: "1g" }]] }),
permissionMode: "edit",
showMemberRoles: false,
columnHeader: "Groups/Members",
selectorLabelText: "Select groups and members",
selectorHelpText:
"Permissions set for a member will replace permissions set by that member's group",
emptySelectionText: "No members or groups added",
items: sampleGroups.concat(sampleMembers),
},
render: ReactiveFormAccessSelectorRender,
};

View File

@@ -1,6 +1,6 @@
import { importProvidersFrom } from "@angular/core";
import { RouterModule } from "@angular/router";
import { Meta, Story, applicationConfig, moduleMetadata } from "@storybook/angular";
import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
import { delay, of, startWith } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
@@ -26,60 +26,61 @@ export default {
],
}),
],
render: (args) => ({
props: {
createServiceAccount: false,
importSecrets$: of(false),
createSecret: false,
createProject: false,
...args,
},
template: `
<app-onboarding title="Get started">
<app-onboarding-task
[title]="'createMachineAccount' | i18n"
icon="bwi-cli"
[completed]="createServiceAccount"
>
<span>
{{ "downloadThe" | i18n }} <a bitLink routerLink="">{{ "smCLI" | i18n }}</a>
</span>
</app-onboarding-task>
<app-onboarding-task
[title]="'createProject' | i18n"
icon="bwi-collection"
[completed]="createProject"
></app-onboarding-task>
<app-onboarding-task
[title]="'importSecrets' | i18n"
icon="bwi-download"
[completed]="importSecrets$ | async"
></app-onboarding-task>
<app-onboarding-task
[title]="'createSecret' | i18n"
icon="bwi-key"
[completed]="createSecret"
></app-onboarding-task>
</app-onboarding>
`,
}),
} as Meta;
const Template: Story = (args) => ({
props: {
createServiceAccount: false,
importSecrets$: of(false),
createSecret: false,
createProject: false,
...args,
type Story = StoryObj<OnboardingComponent>;
export const Empty: Story = {};
export const Partial = {
args: {
createServiceAccount: true,
createProject: true,
},
template: `
<app-onboarding title="Get started">
<app-onboarding-task
[title]="'createMachineAccount' | i18n"
icon="bwi-cli"
[completed]="createServiceAccount"
>
<span>
{{ "downloadThe" | i18n }} <a bitLink routerLink="">{{ "smCLI" | i18n }}</a>
</span>
</app-onboarding-task>
<app-onboarding-task
[title]="'createProject' | i18n"
icon="bwi-collection"
[completed]="createProject"
></app-onboarding-task>
<app-onboarding-task
[title]="'importSecrets' | i18n"
icon="bwi-download"
[completed]="importSecrets$ | async"
></app-onboarding-task>
<app-onboarding-task
[title]="'createSecret' | i18n"
icon="bwi-key"
[completed]="createSecret"
></app-onboarding-task>
</app-onboarding>
`,
});
export const Empty = Template.bind({});
export const Partial = Template.bind({});
Partial.args = {
...Template.args,
createServiceAccount: true,
createProject: true,
};
export const Full = Template.bind({});
Full.args = {
...Template.args,
createServiceAccount: true,
createProject: true,
createSecret: true,
importSecrets$: of(true).pipe(delay(0), startWith(false)),
export const Full = {
args: {
createServiceAccount: true,
createProject: true,
createSecret: true,
importSecrets$: of(true).pipe(delay(0), startWith(false)),
},
};

View File

@@ -1,6 +1,6 @@
import { importProvidersFrom } from "@angular/core";
import { RouterTestingModule } from "@angular/router/testing";
import { Meta, Story, applicationConfig, moduleMetadata } from "@storybook/angular";
import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { BadgeModule, IconModule } from "@bitwarden/components";
@@ -32,18 +32,18 @@ export default {
},
} as Meta;
const Template: Story<ReportCardComponent> = (args: ReportCardComponent) => ({
props: args,
});
type Story = StoryObj<ReportCardComponent>;
export const Enabled = Template.bind({});
export const Enabled: Story = {};
export const RequiresPremium = Template.bind({});
RequiresPremium.args = {
variant: ReportVariant.RequiresPremium,
export const RequiresPremium: Story = {
args: {
variant: ReportVariant.RequiresPremium,
},
};
export const RequiresUpgrade = Template.bind({});
RequiresUpgrade.args = {
variant: ReportVariant.RequiresUpgrade,
export const RequiresUpgrade: Story = {
args: {
variant: ReportVariant.RequiresUpgrade,
},
};

View File

@@ -1,6 +1,6 @@
import { importProvidersFrom } from "@angular/core";
import { RouterTestingModule } from "@angular/router/testing";
import { Meta, Story, applicationConfig, moduleMetadata } from "@storybook/angular";
import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { BadgeModule, IconModule } from "@bitwarden/components";
@@ -34,8 +34,6 @@ export default {
},
} as Meta;
const Template: Story<ReportListComponent> = (args: ReportListComponent) => ({
props: args,
});
type Story = StoryObj<ReportListComponent>;
export const Default = Template.bind({});
export const Default: Story = {};

View File

@@ -1,4 +1,4 @@
import { Meta, moduleMetadata, Story } from "@storybook/angular";
import { Meta, moduleMetadata, StoryObj } from "@storybook/angular";
import { of } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
@@ -47,9 +47,6 @@ export default {
],
} as Meta;
const Template: Story<PremiumBadgeComponent> = (args: PremiumBadgeComponent) => ({
props: args,
});
type Story = StoryObj<PremiumBadgeComponent>;
export const Primary = Template.bind({});
Primary.args = {};
export const Primary: Story = {};

View File

@@ -1,6 +1,6 @@
import { importProvidersFrom } from "@angular/core";
import { RouterModule } from "@angular/router";
import { applicationConfig, Meta, moduleMetadata, Story } from "@storybook/angular";
import { applicationConfig, Meta, moduleMetadata, StoryObj } from "@storybook/angular";
import { BehaviorSubject, of } from "rxjs";
import { OrganizationUserType } from "@bitwarden/common/admin-console/enums";
@@ -118,138 +118,136 @@ export default {
argTypes: { onEvent: { action: "onEvent" } },
} as Meta;
const Template: Story<VaultItemsComponent> = (args: VaultItemsComponent) => ({
props: args,
});
type Story = StoryObj<VaultItemsComponent>;
export const Individual = Template.bind({});
Individual.args = {
ciphers,
collections: [],
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: true,
showBulkTrashOptions: false,
useEvents: false,
cloneableOrganizationCiphers: false,
export const Individual: Story = {
args: {
ciphers,
collections: [],
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: true,
showBulkTrashOptions: false,
useEvents: false,
},
};
export const IndividualDisabled = Template.bind({});
IndividualDisabled.args = {
ciphers,
collections: [],
disabled: true,
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: true,
showBulkTrashOptions: false,
useEvents: false,
cloneableOrganizationCiphers: false,
export const IndividualDisabled: Story = {
args: {
ciphers,
collections: [],
disabled: true,
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: true,
showBulkTrashOptions: false,
useEvents: false,
},
};
export const IndividualTrash = Template.bind({});
IndividualTrash.args = {
ciphers: deletedCiphers,
collections: [],
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: true,
useEvents: false,
cloneableOrganizationCiphers: false,
export const IndividualTrash: Story = {
args: {
ciphers: deletedCiphers,
collections: [],
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: true,
useEvents: false,
},
};
export const IndividualTopLevelCollection = Template.bind({});
IndividualTopLevelCollection.args = {
ciphers: [],
collections,
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: false,
useEvents: false,
cloneableOrganizationCiphers: false,
export const IndividualTopLevelCollection: Story = {
args: {
ciphers: [],
collections,
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: false,
useEvents: false,
},
};
export const IndividualSecondLevelCollection = Template.bind({});
IndividualSecondLevelCollection.args = {
ciphers,
collections,
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: true,
showBulkTrashOptions: false,
useEvents: false,
cloneableOrganizationCiphers: false,
export const IndividualSecondLevelCollection: Story = {
args: {
ciphers,
collections,
showOwner: true,
showCollections: false,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: true,
showBulkTrashOptions: false,
useEvents: false,
},
};
export const OrganizationVault = Template.bind({});
OrganizationVault.args = {
ciphers: organizationOnlyCiphers,
collections: [],
showOwner: false,
showCollections: true,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: false,
useEvents: true,
cloneableOrganizationCiphers: true,
export const OrganizationVault: Story = {
args: {
ciphers: organizationOnlyCiphers,
collections: [],
showOwner: false,
showCollections: true,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: false,
useEvents: true,
},
};
export const OrganizationTrash = Template.bind({});
OrganizationTrash.args = {
ciphers: deletedOrganizationOnlyCiphers,
collections: [],
showOwner: false,
showCollections: true,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: true,
useEvents: true,
cloneableOrganizationCiphers: true,
export const OrganizationTrash: Story = {
args: {
ciphers: deletedOrganizationOnlyCiphers,
collections: [],
showOwner: false,
showCollections: true,
showGroups: false,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: true,
useEvents: true,
},
};
const unassignedCollection = new CollectionAdminView();
unassignedCollection.id = Unassigned;
unassignedCollection.name = "Unassigned";
export const OrganizationTopLevelCollection = Template.bind({});
OrganizationTopLevelCollection.args = {
ciphers: [],
collections: collections.concat(unassignedCollection),
showOwner: false,
showCollections: false,
showGroups: true,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: false,
useEvents: true,
cloneableOrganizationCiphers: true,
export const OrganizationTopLevelCollection: Story = {
args: {
ciphers: [],
collections: collections.concat(unassignedCollection),
showOwner: false,
showCollections: false,
showGroups: true,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: false,
useEvents: true,
},
};
export const OrganizationSecondLevelCollection = Template.bind({});
OrganizationSecondLevelCollection.args = {
ciphers: organizationOnlyCiphers,
collections,
showOwner: false,
showCollections: false,
showGroups: true,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: false,
useEvents: true,
cloneableOrganizationCiphers: true,
export const OrganizationSecondLevelCollection: Story = {
args: {
ciphers: organizationOnlyCiphers,
collections,
showOwner: false,
showCollections: false,
showGroups: true,
showPremiumFeatures: true,
showBulkMove: false,
showBulkTrashOptions: false,
useEvents: true,
},
};
function createCipherView(i: number, deleted = false): CipherView {