mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 21:33:27 +00:00
[PM-21211] Update Lit component stories (#14657)
* replace string with message catalog representation * add I18n type * update components * update and add stories and mock data * mock chrome.i18n.getMessage for lit stories that require it * set a max-width for the header component story * move i18n to bottom of story controls
This commit is contained in:
@@ -5,17 +5,19 @@ import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { border, themes, typography, spacing } from "../constants/styles";
|
||||
|
||||
export type ActionButtonProps = {
|
||||
buttonText: string | TemplateResult;
|
||||
disabled?: boolean;
|
||||
theme: Theme;
|
||||
handleClick: (e: Event) => void;
|
||||
};
|
||||
|
||||
export function ActionButton({
|
||||
buttonText,
|
||||
disabled = false,
|
||||
theme,
|
||||
handleClick,
|
||||
}: {
|
||||
buttonText: string | TemplateResult;
|
||||
disabled?: boolean;
|
||||
theme: Theme;
|
||||
handleClick: (e: Event) => void;
|
||||
}) {
|
||||
}: ActionButtonProps) {
|
||||
const handleButtonClick = (event: Event) => {
|
||||
if (!disabled) {
|
||||
handleClick(event);
|
||||
|
||||
@@ -5,17 +5,19 @@ import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { border, themes, typography, spacing } from "../constants/styles";
|
||||
|
||||
export type BadgeButtonProps = {
|
||||
buttonAction: (e: Event) => void;
|
||||
buttonText: string;
|
||||
disabled?: boolean;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export function BadgeButton({
|
||||
buttonAction,
|
||||
buttonText,
|
||||
disabled = false,
|
||||
theme,
|
||||
}: {
|
||||
buttonAction: (e: Event) => void;
|
||||
buttonText: string;
|
||||
disabled?: boolean;
|
||||
theme: Theme;
|
||||
}) {
|
||||
}: BadgeButtonProps) {
|
||||
const handleButtonClick = (event: Event) => {
|
||||
if (!disabled) {
|
||||
buttonAction(event);
|
||||
|
||||
@@ -6,13 +6,12 @@ import { Theme } from "@bitwarden/common/platform/enums";
|
||||
import { spacing, themes } from "../constants/styles";
|
||||
import { Close as CloseIcon } from "../icons";
|
||||
|
||||
export function CloseButton({
|
||||
handleCloseNotification,
|
||||
theme,
|
||||
}: {
|
||||
export type CloseButtonProps = {
|
||||
handleCloseNotification: (e: Event) => void;
|
||||
theme: Theme;
|
||||
}) {
|
||||
};
|
||||
|
||||
export function CloseButton({ handleCloseNotification, theme }: CloseButtonProps) {
|
||||
return html`
|
||||
<button type="button" class=${closeButtonStyles(theme)} @click=${handleCloseNotification}>
|
||||
${CloseIcon({ theme })}
|
||||
|
||||
@@ -6,17 +6,14 @@ import { Theme } from "@bitwarden/common/platform/enums";
|
||||
import { themes, typography, spacing } from "../constants/styles";
|
||||
import { PencilSquare } from "../icons";
|
||||
|
||||
export function EditButton({
|
||||
buttonAction,
|
||||
buttonText,
|
||||
disabled = false,
|
||||
theme,
|
||||
}: {
|
||||
export type EditButtonProps = {
|
||||
buttonAction: (e: Event) => void;
|
||||
buttonText: string;
|
||||
disabled?: boolean;
|
||||
theme: Theme;
|
||||
}) {
|
||||
};
|
||||
|
||||
export function EditButton({ buttonAction, buttonText, disabled = false, theme }: EditButtonProps) {
|
||||
return html`
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -3,6 +3,14 @@ import { Theme } from "@bitwarden/common/platform/enums";
|
||||
import { BadgeButton } from "../../../content/components/buttons/badge-button";
|
||||
import { EditButton } from "../../../content/components/buttons/edit-button";
|
||||
import { NotificationTypes } from "../../../notification/abstractions/notification-bar";
|
||||
import { I18n } from "../common-types";
|
||||
|
||||
export type CipherActionProps = {
|
||||
handleAction?: (e: Event) => void;
|
||||
i18n: I18n;
|
||||
notificationType: typeof NotificationTypes.Change | typeof NotificationTypes.Add;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export function CipherAction({
|
||||
handleAction = () => {
|
||||
@@ -11,12 +19,7 @@ export function CipherAction({
|
||||
i18n,
|
||||
notificationType,
|
||||
theme,
|
||||
}: {
|
||||
handleAction?: (e: Event) => void;
|
||||
i18n: { [key: string]: string };
|
||||
notificationType: typeof NotificationTypes.Change | typeof NotificationTypes.Add;
|
||||
theme: Theme;
|
||||
}) {
|
||||
}: CipherActionProps) {
|
||||
return notificationType === NotificationTypes.Change
|
||||
? BadgeButton({
|
||||
buttonAction: handleAction,
|
||||
|
||||
@@ -5,21 +5,18 @@ import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { Globe } from "../../../content/components/icons";
|
||||
|
||||
/**
|
||||
* @param {string} props.color contextual color override if no icon URI is available
|
||||
* @param {string} props.size valid CSS `width` value, represents the width-basis of the graphic, with height maintaining original aspect-ratio
|
||||
*/
|
||||
export function CipherIcon({
|
||||
color,
|
||||
size,
|
||||
theme,
|
||||
uri,
|
||||
}: {
|
||||
export type CipherIconProps = {
|
||||
color: string;
|
||||
size: string;
|
||||
theme: Theme;
|
||||
uri?: string;
|
||||
}) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} props.color contextual color override if no icon URI is available
|
||||
* @param {string} props.size valid CSS `width` value, represents the width-basis of the graphic, with height maintaining original aspect-ratio
|
||||
*/
|
||||
export function CipherIcon({ color, size, theme, uri }: CipherIconProps) {
|
||||
const iconClass = cipherIconStyle({ width: size });
|
||||
|
||||
return uri
|
||||
|
||||
@@ -16,13 +16,15 @@ const cipherIndicatorIconsMap: Record<
|
||||
[OrganizationCategories.family]: Family,
|
||||
};
|
||||
|
||||
export type CipherInfoIndicatorIconsProps = {
|
||||
organizationCategories?: OrganizationCategory[];
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export function CipherInfoIndicatorIcons({
|
||||
organizationCategories = [],
|
||||
theme,
|
||||
}: {
|
||||
organizationCategories?: OrganizationCategory[];
|
||||
theme: Theme;
|
||||
}) {
|
||||
}: CipherInfoIndicatorIconsProps) {
|
||||
return html`
|
||||
<span class=${cipherInfoIndicatorIconsStyles}>
|
||||
${organizationCategories.map((name) =>
|
||||
|
||||
@@ -8,7 +8,9 @@ import { themes, typography } from "../../../content/components/constants/styles
|
||||
import { CipherInfoIndicatorIcons } from "./cipher-indicator-icons";
|
||||
import { NotificationCipherData } from "./types";
|
||||
|
||||
export function CipherInfo({ cipher, theme }: { cipher: NotificationCipherData; theme: Theme }) {
|
||||
export type CipherInfoProps = { cipher: NotificationCipherData; theme: Theme };
|
||||
|
||||
export function CipherInfo({ cipher, theme }: CipherInfoProps) {
|
||||
const { name, login, organizationCategories } = cipher;
|
||||
const hasIndicatorIcons = organizationCategories?.length;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
NotificationType,
|
||||
NotificationTypes,
|
||||
} from "../../../notification/abstractions/notification-bar";
|
||||
import { I18n } from "../common-types";
|
||||
|
||||
import { CipherAction } from "./cipher-action";
|
||||
import { CipherIcon } from "./cipher-icon";
|
||||
@@ -16,19 +17,21 @@ import { NotificationCipherData } from "./types";
|
||||
|
||||
const cipherIconWidth = "24px";
|
||||
|
||||
export type CipherItemProps = {
|
||||
cipher: NotificationCipherData;
|
||||
handleAction?: (e: Event) => void;
|
||||
i18n: I18n;
|
||||
notificationType?: NotificationType;
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export function CipherItem({
|
||||
cipher,
|
||||
handleAction,
|
||||
i18n,
|
||||
notificationType,
|
||||
theme = ThemeTypes.Light,
|
||||
}: {
|
||||
cipher: NotificationCipherData;
|
||||
handleAction?: (e: Event) => void;
|
||||
i18n: { [key: string]: string };
|
||||
notificationType?: NotificationType;
|
||||
theme: Theme;
|
||||
}) {
|
||||
}: CipherItemProps) {
|
||||
const { icon } = cipher;
|
||||
const uri = (icon.imageEnabled && icon.image) || undefined;
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ import { TemplateResult } from "lit";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
export type I18n = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
export type IconProps = {
|
||||
color?: string;
|
||||
disabled?: boolean;
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { ActionButton } from "../../buttons/action-button";
|
||||
|
||||
type Args = {
|
||||
buttonText: string;
|
||||
disabled: boolean;
|
||||
theme: Theme;
|
||||
handleClick: (e: Event) => void;
|
||||
};
|
||||
import { ActionButton, ActionButtonProps } from "../../buttons/action-button";
|
||||
|
||||
export default {
|
||||
title: "Components/Buttons/Action Button",
|
||||
@@ -31,10 +24,10 @@ export default {
|
||||
url: "https://www.figma.com/design/LEhqLAcBPY8uDKRfU99n9W/Autofill-notification-redesign?node-id=487-14755&t=2O7uCAkwRZCcjumm-4",
|
||||
},
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<ActionButtonProps>;
|
||||
|
||||
const Template = (args: Args) => ActionButton({ ...args });
|
||||
const Template = (args: ActionButtonProps) => ActionButton({ ...args });
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<ActionButtonProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { BadgeButton } from "../../buttons/badge-button";
|
||||
|
||||
type Args = {
|
||||
buttonAction: (e: Event) => void;
|
||||
buttonText: string;
|
||||
disabled?: boolean;
|
||||
theme: Theme;
|
||||
};
|
||||
import { BadgeButton, BadgeButtonProps } from "../../buttons/badge-button";
|
||||
|
||||
export default {
|
||||
title: "Components/Buttons/Badge Button",
|
||||
@@ -31,10 +24,10 @@ export default {
|
||||
url: "https://www.figma.com/design/LEhqLAcBPY8uDKRfU99n9W/Autofill-notification-redesign?node-id=502-24973&t=2O7uCAkwRZCcjumm-4",
|
||||
},
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<BadgeButtonProps>;
|
||||
|
||||
const Template = (args: Args) => BadgeButton({ ...args });
|
||||
const Template = (args: BadgeButtonProps) => BadgeButton({ ...args });
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<BadgeButtonProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { CloseButton } from "../../buttons/close-button";
|
||||
import { CloseButton, CloseButtonProps } from "../../buttons/close-button";
|
||||
|
||||
type Args = {
|
||||
handleCloseNotification: (e: Event) => void;
|
||||
theme: Theme;
|
||||
};
|
||||
export default {
|
||||
title: "Components/Buttons/Close Button",
|
||||
argTypes: {
|
||||
@@ -26,10 +22,10 @@ export default {
|
||||
url: "https://www.figma.com/design/LEhqLAcBPY8uDKRfU99n9W/Autofill-notification-redesign?node-id=502-24633&t=2O7uCAkwRZCcjumm-4",
|
||||
},
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<CloseButtonProps>;
|
||||
|
||||
const Template = (args: Args) => CloseButton({ ...args });
|
||||
const Template = (args: CloseButtonProps) => CloseButton({ ...args });
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<CloseButtonProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -1,28 +1,22 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { EditButton } from "../../buttons/edit-button";
|
||||
import { EditButton, EditButtonProps } from "../../buttons/edit-button";
|
||||
|
||||
type Args = {
|
||||
buttonAction: (e: Event) => void;
|
||||
buttonText: string;
|
||||
disabled?: boolean;
|
||||
theme: Theme;
|
||||
};
|
||||
export default {
|
||||
title: "Components/Buttons/Edit Button",
|
||||
argTypes: {
|
||||
buttonAction: { control: false },
|
||||
buttonText: { control: "text" },
|
||||
disabled: { control: "boolean" },
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
buttonAction: { control: false },
|
||||
},
|
||||
args: {
|
||||
buttonAction: () => alert("Clicked"),
|
||||
buttonText: "Click Me",
|
||||
disabled: false,
|
||||
theme: ThemeTypes.Light,
|
||||
buttonAction: () => alert("Clicked"),
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
@@ -30,10 +24,10 @@ export default {
|
||||
url: "https://www.figma.com/design/LEhqLAcBPY8uDKRfU99n9W/Autofill-notification-redesign?node-id=502-24633&t=2O7uCAkwRZCcjumm-4",
|
||||
},
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<EditButtonProps>;
|
||||
|
||||
const Template = (args: Args) => EditButton({ ...args });
|
||||
const Template = (args: EditButtonProps) => EditButton({ ...args });
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<EditButtonProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import {
|
||||
OptionSelectionButton,
|
||||
OptionSelectionButtonProps,
|
||||
} from "../../buttons/option-selection-button";
|
||||
|
||||
export default {
|
||||
title: "Components/Buttons/Option Selection Button",
|
||||
argTypes: {
|
||||
disabled: { control: "boolean" },
|
||||
handleButtonClick: { control: false },
|
||||
text: { control: "text" },
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
toggledOn: { control: "boolean" },
|
||||
},
|
||||
args: {
|
||||
disabled: false,
|
||||
handleButtonClick: () => alert("Clicked"),
|
||||
text: "Click Me",
|
||||
theme: ThemeTypes.Light,
|
||||
toggledOn: false,
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
type: "figma",
|
||||
url: "https://www.figma.com/design/LEhqLAcBPY8uDKRfU99n9W/Autofill-notification-redesign?node-id=502-24633&t=2O7uCAkwRZCcjumm-4",
|
||||
},
|
||||
},
|
||||
} as Meta<OptionSelectionButtonProps>;
|
||||
|
||||
const Template = (args: OptionSelectionButtonProps) => OptionSelectionButton({ ...args });
|
||||
|
||||
export const Default: StoryObj<OptionSelectionButtonProps> = {
|
||||
render: Template,
|
||||
};
|
||||
@@ -1,16 +1,11 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { NotificationTypes } from "../../../../notification/abstractions/notification-bar";
|
||||
import { CipherAction } from "../../cipher/cipher-action";
|
||||
import { CipherAction, CipherActionProps } from "../../cipher/cipher-action";
|
||||
import { mockI18n } from "../mock-data";
|
||||
|
||||
type Args = {
|
||||
handleAction?: (e: Event) => void;
|
||||
i18n: { [key: string]: string };
|
||||
notificationType: typeof NotificationTypes.Change | typeof NotificationTypes.Add;
|
||||
theme: Theme;
|
||||
};
|
||||
export default {
|
||||
title: "Components/Ciphers/Cipher Action",
|
||||
argTypes: {
|
||||
@@ -24,14 +19,13 @@ export default {
|
||||
args: {
|
||||
theme: ThemeTypes.Light,
|
||||
notificationType: NotificationTypes.Change,
|
||||
handleAction: () => {
|
||||
alert("Action triggered!");
|
||||
},
|
||||
handleAction: () => alert("Action triggered!"),
|
||||
i18n: mockI18n,
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<CipherActionProps>;
|
||||
|
||||
const Template = (args: Args) => CipherAction({ ...args });
|
||||
const Template = (args: CipherActionProps) => CipherAction({ ...args });
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<CipherActionProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
import { html } from "lit";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { CipherIcon } from "../../cipher/cipher-icon";
|
||||
|
||||
type Args = {
|
||||
color: string;
|
||||
size: string;
|
||||
theme: Theme;
|
||||
uri?: string;
|
||||
};
|
||||
import { CipherIcon, CipherIconProps } from "../../cipher/cipher-icon";
|
||||
|
||||
export default {
|
||||
title: "Components/Ciphers/Cipher Icon",
|
||||
@@ -25,9 +18,9 @@ export default {
|
||||
theme: ThemeTypes.Light,
|
||||
uri: "",
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<CipherIconProps>;
|
||||
|
||||
const Template = (args: Args) => {
|
||||
const Template = (args: CipherIconProps) => {
|
||||
return html`
|
||||
<div style="width: ${args.size}; height: ${args.size}; overflow: hidden;">
|
||||
${CipherIcon({ ...args })}
|
||||
@@ -35,6 +28,6 @@ const Template = (args: Args) => {
|
||||
`;
|
||||
};
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<CipherIconProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
import { html } from "lit";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { CipherInfoIndicatorIcons } from "../../cipher/cipher-indicator-icons";
|
||||
|
||||
type Args = {
|
||||
showBusinessIcon?: boolean;
|
||||
showFamilyIcon?: boolean;
|
||||
theme: Theme;
|
||||
};
|
||||
import {
|
||||
CipherInfoIndicatorIcons,
|
||||
CipherInfoIndicatorIconsProps,
|
||||
} from "../../cipher/cipher-indicator-icons";
|
||||
import { OrganizationCategories } from "../../cipher/types";
|
||||
|
||||
export default {
|
||||
title: "Components/Ciphers/Cipher Indicator Icons",
|
||||
argTypes: {
|
||||
showBusinessIcon: { control: "boolean" },
|
||||
showFamilyIcon: { control: "boolean" },
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
},
|
||||
args: {
|
||||
theme: ThemeTypes.Light,
|
||||
showBusinessIcon: true,
|
||||
showFamilyIcon: false,
|
||||
organizationCategories: [...Object.values(OrganizationCategories)],
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<CipherInfoIndicatorIconsProps>;
|
||||
|
||||
const Template: StoryObj<Args>["render"] = (args) =>
|
||||
const Template: StoryObj<CipherInfoIndicatorIconsProps>["render"] = (args) =>
|
||||
html`<div>${CipherInfoIndicatorIcons({ ...args })}</div>`;
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<CipherInfoIndicatorIconsProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { CipherInfo, CipherInfoProps } from "../../cipher/cipher-info";
|
||||
import { mockCiphers } from "../mock-data";
|
||||
|
||||
export default {
|
||||
title: "Components/Ciphers/Cipher Info",
|
||||
argTypes: {
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
},
|
||||
args: {
|
||||
cipher: mockCiphers[0],
|
||||
theme: ThemeTypes.Light,
|
||||
},
|
||||
} as Meta<CipherInfoProps>;
|
||||
|
||||
const Template = (args: CipherInfoProps) => CipherInfo({ ...args });
|
||||
|
||||
export const Default: StoryObj<CipherInfoProps> = {
|
||||
render: Template,
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { NotificationTypes } from "../../../../notification/abstractions/notification-bar";
|
||||
import { CipherItem, CipherItemProps } from "../../cipher/cipher-item";
|
||||
import { mockCiphers, mockI18n } from "../mock-data";
|
||||
|
||||
export default {
|
||||
title: "Components/Ciphers/Cipher Item",
|
||||
argTypes: {
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
handleAction: { control: false },
|
||||
notificationType: {
|
||||
control: "select",
|
||||
options: [NotificationTypes.Change, NotificationTypes.Add],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
cipher: mockCiphers[0],
|
||||
theme: ThemeTypes.Light,
|
||||
notificationType: NotificationTypes.Change,
|
||||
handleAction: () => alert("Clicked"),
|
||||
i18n: mockI18n,
|
||||
},
|
||||
} as Meta<CipherItemProps>;
|
||||
|
||||
const Template = (args: CipherItemProps) => CipherItem({ ...args });
|
||||
|
||||
export const Default: StoryObj<CipherItemProps> = {
|
||||
render: Template,
|
||||
};
|
||||
@@ -1,14 +1,12 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
import { html } from "lit";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { IconProps } from "../../common-types";
|
||||
import * as Icons from "../../icons";
|
||||
|
||||
type Args = {
|
||||
color?: string;
|
||||
disabled?: boolean;
|
||||
theme: Theme;
|
||||
type Args = IconProps & {
|
||||
size: number;
|
||||
iconLink: URL;
|
||||
};
|
||||
@@ -16,21 +14,19 @@ type Args = {
|
||||
export default {
|
||||
title: "Components/Icons",
|
||||
argTypes: {
|
||||
iconLink: { control: "text" },
|
||||
color: { control: "color" },
|
||||
disabled: { control: "boolean" },
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
size: { control: "number", min: 10, max: 100, step: 1 },
|
||||
},
|
||||
args: {
|
||||
iconLink: new URL("https://bitwarden.com"),
|
||||
disabled: false,
|
||||
theme: ThemeTypes.Light,
|
||||
size: 50,
|
||||
},
|
||||
} as Meta<Args>;
|
||||
|
||||
const Template = (args: Args, IconComponent: (props: Args) => ReturnType<typeof html>) => html`
|
||||
const Template = (args: Args, IconComponent: (props: IconProps) => ReturnType<typeof html>) => html`
|
||||
<div
|
||||
style="width: ${args.size}px; height: ${args.size}px; display: flex; align-items: center; justify-content: center;"
|
||||
>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
import { html } from "lit";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { IconProps } from "../../common-types";
|
||||
import * as Illustrations from "../../illustrations";
|
||||
|
||||
type Args = {
|
||||
theme: Theme;
|
||||
type Args = IconProps & {
|
||||
size: number;
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ export default {
|
||||
|
||||
const Template = (
|
||||
args: Args,
|
||||
IllustrationComponent: (props: Args) => ReturnType<typeof html>,
|
||||
IllustrationComponent: (props: IconProps) => ReturnType<typeof html>,
|
||||
) => html`
|
||||
<div
|
||||
style="width: ${args.size}px; height: ${args.size}px; display: flex; align-items: center; justify-content: center;"
|
||||
|
||||
@@ -1,6 +1,48 @@
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
|
||||
export const mockFolderData = [
|
||||
export const mockOrganizations = [
|
||||
{
|
||||
id: "unique-id0",
|
||||
name: "Another personal vault",
|
||||
},
|
||||
{
|
||||
id: "unique-id1",
|
||||
name: "Acme, inc",
|
||||
productTierType: ProductTierType.Teams,
|
||||
},
|
||||
{
|
||||
id: "unique-id2",
|
||||
name: "A Really Long Business Name That Just Kinda Goes On For A Really Long Time",
|
||||
productTierType: ProductTierType.TeamsStarter,
|
||||
},
|
||||
{
|
||||
id: "unique-id3",
|
||||
name: "Family Vault",
|
||||
productTierType: ProductTierType.Families,
|
||||
},
|
||||
{
|
||||
id: "unique-id4",
|
||||
name: "Family Vault Trial",
|
||||
productTierType: ProductTierType.Free,
|
||||
},
|
||||
{
|
||||
id: "unique-id5",
|
||||
name: "Exciting Enterprises, LLC",
|
||||
productTierType: ProductTierType.Enterprise,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockCollections = [
|
||||
{
|
||||
id: "collection-id-01",
|
||||
name: "A collection for stuff",
|
||||
organizationId: mockOrganizations[0].id,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockFolders = [
|
||||
{
|
||||
id: "unique-id1",
|
||||
name: "A folder",
|
||||
@@ -35,34 +77,101 @@ export const mockFolderData = [
|
||||
},
|
||||
];
|
||||
|
||||
export const mockOrganizationData = [
|
||||
export const mockCiphers = [
|
||||
{
|
||||
id: "unique-id0",
|
||||
name: "Another personal vault",
|
||||
},
|
||||
{
|
||||
id: "unique-id1",
|
||||
name: "Acme, inc",
|
||||
productTierType: ProductTierType.Teams,
|
||||
},
|
||||
{
|
||||
id: "unique-id2",
|
||||
name: "A Really Long Business Name That Just Kinda Goes On For A Really Long Time",
|
||||
productTierType: ProductTierType.TeamsStarter,
|
||||
},
|
||||
{
|
||||
id: "unique-id3",
|
||||
name: "Family Vault",
|
||||
productTierType: ProductTierType.Families,
|
||||
},
|
||||
{
|
||||
id: "unique-id4",
|
||||
name: "Family Vault Trial",
|
||||
productTierType: ProductTierType.Free,
|
||||
},
|
||||
{
|
||||
id: "unique-id5",
|
||||
name: "Exciting Enterprises, LLC",
|
||||
productTierType: ProductTierType.Enterprise,
|
||||
id: "1",
|
||||
name: "Example Cipher",
|
||||
type: CipherType.Login,
|
||||
favorite: false,
|
||||
reprompt: CipherRepromptType.None,
|
||||
icon: {
|
||||
imageEnabled: true,
|
||||
image: "",
|
||||
fallbackImage: "https://example.com/fallback.png",
|
||||
icon: "icon-class",
|
||||
},
|
||||
login: { username: "user@example.com" },
|
||||
},
|
||||
];
|
||||
|
||||
export const mockTasks = [
|
||||
{
|
||||
orgName: "Acme, Inc.",
|
||||
remainingTasksCount: 0,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockI18n = {
|
||||
appName: "Bitwarden",
|
||||
close: "Close",
|
||||
collection: "Collection",
|
||||
folder: "Folder",
|
||||
loginSaveSuccess: "Login saved",
|
||||
loginSaveConfirmation: "$ITEMNAME$ saved to Bitwarden.",
|
||||
loginUpdateSuccess: "Login updated",
|
||||
loginUpdatedConfirmation: "$ITEMNAME$ updated in Bitwarden.",
|
||||
loginUpdateTaskSuccess:
|
||||
"Great job! You took the steps to make you and $ORGANIZATION$ more secure.",
|
||||
loginUpdateTaskSuccessAdditional:
|
||||
"Thank you for making $ORGANIZATION$ more secure. You have $TASK_COUNT$ more passwords to update.",
|
||||
nextSecurityTaskAction: "Change next password",
|
||||
newItem: "New item",
|
||||
never: "Never",
|
||||
myVault: "My vault",
|
||||
notificationAddDesc: "Should Bitwarden remember this password for you?",
|
||||
notificationAddSave: "Save",
|
||||
notificationChangeDesc: "Do you want to update this password in Bitwarden?",
|
||||
notificationUpdate: "Update",
|
||||
notificationEdit: "Edit",
|
||||
notificationEditTooltip: "Edit before saving",
|
||||
notificationUnlock: "Unlock",
|
||||
notificationUnlockDesc: "Unlock your Bitwarden vault to complete the autofill request.",
|
||||
notificationViewAria: `View $ITEMNAME$, opens in new window`,
|
||||
saveAction: "Save",
|
||||
saveAsNewLoginAction: "Save as new login",
|
||||
saveFailure: "Error saving",
|
||||
saveFailureDetails: "Oh no! We couldn't save this. Try entering the details manually.",
|
||||
saveLogin: "Save login",
|
||||
typeLogin: "Login",
|
||||
updateLoginAction: "Update login",
|
||||
updateLogin: "Update existing login",
|
||||
vault: "Vault",
|
||||
view: "View",
|
||||
} as const;
|
||||
|
||||
type i18nMessageName = keyof typeof mockI18n;
|
||||
type i18nMessageValue = (typeof mockI18n)[i18nMessageName];
|
||||
|
||||
/**
|
||||
* Very basic mock of {@link chrome.i18n.getMessage} to enable stories
|
||||
*
|
||||
* @param {i18nMessageName} messageName must match a key in {@link mockI18n}
|
||||
* @param {(string | string[])} [substitutions]
|
||||
* @return {*} {(i18nMessageValue | string)}
|
||||
*/
|
||||
export function mockBrowserI18nGetMessage(
|
||||
messageName: i18nMessageName,
|
||||
substitutions?: string | string[],
|
||||
): i18nMessageValue | string {
|
||||
let normalizedSubstitutions: string[] = [];
|
||||
|
||||
if (substitutions) {
|
||||
normalizedSubstitutions =
|
||||
typeof substitutions === "string"
|
||||
? [substitutions]
|
||||
: substitutions.length
|
||||
? substitutions
|
||||
: [];
|
||||
}
|
||||
|
||||
if (normalizedSubstitutions.length) {
|
||||
const resolvedString = normalizedSubstitutions.reduce((builtString, substitution) => {
|
||||
// Replace first found match each iteration, in order
|
||||
return builtString.replace(/\$[A-Z_]+\$/, substitution);
|
||||
}, mockI18n[messageName] || "");
|
||||
|
||||
return resolvedString;
|
||||
}
|
||||
|
||||
return mockI18n[messageName];
|
||||
}
|
||||
|
||||
@@ -1,50 +1,27 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { NotificationType } from "../../../../notification/abstractions/notification-bar";
|
||||
import { NotificationCipherData } from "../../cipher/types";
|
||||
import { NotificationBody } from "../../notification/body";
|
||||
|
||||
type Args = {
|
||||
ciphers: NotificationCipherData[];
|
||||
i18n: { [key: string]: string };
|
||||
notificationType: NotificationType;
|
||||
theme: Theme;
|
||||
handleEditOrUpdateAction: (e: Event) => void;
|
||||
};
|
||||
import { NotificationTypes } from "../../../../notification/abstractions/notification-bar";
|
||||
import { NotificationBody, NotificationBodyProps } from "../../notification/body";
|
||||
import { mockCiphers, mockI18n } from "../mock-data";
|
||||
|
||||
export default {
|
||||
title: "Components/Notifications/Body",
|
||||
argTypes: {
|
||||
ciphers: { control: "object" },
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
notificationType: {
|
||||
control: "select",
|
||||
options: ["add", "change", "unlock", "fileless-import"],
|
||||
options: [...Object.values(NotificationTypes)],
|
||||
},
|
||||
handleEditOrUpdateAction: { control: false },
|
||||
},
|
||||
args: {
|
||||
ciphers: [
|
||||
{
|
||||
id: "1",
|
||||
name: "Example Cipher",
|
||||
type: CipherType.Login,
|
||||
favorite: false,
|
||||
reprompt: CipherRepromptType.None,
|
||||
icon: {
|
||||
imageEnabled: true,
|
||||
image: "",
|
||||
fallbackImage: "https://example.com/fallback.png",
|
||||
icon: "icon-class",
|
||||
},
|
||||
login: { username: "user@example.com" },
|
||||
},
|
||||
],
|
||||
ciphers: mockCiphers,
|
||||
notificationType: NotificationTypes.Change,
|
||||
theme: ThemeTypes.Light,
|
||||
notificationType: "change",
|
||||
handleEditOrUpdateAction: () => window.alert("clicked!"),
|
||||
i18n: mockI18n,
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
@@ -52,10 +29,10 @@ export default {
|
||||
url: "https://www.figma.com/design/LEhqLAcBPY8uDKRfU99n9W/Autofill-notification-redesign?node-id=217-6841&m=dev",
|
||||
},
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<NotificationBodyProps>;
|
||||
|
||||
const Template = (args: Args) => NotificationBody({ ...args });
|
||||
const Template = (args: NotificationBodyProps) => NotificationBody({ ...args });
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<NotificationBodyProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ export default {
|
||||
title: "Components/Notifications/Confirmation/Body",
|
||||
argTypes: {
|
||||
error: { control: "text" },
|
||||
buttonAria: { control: "text" },
|
||||
buttonText: { control: "text" },
|
||||
confirmationMessage: { control: "text" },
|
||||
messageDetails: { control: "text" },
|
||||
@@ -18,9 +19,12 @@ export default {
|
||||
},
|
||||
args: {
|
||||
error: "",
|
||||
buttonAria: "View",
|
||||
buttonText: "View",
|
||||
confirmationMessage: "[item name] updated in Bitwarden.",
|
||||
messageDetails: "You can view it in your vault.",
|
||||
tasksAreComplete: false,
|
||||
handleOpenVault: () => window.alert("Link was clicked!"),
|
||||
theme: ThemeTypes.Light,
|
||||
},
|
||||
parameters: {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
NotificationConfirmationContainer,
|
||||
NotificationConfirmationContainerProps,
|
||||
} from "../../../notification/confirmation/container";
|
||||
import { mockI18n, mockCiphers, mockBrowserI18nGetMessage, mockTasks } from "../../mock-data";
|
||||
|
||||
export default {
|
||||
title: "Components/Notifications/Confirmation",
|
||||
@@ -17,27 +18,14 @@ export default {
|
||||
},
|
||||
args: {
|
||||
error: "",
|
||||
task: {
|
||||
orgName: "Acme, Inc.",
|
||||
remainingTasksCount: 0,
|
||||
},
|
||||
handleCloseNotification: () => alert("Close notification action triggered"),
|
||||
handleOpenTasks: () => alert("Open tasks action triggered"),
|
||||
i18n: {
|
||||
loginSaveSuccess: "Login saved",
|
||||
loginUpdateSuccess: "Login updated",
|
||||
loginUpdateTaskSuccessAdditional:
|
||||
"Thank you for making your organization more secure. You have 3 more passwords to update.",
|
||||
loginUpdateTaskSuccess:
|
||||
"Great job! You took the steps to make you and your organization more secure.",
|
||||
nextSecurityTaskAction: "Change next password",
|
||||
saveFailure: "Error saving",
|
||||
saveFailureDetails: "Oh no! We couldn't save this. Try entering the details manually.",
|
||||
view: "View",
|
||||
},
|
||||
task: mockTasks[0],
|
||||
itemName: mockCiphers[0].name,
|
||||
type: NotificationTypes.Change,
|
||||
username: "Acme, Inc. Login",
|
||||
theme: ThemeTypes.Light,
|
||||
handleCloseNotification: () => alert("Close notification action triggered"),
|
||||
handleOpenVault: () => alert("Open vault action triggered"),
|
||||
handleOpenTasks: () => alert("Open tasks action triggered"),
|
||||
i18n: mockI18n,
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
@@ -53,3 +41,10 @@ const Template = (args: NotificationConfirmationContainerProps) =>
|
||||
export const Default: StoryObj<NotificationConfirmationContainerProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
window.chrome = {
|
||||
...window.chrome,
|
||||
i18n: {
|
||||
getMessage: mockBrowserI18nGetMessage,
|
||||
},
|
||||
} as typeof chrome;
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
NotificationConfirmationFooter,
|
||||
NotificationConfirmationFooterProps,
|
||||
} from "../../../notification/confirmation/footer";
|
||||
import { mockI18n } from "../../mock-data";
|
||||
|
||||
export default {
|
||||
title: "Components/Notifications/Confirmation/Footer",
|
||||
@@ -14,11 +15,9 @@ export default {
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
},
|
||||
args: {
|
||||
handleButtonClick: () => alert("Action button triggered"),
|
||||
i18n: {
|
||||
nextSecurityTaskAction: "Change next password",
|
||||
},
|
||||
theme: ThemeTypes.Light,
|
||||
handleButtonClick: () => alert("Action button triggered"),
|
||||
i18n: mockI18n,
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
export default {
|
||||
title: "Components/Notifications/Confirmation/Message",
|
||||
argTypes: {
|
||||
buttonAria: { control: "text" },
|
||||
buttonText: { control: "text" },
|
||||
message: { control: "text" },
|
||||
messageDetails: { control: "text" },
|
||||
@@ -19,6 +20,7 @@ export default {
|
||||
buttonText: "View",
|
||||
message: "[item name] updated in Bitwarden.",
|
||||
messageDetails: "It was added to your vault.",
|
||||
handleClick: () => window.alert("link was clicked!"),
|
||||
theme: ThemeTypes.Light,
|
||||
},
|
||||
parameters: {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-repromp
|
||||
|
||||
import { NotificationTypes } from "../../../../notification/abstractions/notification-bar";
|
||||
import { NotificationContainer, NotificationContainerProps } from "../../notification/container";
|
||||
import { mockBrowserI18nGetMessage, mockI18n } from "../mock-data";
|
||||
|
||||
export default {
|
||||
title: "Components/Notifications",
|
||||
@@ -32,19 +33,10 @@ export default {
|
||||
login: { username: "user@example.com" },
|
||||
},
|
||||
],
|
||||
i18n: {
|
||||
loginSaveSuccess: "Login saved",
|
||||
loginUpdateSuccess: "Login updated",
|
||||
saveAction: "Save",
|
||||
saveAsNewLoginAction: "Save as new login",
|
||||
saveFailure: "Error saving",
|
||||
saveFailureDetails: "Oh no! We couldn't save this. Try entering the details manually.",
|
||||
updateLoginPrompt: "Update existing login?",
|
||||
view: "View",
|
||||
},
|
||||
type: NotificationTypes.Change,
|
||||
username: "mockUsername",
|
||||
theme: ThemeTypes.Light,
|
||||
i18n: mockI18n,
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
@@ -59,3 +51,10 @@ const Template = (args: NotificationContainerProps) => NotificationContainer({ .
|
||||
export const Default: StoryObj<NotificationContainerProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
window.chrome = {
|
||||
...window.chrome,
|
||||
i18n: {
|
||||
getMessage: mockBrowserI18nGetMessage,
|
||||
},
|
||||
} as typeof chrome;
|
||||
|
||||
@@ -3,28 +3,27 @@ import { html } from "lit";
|
||||
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { NotificationTypes } from "../../../../notification/abstractions/notification-bar";
|
||||
import { NotificationFooter, NotificationFooterProps } from "../../notification/footer";
|
||||
import { mockFolderData, mockOrganizationData } from "../mock-data";
|
||||
import { mockCollections, mockI18n, mockFolders, mockOrganizations } from "../mock-data";
|
||||
|
||||
export default {
|
||||
title: "Components/Notifications/Footer",
|
||||
argTypes: {
|
||||
notificationType: {
|
||||
control: "select",
|
||||
options: ["add", "change", "unlock", "fileless-import"],
|
||||
options: [...Object.values(NotificationTypes)],
|
||||
},
|
||||
theme: { control: "select", options: [...Object.values(ThemeTypes)] },
|
||||
},
|
||||
args: {
|
||||
folders: mockFolderData,
|
||||
i18n: {
|
||||
saveAction: "Save",
|
||||
saveAsNewLoginAction: "Save as New Login",
|
||||
},
|
||||
notificationType: "add",
|
||||
organizations: mockOrganizationData,
|
||||
collections: mockCollections,
|
||||
folders: mockFolders,
|
||||
notificationType: NotificationTypes.Add,
|
||||
organizations: mockOrganizations,
|
||||
theme: ThemeTypes.Light,
|
||||
handleSaveAction: () => alert("Save action triggered"),
|
||||
i18n: mockI18n,
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
import { html } from "lit";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { NotificationHeader } from "../../notification/header";
|
||||
|
||||
type Args = {
|
||||
message: string;
|
||||
standalone: boolean;
|
||||
theme: Theme;
|
||||
handleCloseNotification: (e: Event) => void;
|
||||
};
|
||||
import { NotificationHeader, NotificationHeaderProps } from "../../notification/header";
|
||||
|
||||
export default {
|
||||
title: "Components/Notifications/Header",
|
||||
@@ -30,10 +24,11 @@ export default {
|
||||
url: "https://www.figma.com/design/LEhqLAcBPY8uDKRfU99n9W/Autofill-notification-redesign?node-id=32-3461&m=dev",
|
||||
},
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<NotificationHeaderProps>;
|
||||
|
||||
const Template = (args: Args) => NotificationHeader({ ...args });
|
||||
const Template = (args: NotificationHeaderProps) =>
|
||||
html`<div style="max-width:400px;">${NotificationHeader({ ...args })}</div>`;
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<NotificationHeaderProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -7,11 +7,11 @@ import { Option } from "../../common-types";
|
||||
import { themes } from "../../constants/styles";
|
||||
import { User, Business } from "../../icons";
|
||||
import "../../option-selection/option-selection";
|
||||
import { mockOrganizationData } from "../mock-data";
|
||||
import { mockOrganizations } from "../mock-data";
|
||||
|
||||
const mockOptions: Option[] = [
|
||||
{ icon: User, text: "My Vault", value: "0" },
|
||||
...mockOrganizationData.map(({ id, name }) => ({ icon: Business, text: name, value: id })),
|
||||
...mockOrganizations.map(({ id, name }) => ({ icon: Business, text: name, value: id })),
|
||||
];
|
||||
|
||||
type ComponentProps = {
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { ActionRow } from "../../rows/action-row";
|
||||
|
||||
type Args = {
|
||||
itemText: string;
|
||||
handleAction: (e: Event) => void;
|
||||
theme: Theme;
|
||||
};
|
||||
import { ActionRow, ActionRowProps } from "../../rows/action-row";
|
||||
|
||||
export default {
|
||||
title: "Components/Rows/Action Row",
|
||||
@@ -22,10 +16,10 @@ export default {
|
||||
theme: ThemeTypes.Light,
|
||||
handleAction: () => alert("Action triggered"),
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<ActionRowProps>;
|
||||
|
||||
const Template = (args: Args) => ActionRow({ ...args });
|
||||
const Template = (args: ActionRowProps) => ActionRow({ ...args });
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<ActionRowProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import { Meta, StoryObj } from "@storybook/web-components";
|
||||
import { TemplateResult } from "lit";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.enum";
|
||||
|
||||
import { ItemRow } from "../../rows/item-row";
|
||||
|
||||
type Args = {
|
||||
theme: Theme;
|
||||
children: TemplateResult | TemplateResult[];
|
||||
};
|
||||
import { ItemRow, ItemRowProps } from "../../rows/item-row";
|
||||
|
||||
export default {
|
||||
title: "Components/Rows/Item Row",
|
||||
@@ -19,10 +13,10 @@ export default {
|
||||
args: {
|
||||
theme: ThemeTypes.Light,
|
||||
},
|
||||
} as Meta<Args>;
|
||||
} as Meta<ItemRowProps>;
|
||||
|
||||
const Template = (args: Args) => ItemRow({ ...args });
|
||||
const Template = (args: ItemRowProps) => ItemRow({ ...args });
|
||||
|
||||
export const Default: StoryObj<Args> = {
|
||||
export const Default: StoryObj<ItemRowProps> = {
|
||||
render: Template,
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||
import { NotificationType } from "../../../notification/abstractions/notification-bar";
|
||||
import { CipherItem } from "../cipher";
|
||||
import { NotificationCipherData } from "../cipher/types";
|
||||
import { I18n } from "../common-types";
|
||||
import { scrollbarStyles, spacing, themes, typography } from "../constants/styles";
|
||||
import { ItemRow } from "../rows/item-row";
|
||||
|
||||
@@ -15,20 +16,21 @@ const { css } = createEmotion({
|
||||
key: componentClassPrefix,
|
||||
});
|
||||
|
||||
export type NotificationBodyProps = {
|
||||
ciphers?: NotificationCipherData[];
|
||||
i18n: I18n;
|
||||
notificationType?: NotificationType;
|
||||
theme: Theme;
|
||||
handleEditOrUpdateAction: (e: Event) => void;
|
||||
};
|
||||
|
||||
export function NotificationBody({
|
||||
ciphers = [],
|
||||
i18n,
|
||||
notificationType,
|
||||
theme = ThemeTypes.Light,
|
||||
handleEditOrUpdateAction,
|
||||
}: {
|
||||
ciphers?: NotificationCipherData[];
|
||||
customClasses?: string[];
|
||||
i18n: { [key: string]: string };
|
||||
notificationType?: NotificationType;
|
||||
theme: Theme;
|
||||
handleEditOrUpdateAction: (e: Event) => void;
|
||||
}) {
|
||||
}: NotificationBodyProps) {
|
||||
// @TODO get client vendor from context
|
||||
const isSafari = false;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { html } from "lit";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { Option, OrgView, FolderView, CollectionView } from "../common-types";
|
||||
import { Option, OrgView, FolderView, I18n, CollectionView } from "../common-types";
|
||||
import { Business, Family, Folder, User, CollectionShared } from "../icons";
|
||||
import { ButtonRow } from "../rows/button-row";
|
||||
import { selectedCollection as selectedCollectionSignal } from "../signals/selected-collection";
|
||||
@@ -30,7 +30,7 @@ const defaultNoneSelectValue = "0";
|
||||
export type NotificationButtonRowProps = {
|
||||
collections?: CollectionView[];
|
||||
folders?: FolderView[];
|
||||
i18n: { [key: string]: string };
|
||||
i18n: I18n;
|
||||
organizations?: OrgView[];
|
||||
primaryButton: {
|
||||
text: string;
|
||||
@@ -148,7 +148,7 @@ export function NotificationButtonRow({
|
||||
? [
|
||||
{
|
||||
id: "collection",
|
||||
label: "Collection", // @TODO localize
|
||||
label: i18n.collection,
|
||||
options: collectionOptions,
|
||||
selectedSignal: selectedCollectionSignal,
|
||||
},
|
||||
|
||||
@@ -15,8 +15,8 @@ const { css } = createEmotion({
|
||||
});
|
||||
|
||||
export type NotificationConfirmationBodyProps = {
|
||||
buttonAria: string;
|
||||
buttonText: string;
|
||||
itemName: string;
|
||||
confirmationMessage: string;
|
||||
error?: string;
|
||||
messageDetails?: string;
|
||||
@@ -26,8 +26,8 @@ export type NotificationConfirmationBodyProps = {
|
||||
};
|
||||
|
||||
export function NotificationConfirmationBody({
|
||||
buttonAria,
|
||||
buttonText,
|
||||
itemName,
|
||||
confirmationMessage,
|
||||
error,
|
||||
messageDetails,
|
||||
@@ -44,8 +44,8 @@ export function NotificationConfirmationBody({
|
||||
<div class=${iconContainerStyles(error)}>${IconComponent({ theme })}</div>
|
||||
${showConfirmationMessage
|
||||
? NotificationConfirmationMessage({
|
||||
buttonAria,
|
||||
buttonText,
|
||||
itemName,
|
||||
message: confirmationMessage,
|
||||
messageDetails,
|
||||
theme,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
NotificationType,
|
||||
NotificationTypes,
|
||||
} from "../../../../notification/abstractions/notification-bar";
|
||||
import { I18n } from "../../common-types";
|
||||
import { themes, spacing } from "../../constants/styles";
|
||||
import {
|
||||
NotificationHeader,
|
||||
@@ -24,7 +25,7 @@ export type NotificationConfirmationContainerProps = NotificationBarIframeInitDa
|
||||
handleOpenTasks: (e: Event) => void;
|
||||
} & {
|
||||
error?: string;
|
||||
i18n: { [key: string]: string };
|
||||
i18n: I18n;
|
||||
itemName: string;
|
||||
task?: NotificationTaskInfo;
|
||||
type: NotificationType;
|
||||
@@ -44,6 +45,7 @@ export function NotificationConfirmationContainer({
|
||||
const headerMessage = getHeaderMessage(i18n, type, error);
|
||||
const confirmationMessage = getConfirmationMessage(i18n, itemName, type, error);
|
||||
const buttonText = error ? i18n.newItem : i18n.view;
|
||||
const buttonAria = chrome.i18n.getMessage("notificationViewAria", [itemName]);
|
||||
|
||||
let messageDetails: string | undefined;
|
||||
let remainingTasksCount: number | undefined;
|
||||
@@ -70,8 +72,8 @@ export function NotificationConfirmationContainer({
|
||||
theme,
|
||||
})}
|
||||
${NotificationConfirmationBody({
|
||||
buttonAria,
|
||||
buttonText,
|
||||
itemName,
|
||||
confirmationMessage,
|
||||
tasksAreComplete,
|
||||
messageDetails,
|
||||
@@ -106,7 +108,7 @@ const notificationContainerStyles = (theme: Theme) => css`
|
||||
`;
|
||||
|
||||
function getConfirmationMessage(
|
||||
i18n: { [key: string]: string },
|
||||
i18n: I18n,
|
||||
itemName: string,
|
||||
type?: NotificationType,
|
||||
error?: string,
|
||||
@@ -117,14 +119,10 @@ function getConfirmationMessage(
|
||||
if (error) {
|
||||
return i18n.saveFailureDetails;
|
||||
}
|
||||
return type === "add" ? loginSaveConfirmation : loginUpdatedConfirmation;
|
||||
return type === NotificationTypes.Add ? loginSaveConfirmation : loginUpdatedConfirmation;
|
||||
}
|
||||
|
||||
function getHeaderMessage(
|
||||
i18n: { [key: string]: string },
|
||||
type?: NotificationType,
|
||||
error?: string,
|
||||
) {
|
||||
function getHeaderMessage(i18n: I18n, type?: NotificationType, error?: string) {
|
||||
if (error) {
|
||||
return i18n.saveFailure;
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import { html } from "lit";
|
||||
import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { ActionButton } from "../../buttons/action-button";
|
||||
import { I18n } from "../../common-types";
|
||||
import { spacing, themes } from "../../constants/styles";
|
||||
import { ExternalLink } from "../../icons";
|
||||
|
||||
export type NotificationConfirmationFooterProps = {
|
||||
i18n: { [key: string]: string };
|
||||
i18n: I18n;
|
||||
theme: Theme;
|
||||
handleButtonClick: (event: Event) => void;
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@ import { Theme } from "@bitwarden/common/platform/enums";
|
||||
import { themes, typography } from "../../constants/styles";
|
||||
|
||||
export type NotificationConfirmationMessageProps = {
|
||||
buttonAria?: string;
|
||||
buttonText?: string;
|
||||
itemName: string;
|
||||
message?: string;
|
||||
messageDetails?: string;
|
||||
handleClick: () => void;
|
||||
@@ -15,15 +15,13 @@ export type NotificationConfirmationMessageProps = {
|
||||
};
|
||||
|
||||
export function NotificationConfirmationMessage({
|
||||
buttonAria,
|
||||
buttonText,
|
||||
itemName,
|
||||
message,
|
||||
messageDetails,
|
||||
handleClick,
|
||||
theme,
|
||||
}: NotificationConfirmationMessageProps) {
|
||||
const buttonAria = chrome.i18n.getMessage("notificationViewAria", [itemName]);
|
||||
|
||||
return html`
|
||||
<div>
|
||||
${message || buttonText
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
NotificationType,
|
||||
} from "../../../notification/abstractions/notification-bar";
|
||||
import { NotificationCipherData } from "../cipher/types";
|
||||
import { CollectionView, FolderView, OrgView } from "../common-types";
|
||||
import { CollectionView, FolderView, I18n, OrgView } from "../common-types";
|
||||
import { themes, spacing } from "../constants/styles";
|
||||
|
||||
import { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body";
|
||||
@@ -27,7 +27,7 @@ export type NotificationContainerProps = NotificationBarIframeInitData & {
|
||||
ciphers?: NotificationCipherData[];
|
||||
collections?: CollectionView[];
|
||||
folders?: FolderView[];
|
||||
i18n: { [key: string]: string };
|
||||
i18n: I18n;
|
||||
organizations?: OrgView[];
|
||||
personalVaultIsAllowed?: boolean;
|
||||
type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type`
|
||||
@@ -99,7 +99,7 @@ const notificationContainerStyles = (theme: Theme) => css`
|
||||
}
|
||||
`;
|
||||
|
||||
function getHeaderMessage(i18n: { [key: string]: string }, type?: NotificationType) {
|
||||
function getHeaderMessage(i18n: I18n, type?: NotificationType) {
|
||||
switch (type) {
|
||||
case NotificationTypes.Add:
|
||||
return i18n.saveLogin;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
NotificationType,
|
||||
NotificationTypes,
|
||||
} from "../../../notification/abstractions/notification-bar";
|
||||
import { OrgView, FolderView, CollectionView } from "../common-types";
|
||||
import { OrgView, FolderView, I18n, CollectionView } from "../common-types";
|
||||
import { spacing, themes } from "../constants/styles";
|
||||
|
||||
import { NotificationButtonRow } from "./button-row";
|
||||
@@ -15,7 +15,7 @@ import { NotificationButtonRow } from "./button-row";
|
||||
export type NotificationFooterProps = {
|
||||
collections?: CollectionView[];
|
||||
folders?: FolderView[];
|
||||
i18n: { [key: string]: string };
|
||||
i18n: I18n;
|
||||
notificationType?: NotificationType;
|
||||
organizations?: OrgView[];
|
||||
personalVaultIsAllowed: boolean;
|
||||
|
||||
@@ -15,17 +15,19 @@ const { css } = createEmotion({
|
||||
key: componentClassPrefix,
|
||||
});
|
||||
|
||||
export type NotificationHeaderProps = {
|
||||
message?: string;
|
||||
standalone?: boolean;
|
||||
theme: Theme;
|
||||
handleCloseNotification: (e: Event) => void;
|
||||
};
|
||||
|
||||
export function NotificationHeader({
|
||||
message,
|
||||
standalone = false,
|
||||
theme = ThemeTypes.Light,
|
||||
handleCloseNotification,
|
||||
}: {
|
||||
message?: string;
|
||||
standalone?: boolean;
|
||||
theme: Theme;
|
||||
handleCloseNotification: (e: Event) => void;
|
||||
}) {
|
||||
}: NotificationHeaderProps) {
|
||||
const showIcon = true;
|
||||
const isDismissable = true;
|
||||
|
||||
|
||||
@@ -12,16 +12,12 @@ const { css } = createEmotion({
|
||||
key: optionItemTagName,
|
||||
});
|
||||
|
||||
export function OptionItem({
|
||||
icon,
|
||||
text,
|
||||
value,
|
||||
theme,
|
||||
handleSelection,
|
||||
}: Option & {
|
||||
export type OptionItemProps = Option & {
|
||||
theme: Theme;
|
||||
handleSelection: () => void;
|
||||
}) {
|
||||
};
|
||||
|
||||
export function OptionItem({ icon, text, value, theme, handleSelection }: OptionItemProps) {
|
||||
const handleSelectionKeyUpProxy = (event: KeyboardEvent) => {
|
||||
const listenedForKeys = new Set(["Enter", "Space"]);
|
||||
if (listenedForKeys.has(event.code) && event.target instanceof Element) {
|
||||
|
||||
@@ -14,19 +14,21 @@ const { css } = createEmotion({
|
||||
key: optionItemsTagName,
|
||||
});
|
||||
|
||||
export type OptionItemsProps = {
|
||||
theme: Theme;
|
||||
topOffset: number;
|
||||
label?: string;
|
||||
options: Option[];
|
||||
handleOptionSelection: (selectedOption: Option) => void;
|
||||
};
|
||||
|
||||
export function OptionItems({
|
||||
theme,
|
||||
topOffset,
|
||||
label,
|
||||
options,
|
||||
handleOptionSelection,
|
||||
}: {
|
||||
theme: Theme;
|
||||
topOffset: number;
|
||||
label?: string;
|
||||
options: Option[];
|
||||
handleOptionSelection: (selectedOption: Option) => void;
|
||||
}) {
|
||||
}: OptionItemsProps) {
|
||||
// @TODO get client vendor from context
|
||||
const isSafari = false;
|
||||
|
||||
|
||||
@@ -5,15 +5,13 @@ import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { spacing, themes, typography } from "../../../content/components/constants/styles";
|
||||
|
||||
export function ActionRow({
|
||||
handleAction,
|
||||
itemText,
|
||||
theme = ThemeTypes.Light,
|
||||
}: {
|
||||
export type ActionRowProps = {
|
||||
itemText: string;
|
||||
handleAction?: (e: Event) => void;
|
||||
theme: Theme;
|
||||
}) {
|
||||
};
|
||||
|
||||
export function ActionRow({ handleAction, itemText, theme = ThemeTypes.Light }: ActionRowProps) {
|
||||
return html`
|
||||
<button type="button" @click=${handleAction} class=${actionRowStyles(theme)} title=${itemText}>
|
||||
<span>${itemText}</span>
|
||||
|
||||
@@ -5,13 +5,12 @@ import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { spacing, themes, typography } from "../../../content/components/constants/styles";
|
||||
|
||||
export function ItemRow({
|
||||
theme = ThemeTypes.Light,
|
||||
children,
|
||||
}: {
|
||||
export type ItemRowProps = {
|
||||
theme: Theme;
|
||||
children: TemplateResult | TemplateResult[];
|
||||
}) {
|
||||
};
|
||||
|
||||
export function ItemRow({ theme = ThemeTypes.Light, children }: ItemRowProps) {
|
||||
return html` <div class=${itemRowStyles({ theme })}>${children}</div> `;
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||
? chrome.runtime.getURL("images/icon38_locked.png")
|
||||
: chrome.runtime.getURL("images/icon38.png");
|
||||
|
||||
setupLogoLink(i18n);
|
||||
setupLogoLink(i18n.appName);
|
||||
|
||||
// i18n for "Add" template
|
||||
const addTemplate = document.getElementById("template-add") as HTMLTemplateElement;
|
||||
@@ -523,9 +523,9 @@ function handleWindowMessage(event: MessageEvent) {
|
||||
handler({ message });
|
||||
}
|
||||
|
||||
function setupLogoLink(i18n: Record<string, string>) {
|
||||
function setupLogoLink(linkText: string) {
|
||||
const logoLink = document.getElementById("logo-link") as HTMLAnchorElement;
|
||||
logoLink.title = i18n.appName;
|
||||
logoLink.title = linkText;
|
||||
const setWebVaultUrlLink = (webVaultURL: string) => {
|
||||
const newVaultURL = webVaultURL && decodeURIComponent(webVaultURL);
|
||||
if (newVaultURL && newVaultURL !== logoLink.href) {
|
||||
|
||||
Reference in New Issue
Block a user