Upgrade Storybook

Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
Jamie Kyle
2023-10-11 12:06:43 -07:00
committed by GitHub
parent 8c966dfbd8
commit 502ea174ab
328 changed files with 10863 additions and 12432 deletions

View File

@@ -13,8 +13,6 @@ import { getBytesSubarray } from './util/uuidToBytes';
export { HashType, CipherType };
export const UUID_BYTE_SIZE = 16;
const PROFILE_IV_LENGTH = 12; // bytes
const PROFILE_KEY_LENGTH = 32; // bytes

View File

@@ -3288,13 +3288,14 @@ export async function startApp(): Promise<void> {
'onDeliveryReceipt: missing valid sourceServiceId'
);
strictAssert(sourceDevice, 'onDeliveryReceipt: missing sourceDevice');
strictAssert(sourceConversation, 'onDeliveryReceipt: missing conversation');
const attributes: MessageReceiptAttributesType = {
envelopeId: ev.deliveryReceipt.envelopeId,
removeFromMessageReceiverCache: ev.confirm,
messageSentAt: timestamp,
receiptTimestamp: envelopeTimestamp,
sourceConversationId: sourceConversation?.id,
sourceConversationId: sourceConversation.id,
sourceServiceId,
sourceDevice,
type: MessageReceipts.MessageReceiptType.Delivery,

View File

@@ -5,8 +5,10 @@ import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './AddGroupMemberErrorDialog';
import {
AddGroupMemberErrorDialog,
AddGroupMemberErrorDialogMode,
@@ -16,24 +18,22 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/AddGroupMemberErrorDialog',
};
} satisfies Meta<PropsType>;
const defaultProps = {
i18n,
onClose: action('onClose'),
};
export const _MaximumGroupSize = (): JSX.Element => (
<AddGroupMemberErrorDialog
{...defaultProps}
mode={AddGroupMemberErrorDialogMode.MaximumGroupSize}
maximumNumberOfContacts={123}
/>
);
_MaximumGroupSize.story = {
name: 'Maximum group size',
};
export function MaximumGroupSize(): JSX.Element {
return (
<AddGroupMemberErrorDialog
{...defaultProps}
mode={AddGroupMemberErrorDialogMode.MaximumGroupSize}
maximumNumberOfContacts={123}
/>
);
}
export function MaximumRecommendedGroupSize(): JSX.Element {
return (
@@ -44,7 +44,3 @@ export function MaximumRecommendedGroupSize(): JSX.Element {
/>
);
}
MaximumRecommendedGroupSize.story = {
name: 'Maximum recommended group size',
};

View File

@@ -23,7 +23,7 @@ type PropsDataType =
recommendedMaximumNumberOfContacts: number;
};
type PropsType = {
export type PropsType = {
i18n: LocalizerType;
onClose: () => void;
} & PropsDataType;

View File

@@ -1,8 +1,9 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { Meta, Story } from '@storybook/react';
import React, { useContext } from 'react';
import type { Meta, StoryFn } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import type { Props } from './AddUserToAnotherGroupModal';
import enMessages from '../../_locales/en/messages.json';
@@ -19,28 +20,25 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/AddUserToAnotherGroupModal',
component: AddUserToAnotherGroupModal,
argTypes: {
candidateConversations: {
defaultValue: Array.from(Array(100), () => getDefaultGroup()),
},
contact: {
defaultValue: getDefaultConversation(),
},
i18n: {
defaultValue: i18n,
},
addMembersToGroup: { action: true },
toggleAddUserToAnotherGroupModal: { action: true },
args: {
i18n,
candidateConversations: Array.from(Array(100), () => getDefaultGroup()),
contact: getDefaultConversation(),
},
} as Meta;
} satisfies Meta<Props>;
// eslint-disable-next-line react/function-component-definition
const Template: Story<Props> = args => (
<AddUserToAnotherGroupModal
{...args}
theme={React.useContext(StorybookThemeContext)}
/>
);
const Template: StoryFn<Props> = args => {
return (
<AddUserToAnotherGroupModal
{...args}
addMembersToGroup={action('addMembersToGroup')}
toggleAddUserToAnotherGroupModal={action(
'toggleAddUserToAnotherGroupModal'
)}
theme={useContext(StorybookThemeContext)}
/>
);
};
export const Modal = Template.bind({});
Modal.args = {};

View File

@@ -2,18 +2,18 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './Alert';
import { Alert } from './Alert';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/Alert',
};
} satisfies Meta<PropsType>;
const defaultProps = {
i18n,
@@ -33,10 +33,6 @@ export function TitleAndBodyAreStrings(): JSX.Element {
);
}
TitleAndBodyAreStrings.story = {
name: 'Title and body are strings',
};
export function BodyIsAReactNode(): JSX.Element {
return (
<Alert
@@ -52,10 +48,6 @@ export function BodyIsAReactNode(): JSX.Element {
);
}
BodyIsAReactNode.story = {
name: 'Body is a ReactNode',
};
export function LongBodyWithoutTitle(): JSX.Element {
return (
<Alert
@@ -72,10 +64,6 @@ export function LongBodyWithoutTitle(): JSX.Element {
);
}
LongBodyWithoutTitle.story = {
name: 'Long body (without title)',
};
export function LongBodyWithTitle(): JSX.Element {
return (
<Alert
@@ -92,7 +80,3 @@ export function LongBodyWithTitle(): JSX.Element {
/>
);
}
LongBodyWithTitle.story = {
name: 'Long body (with title)',
};

View File

@@ -9,7 +9,7 @@ import type { Theme } from '../util/theme';
import { Button } from './Button';
import { Modal } from './Modal';
type PropsType = {
export type PropsType = {
body: ReactNode;
i18n: LocalizerType;
onClose: () => void;

View File

@@ -4,12 +4,13 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './AnimatedEmojiGalore';
import { AnimatedEmojiGalore } from './AnimatedEmojiGalore';
export default {
title: 'Components/AnimatedEmojiGalore',
};
} satisfies Meta<PropsType>;
function getDefaultProps(): PropsType {
return {

View File

@@ -1,13 +1,12 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Meta, Story } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import { expect } from '@storybook/jest';
import { expect, jest } from '@storybook/jest';
import { isBoolean } from 'lodash';
import { within, userEvent } from '@storybook/testing-library';
import type { AvatarColorType } from '../types/Colors';
import type { Props } from './Avatar';
import enMessages from '../../_locales/en/messages.json';
@@ -42,7 +41,6 @@ export default {
},
blur: {
control: { type: 'radio' },
defaultValue: undefined,
options: {
Undefined: undefined,
NoBlur: AvatarBlur.NoBlur,
@@ -51,14 +49,12 @@ export default {
},
},
color: {
defaultValue: AvatarColors[0],
options: colorMap,
},
conversationType: {
control: { type: 'radio' },
options: conversationTypeMap,
},
onClick: { action: true },
size: {
control: false,
},
@@ -68,11 +64,16 @@ export default {
},
theme: {
control: { type: 'radio' },
defaultValue: ThemeType.light,
options: ThemeType,
},
},
} as Meta;
args: {
blur: undefined,
color: AvatarColors[0],
onClick: action('onClick'),
theme: ThemeType.light,
},
} satisfies Meta<Props>;
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
acceptedMessageRequest: isBoolean(overrideProps.acceptedMessageRequest)
@@ -87,7 +88,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
isMe: false,
loading: Boolean(overrideProps.loading),
noteToSelf: Boolean(overrideProps.noteToSelf),
onClick: action('onClick'),
onClick: jest.fn(action('onClick')),
onClickBadge: action('onClickBadge'),
phoneNumber: overrideProps.phoneNumber || '',
searchResult: Boolean(overrideProps.searchResult),
@@ -103,16 +104,18 @@ const sizes = Object.values(AvatarSize).filter(
) as Array<AvatarSize>;
// eslint-disable-next-line react/function-component-definition
const Template: Story<Props> = args => (
<>
{sizes.map(size => (
<Avatar key={size} {...args} size={size} />
))}
</>
);
const Template: StoryFn<Props> = (args: Props) => {
return (
<>
{sizes.map(size => (
<Avatar key={size} {...args} size={size} />
))}
</>
);
};
// eslint-disable-next-line react/function-component-definition
const TemplateSingle: Story<Props> = args => (
const TemplateSingle: StoryFn<Props> = (args: Props) => (
<Avatar {...args} size={AvatarSize.EIGHTY} />
);
@@ -120,72 +123,50 @@ export const Default = Template.bind({});
Default.args = createProps({
avatarPath: '/fixtures/giphy-GVNvOUpeYmI7e.gif',
});
Default.play = async ({ args, canvasElement }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Default.play = async (context: any) => {
const { args, canvasElement } = context;
const canvas = within(canvasElement);
const [avatar] = canvas.getAllByRole('button');
await userEvent.click(avatar);
await expect(args.onClick).toHaveBeenCalled();
};
Default.story = {
name: 'Avatar',
};
export const WithBadge = Template.bind({});
WithBadge.args = createProps({
avatarPath: '/fixtures/kitten-3-64-64.jpg',
badge: getFakeBadge(),
});
WithBadge.story = {
name: 'With badge',
};
export const WideImage = Template.bind({});
WideImage.args = createProps({
avatarPath: '/fixtures/wide.jpg',
});
WideImage.story = {
name: 'Wide image',
};
export const OneWordName = Template.bind({});
OneWordName.args = createProps({
title: 'John',
});
OneWordName.story = {
name: 'One-word Name',
};
export const TwoWordName = Template.bind({});
TwoWordName.args = createProps({
title: 'John Smith',
});
TwoWordName.story = {
name: 'Two-word Name',
};
export const WideInitials = Template.bind({});
WideInitials.args = createProps({
title: 'Walter White',
});
WideInitials.story = {
name: 'Wide initials',
};
export const ThreeWordName = Template.bind({});
ThreeWordName.args = createProps({
title: 'Walter H. White',
});
ThreeWordName.story = {
name: 'Three-word name',
};
export const NoteToSelf = Template.bind({});
NoteToSelf.args = createProps({
noteToSelf: true,
});
NoteToSelf.story = {
name: 'Note to Self',
};
export const ContactIcon = Template.bind({});
ContactIcon.args = createProps();
@@ -227,9 +208,6 @@ BrokenAvatarForGroup.args = createProps({
avatarPath: 'badimage.png',
conversationType: 'group',
});
BrokenAvatarForGroup.story = {
name: 'Broken Avatar for Group',
};
export const Loading = Template.bind({});
Loading.args = createProps({
@@ -242,42 +220,26 @@ BlurredBasedOnProps.args = createProps({
avatarPath: '/fixtures/kitten-3-64-64.jpg',
});
BlurredBasedOnProps.story = {
name: 'Blurred based on props',
};
export const ForceBlurred = TemplateSingle.bind({});
ForceBlurred.args = createProps({
avatarPath: '/fixtures/kitten-3-64-64.jpg',
blur: AvatarBlur.BlurPicture,
});
ForceBlurred.story = {
name: 'Force-blurred',
};
export const BlurredWithClickToView = TemplateSingle.bind({});
BlurredWithClickToView.args = createProps({
avatarPath: '/fixtures/kitten-3-64-64.jpg',
blur: AvatarBlur.BlurPictureWithClickToView,
});
BlurredWithClickToView.story = {
name: 'Blurred with "click to view"',
};
export const StoryUnread = TemplateSingle.bind({});
StoryUnread.args = createProps({
avatarPath: '/fixtures/kitten-3-64-64.jpg',
storyRing: HasStories.Unread,
});
StoryUnread.story = {
name: 'Story: unread',
};
export const StoryRead = TemplateSingle.bind({});
StoryRead.args = createProps({
avatarPath: '/fixtures/kitten-3-64-64.jpg',
storyRing: HasStories.Read,
});
StoryRead.story = {
name: 'Story: read',
};

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -21,7 +22,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/AvatarColorPicker',
};
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
return <AvatarColorPicker {...createProps()} />;

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -80,7 +81,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/AvatarEditor',
};
} satisfies Meta<PropsType>;
export function NoAvatarGroup(): JSX.Element {
return (
@@ -93,20 +94,12 @@ export function NoAvatarGroup(): JSX.Element {
);
}
NoAvatarGroup.story = {
name: 'No Avatar (group)',
};
export function NoAvatarMe(): JSX.Element {
return (
<AvatarEditor {...createProps({ userAvatarData: getDefaultAvatars() })} />
);
}
NoAvatarMe.story = {
name: 'No Avatar (me)',
};
export function HasAvatar(): JSX.Element {
return (
<AvatarEditor

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -23,7 +24,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/AvatarIconEditor',
};
} satisfies Meta<PropsType>;
export function PersonalIcon(): JSX.Element {
return (

View File

@@ -2,10 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { action } from '@storybook/addon-actions';
import { select } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import { AvatarColors } from '../types/Colors';
import type { PropsType } from './AvatarLightbox';
@@ -15,51 +13,37 @@ import { getDefaultConversation } from '../test-both/helpers/getDefaultConversat
const i18n = setupI18n('en', enMessages);
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
avatarColor: select(
'Color',
AvatarColors,
overrideProps.avatarColor || AvatarColors[0]
),
avatarPath: overrideProps.avatarPath,
conversationTitle: overrideProps.conversationTitle,
i18n,
isGroup: Boolean(overrideProps.isGroup),
onClose: action('onClose'),
});
export default {
title: 'Components/AvatarLightbox',
};
component: AvatarLightbox,
argTypes: {
avatarColor: {
control: { type: 'select' },
options: AvatarColors,
},
},
args: {
i18n,
avatarColor: AvatarColors[0],
onClose: action('onClose'),
},
} satisfies Meta<PropsType>;
export function Group(): JSX.Element {
return (
<AvatarLightbox
{...createProps({
isGroup: true,
})}
/>
);
export function Group(args: PropsType): JSX.Element {
return <AvatarLightbox {...args} isGroup />;
}
export function Person(): JSX.Element {
export function Person(args: PropsType): JSX.Element {
const conversation = getDefaultConversation();
return (
<AvatarLightbox
{...createProps({
avatarColor: conversation.color,
conversationTitle: conversation.title,
})}
{...args}
avatarColor={conversation.color}
conversationTitle={conversation.title}
/>
);
}
export function Photo(): JSX.Element {
return (
<AvatarLightbox
{...createProps({
avatarPath: '/fixtures/kitten-1-64-64.jpg',
})}
/>
);
export function Photo(args: PropsType): JSX.Element {
return <AvatarLightbox {...args} avatarPath="/fixtures/kitten-1-64-64.jpg" />;
}

View File

@@ -5,6 +5,7 @@ import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './AvatarModalButtons';
import { AvatarModalButtons } from './AvatarModalButtons';
@@ -21,7 +22,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/AvatarModalButtons',
};
} satisfies Meta<PropsType>;
export function HasChanges(): JSX.Element {
return (
@@ -33,14 +34,6 @@ export function HasChanges(): JSX.Element {
);
}
HasChanges.story = {
name: 'Has changes',
};
export function NoChanges(): JSX.Element {
return <AvatarModalButtons {...createProps()} />;
}
NoChanges.story = {
name: 'No changes',
};

View File

@@ -6,6 +6,7 @@ import { chunk } from 'lodash';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './AvatarPreview';
import { AvatarPreview } from './AvatarPreview';
import { AvatarColors } from '../types/Colors';
@@ -37,7 +38,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/AvatarPreview',
};
} satisfies Meta<PropsType>;
export function NoStatePersonal(): JSX.Element {
return (
@@ -50,10 +51,6 @@ export function NoStatePersonal(): JSX.Element {
);
}
NoStatePersonal.story = {
name: 'No state (personal)',
};
export function NoStateGroup(): JSX.Element {
return (
<AvatarPreview
@@ -65,10 +62,6 @@ export function NoStateGroup(): JSX.Element {
);
}
NoStateGroup.story = {
name: 'No state (group)',
};
export function NoStateGroupUploadMe(): JSX.Element {
return (
<AvatarPreview
@@ -81,18 +74,10 @@ export function NoStateGroupUploadMe(): JSX.Element {
);
}
NoStateGroupUploadMe.story = {
name: 'No state (group) + upload me',
};
export function Value(): JSX.Element {
return <AvatarPreview {...createProps({ avatarValue: TEST_IMAGE })} />;
}
Value.story = {
name: 'value',
};
export function Path(): JSX.Element {
return (
<AvatarPreview
@@ -101,11 +86,7 @@ export function Path(): JSX.Element {
);
}
Path.story = {
name: 'path',
};
export function ValuePath(): JSX.Element {
export function ValueAndPath(): JSX.Element {
return (
<AvatarPreview
{...createProps({
@@ -116,10 +97,6 @@ export function ValuePath(): JSX.Element {
);
}
ValuePath.story = {
name: 'value & path',
};
export function Style(): JSX.Element {
return (
<AvatarPreview
@@ -130,7 +107,3 @@ export function Style(): JSX.Element {
/>
);
}
Style.story = {
name: 'style',
};

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -22,7 +23,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/AvatarTextEditor',
};
} satisfies Meta<PropsType>;
export function Empty(): JSX.Element {
return <AvatarTextEditor {...createProps()} />;
@@ -42,10 +43,6 @@ export function WithData(): JSX.Element {
);
}
WithData.story = {
name: 'with Data',
};
export function WithWideCharacters(): JSX.Element {
return (
<AvatarTextEditor
@@ -59,7 +56,3 @@ export function WithWideCharacters(): JSX.Element {
/>
);
}
WithWideCharacters.story = {
name: 'with wide characters',
};

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -20,7 +21,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/AvatarUploadButton',
};
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
return <AvatarUploadButton {...createProps()} />;

View File

@@ -3,11 +3,13 @@
import React from 'react';
import type { Meta } from '@storybook/react';
import type { Props } from './BadgeDescription';
import { BadgeDescription } from './BadgeDescription';
export default {
title: 'Components/BadgeDescription',
};
} satisfies Meta<Props>;
export function NormalName(): JSX.Element {
return (
@@ -19,11 +21,7 @@ export function NormalName(): JSX.Element {
);
}
NormalName.story = {
name: 'Normal name',
};
export function NameWithRtlOverrides(): JSX.Element {
export function NameWithRTLOverrides(): JSX.Element {
return (
<BadgeDescription
template="Hello, {short_name}! {short_name}, I think you're great."
@@ -31,7 +29,3 @@ export function NameWithRtlOverrides(): JSX.Element {
/>
);
}
NameWithRtlOverrides.story = {
name: 'Name with RTL overrides',
};

View File

@@ -5,15 +5,17 @@ import type { ReactChild, ReactElement } from 'react';
import React from 'react';
import { ContactName } from './conversation/ContactName';
export type Props = Readonly<{
firstName?: string;
template: string;
title: string;
}>;
export function BadgeDescription({
firstName,
template,
title,
}: Readonly<{
firstName?: string;
template: string;
title: string;
}>): ReactElement {
}: Props): ReactElement {
const result: Array<ReactChild> = [];
let lastIndex = 0;

View File

@@ -5,18 +5,20 @@ import type { ComponentProps } from 'react';
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import { getFakeBadge, getFakeBadges } from '../test-both/helpers/getFakeBadge';
import { repeat, zipObject } from '../util/iterables';
import { BadgeImageTheme } from '../badges/BadgeImageTheme';
import type { PropsType } from './BadgeDialog';
import { BadgeDialog } from './BadgeDialog';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/BadgeDialog',
};
} satisfies Meta<PropsType>;
const defaultProps: ComponentProps<typeof BadgeDialog> = {
areWeASubscriber: false,
@@ -31,18 +33,10 @@ export function NoBadgesClosedImmediately(): JSX.Element {
return <BadgeDialog {...defaultProps} badges={[]} />;
}
NoBadgesClosedImmediately.story = {
name: 'No badges (closed immediately)',
};
export function OneBadge(): JSX.Element {
return <BadgeDialog {...defaultProps} badges={getFakeBadges(1)} />;
}
OneBadge.story = {
name: 'One badge',
};
export function BadgeWithNoImageShouldBeImpossible(): JSX.Element {
return (
<BadgeDialog
@@ -57,10 +51,6 @@ export function BadgeWithNoImageShouldBeImpossible(): JSX.Element {
);
}
BadgeWithNoImageShouldBeImpossible.story = {
name: 'Badge with no image (should be impossible)',
};
export function BadgeWithPendingImage(): JSX.Element {
return (
<BadgeDialog
@@ -80,10 +70,6 @@ export function BadgeWithPendingImage(): JSX.Element {
);
}
BadgeWithPendingImage.story = {
name: 'Badge with pending image',
};
export function BadgeWithOnlyOneLowDetailImage(): JSX.Element {
return (
<BadgeDialog
@@ -112,26 +98,14 @@ export function BadgeWithOnlyOneLowDetailImage(): JSX.Element {
);
}
BadgeWithOnlyOneLowDetailImage.story = {
name: 'Badge with only one, low-detail image',
};
export function FiveBadges(): JSX.Element {
return <BadgeDialog {...defaultProps} badges={getFakeBadges(5)} />;
}
FiveBadges.story = {
name: 'Five badges',
};
export function ManyBadges(): JSX.Element {
return <BadgeDialog {...defaultProps} badges={getFakeBadges(50)} />;
}
ManyBadges.story = {
name: 'Many badges',
};
export function ManyBadgesUserIsASubscriber(): JSX.Element {
return (
<BadgeDialog
@@ -141,7 +115,3 @@ export function ManyBadgesUserIsASubscriber(): JSX.Element {
/>
);
}
ManyBadgesUserIsASubscriber.story = {
name: 'Many badges, user is a subscriber',
};

View File

@@ -15,7 +15,7 @@ import { BadgeImage } from './BadgeImage';
import { BadgeCarouselIndex } from './BadgeCarouselIndex';
import { BadgeSustainerInstructionsDialog } from './BadgeSustainerInstructionsDialog';
type PropsType = Readonly<{
export type PropsType = Readonly<{
areWeASubscriber: boolean;
badges: ReadonlyArray<BadgeType>;
firstName?: string;

View File

@@ -5,6 +5,7 @@ import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import { AvatarColors } from '../types/Colors';
import { GroupAvatarIcons, PersonalAvatarIcons } from '../types/Avatar';
@@ -28,7 +29,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/BetterAvatar',
};
} satisfies Meta<PropsType>;
export function Text(): JSX.Element {
return (

View File

@@ -5,6 +5,7 @@ import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import { AvatarColors } from '../types/Colors';
import type { PropsType } from './BetterAvatarBubble';
@@ -25,7 +26,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/BetterAvatarBubble',
};
} satisfies Meta<PropsType>;
export function Children(): JSX.Element {
return (

View File

@@ -3,12 +3,13 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './Button';
import { Button, ButtonSize, ButtonVariant } from './Button';
export default {
title: 'Components/Button',
};
} satisfies Meta<PropsType>;
export function KitchenSink(): JSX.Element {
return (
@@ -44,10 +45,6 @@ export function KitchenSink(): JSX.Element {
);
}
KitchenSink.story = {
name: 'Kitchen sink',
};
export function AriaLabel(): JSX.Element {
return (
<Button
@@ -58,10 +55,6 @@ export function AriaLabel(): JSX.Element {
);
}
AriaLabel.story = {
name: 'aria-label',
};
export function CustomStyles(): JSX.Element {
return (
<Button onClick={action('onClick')} style={{ transform: 'rotate(5deg)' }}>
@@ -69,7 +62,3 @@ export function CustomStyles(): JSX.Element {
</Button>
);
}
CustomStyles.story = {
name: 'Custom styles',
};

View File

@@ -40,7 +40,7 @@ export enum ButtonIconType {
video = 'video',
}
type PropsType = {
export type PropsType = {
className?: string;
disabled?: boolean;
icon?: ButtonIconType;

View File

@@ -3,8 +3,7 @@
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import { boolean, select, text } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import type { PropsType } from './CallManager';
import { CallManager } from './CallManager';
import {
@@ -16,7 +15,6 @@ import {
GroupCallJoinState,
} from '../types/Calling';
import type { ConversationTypeType } from '../state/ducks/conversations';
import type { AvatarColorType } from '../types/Colors';
import { AvatarColors } from '../types/Colors';
import { generateAci } from '../types/ServiceId';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
@@ -33,13 +31,9 @@ const getConversation = () =>
getDefaultConversation({
id: '3051234567',
avatarPath: undefined,
color: select(
'Callee color',
AvatarColors,
'ultramarine' as AvatarColorType
),
title: text('Callee Title', 'Rick Sanchez'),
name: text('Callee Name', 'Rick Sanchez'),
color: AvatarColors[0],
title: 'Rick Sanchez',
name: 'Rick Sanchez',
phoneNumber: '3051234567',
profileName: 'Rick Sanchez',
markedUnread: false,
@@ -50,18 +44,14 @@ const getConversation = () =>
const getCommonActiveCallData = () => ({
conversation: getConversation(),
joinedAt: Date.now(),
hasLocalAudio: boolean('hasLocalAudio', true),
hasLocalVideo: boolean('hasLocalVideo', false),
localAudioLevel: select('localAudioLevel', [0, 0.5, 1], 0),
viewMode: select(
'viewMode',
[CallViewMode.Grid, CallViewMode.Presentation, CallViewMode.Speaker],
CallViewMode.Grid
),
outgoingRing: boolean('outgoingRing', true),
pip: boolean('pip', false),
settingsDialogOpen: boolean('settingsDialogOpen', false),
showParticipantsList: boolean('showParticipantsList', false),
hasLocalAudio: true,
hasLocalVideo: false,
localAudioLevel: 0,
viewMode: CallViewMode.Grid,
outgoingRing: true,
pip: false,
settingsDialogOpen: false,
showParticipantsList: false,
});
const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
@@ -83,12 +73,8 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
keyChangeOk: action('key-change-ok'),
me: {
...getDefaultConversation({
color: select(
'Caller color',
AvatarColors,
'ultramarine' as AvatarColorType
),
title: text('Caller Title', 'Morty Smith'),
color: AvatarColors[0],
title: 'Morty Smith',
}),
serviceId: generateAci(),
},
@@ -123,7 +109,9 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/CallManager',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
export function NoCall(): JSX.Element {
return <CallManager {...createProps()} />;
@@ -184,10 +172,6 @@ export function RingingDirectCall(): JSX.Element {
);
}
RingingDirectCall.story = {
name: 'Ringing (direct call)',
};
export function RingingGroupCall(): JSX.Element {
return (
<CallManager
@@ -212,10 +196,6 @@ export function RingingGroupCall(): JSX.Element {
);
}
RingingGroupCall.story = {
name: 'Ringing (group call)',
};
export function CallRequestNeeded(): JSX.Element {
return (
<CallManager
@@ -263,7 +243,3 @@ export function GroupCallSafetyNumberChanged(): JSX.Element {
/>
);
}
GroupCallSafetyNumberChanged.story = {
name: 'Group call - Safety Number Changed',
};

View File

@@ -3,9 +3,9 @@
import * as React from 'react';
import { times } from 'lodash';
import { boolean, select, number } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { GroupCallRemoteParticipantType } from '../types/Calling';
import {
CallMode,
@@ -60,6 +60,7 @@ type GroupCallOverrideProps = OverridePropsBase & {
connectionState?: GroupCallConnectionState;
peekedParticipants?: Array<ConversationType>;
remoteParticipants?: Array<GroupCallRemoteParticipantType>;
remoteAudioLevel?: number;
};
const createActiveDirectCallProp = (
@@ -67,18 +68,11 @@ const createActiveDirectCallProp = (
) => ({
callMode: CallMode.Direct as CallMode.Direct,
conversation,
callState: select(
'callState',
CallState,
overrideProps.callState || CallState.Accepted
),
callState: overrideProps.callState ?? CallState.Accepted,
peekedParticipants: [] as [],
remoteParticipants: [
{
hasRemoteVideo: boolean(
'hasRemoteVideo',
Boolean(overrideProps.hasRemoteVideo)
),
hasRemoteVideo: overrideProps.hasRemoteVideo ?? false,
presenting: false,
title: 'test',
},
@@ -109,12 +103,7 @@ const createActiveGroupCallProp = (overrideProps: GroupCallOverrideProps) => ({
remoteAudioLevels: new Map<number, number>(
overrideProps.remoteParticipants?.map((_participant, index) => [
index,
number('remoteAudioLevel', 0, {
range: true,
min: 0,
max: 1,
step: 0.01,
}),
overrideProps.remoteAudioLevel ?? 0,
])
),
});
@@ -125,24 +114,10 @@ const createActiveCallProp = (
const baseResult = {
joinedAt: Date.now(),
conversation,
hasLocalAudio: boolean(
'hasLocalAudio',
overrideProps.hasLocalAudio || false
),
hasLocalVideo: boolean(
'hasLocalVideo',
overrideProps.hasLocalVideo || false
),
localAudioLevel: select(
'localAudioLevel',
[0, 0.5, 1],
overrideProps.localAudioLevel || 0
),
viewMode: select(
'viewMode',
[CallViewMode.Grid, CallViewMode.Speaker, CallViewMode.Presentation],
overrideProps.viewMode || CallViewMode.Grid
),
hasLocalAudio: overrideProps.hasLocalAudio ?? false,
hasLocalVideo: overrideProps.hasLocalVideo ?? false,
localAudioLevel: overrideProps.localAudioLevel ?? 0,
viewMode: overrideProps.viewMode ?? CallViewMode.Grid,
outgoingRing: true,
pip: false,
settingsDialogOpen: false,
@@ -184,7 +159,7 @@ const createProps = (
setLocalVideo: action('set-local-video'),
setPresenting: action('toggle-presenting'),
setRendererCanvas: action('set-renderer-canvas'),
stickyControls: boolean('stickyControls', false),
stickyControls: false,
switchToPresentationView: action('switch-to-presentation-view'),
switchFromPresentationView: action('switch-from-presentation-view'),
toggleParticipants: action('toggle-participants'),
@@ -198,7 +173,9 @@ const createProps = (
export default {
title: 'Components/CallScreen',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
return <CallScreen {...createProps()} />;
@@ -215,11 +192,7 @@ export function PreRing(): JSX.Element {
);
}
PreRing.story = {
name: 'Pre-Ring',
};
export const _Ringing = (): JSX.Element => {
export function Ringing(): JSX.Element {
return (
<CallScreen
{...createProps({
@@ -228,9 +201,9 @@ export const _Ringing = (): JSX.Element => {
})}
/>
);
};
}
export const _Reconnecting = (): JSX.Element => {
export function Reconnecting(): JSX.Element {
return (
<CallScreen
{...createProps({
@@ -239,9 +212,9 @@ export const _Reconnecting = (): JSX.Element => {
})}
/>
);
};
}
export const _Ended = (): JSX.Element => {
export function Ended(): JSX.Element {
return (
<CallScreen
{...createProps({
@@ -250,7 +223,7 @@ export const _Ended = (): JSX.Element => {
})}
/>
);
};
}
export function HasLocalAudio(): JSX.Element {
return (
@@ -263,10 +236,6 @@ export function HasLocalAudio(): JSX.Element {
);
}
HasLocalAudio.story = {
name: 'hasLocalAudio',
};
export function HasLocalVideo(): JSX.Element {
return (
<CallScreen
@@ -278,10 +247,6 @@ export function HasLocalVideo(): JSX.Element {
);
}
HasLocalVideo.story = {
name: 'hasLocalVideo',
};
export function HasRemoteVideo(): JSX.Element {
return (
<CallScreen
@@ -293,10 +258,6 @@ export function HasRemoteVideo(): JSX.Element {
);
}
HasRemoteVideo.story = {
name: 'hasRemoteVideo',
};
export function GroupCall1(): JSX.Element {
return (
<CallScreen
@@ -323,10 +284,6 @@ export function GroupCall1(): JSX.Element {
);
}
GroupCall1.story = {
name: 'Group call - 1',
};
// We generate these upfront so that the list is stable when you move the slider.
const allRemoteParticipants = times(MAX_PARTICIPANTS).map(index => ({
aci: generateAci(),
@@ -347,24 +304,12 @@ export function GroupCallMany(): JSX.Element {
<CallScreen
{...createProps({
callMode: CallMode.Group,
remoteParticipants: allRemoteParticipants.slice(
0,
number('Participant count', 40, {
range: true,
min: 0,
max: MAX_PARTICIPANTS,
step: 1,
})
),
remoteParticipants: allRemoteParticipants.slice(0, 40),
})}
/>
);
}
GroupCallMany.story = {
name: 'Group call - Many',
};
export function GroupCallReconnecting(): JSX.Element {
return (
<CallScreen
@@ -392,10 +337,6 @@ export function GroupCallReconnecting(): JSX.Element {
);
}
GroupCallReconnecting.story = {
name: 'Group call - reconnecting',
};
export function GroupCall0(): JSX.Element {
return (
<CallScreen
@@ -407,10 +348,6 @@ export function GroupCall0(): JSX.Element {
);
}
GroupCall0.story = {
name: 'Group call - 0',
};
export function GroupCallSomeoneIsSharingScreen(): JSX.Element {
return (
<CallScreen
@@ -428,10 +365,6 @@ export function GroupCallSomeoneIsSharingScreen(): JSX.Element {
);
}
GroupCallSomeoneIsSharingScreen.story = {
name: 'Group call - someone is sharing screen',
};
export function GroupCallSomeoneIsSharingScreenAndYoureReconnecting(): JSX.Element {
return (
<CallScreen
@@ -449,7 +382,3 @@ export function GroupCallSomeoneIsSharingScreenAndYoureReconnecting(): JSX.Eleme
/>
);
}
GroupCallSomeoneIsSharingScreenAndYoureReconnecting.story = {
name: "Group call - someone is sharing screen and you're reconnecting",
};

View File

@@ -2,8 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState, useEffect } from 'react';
import { boolean } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import type { Props } from './CallingAudioIndicator';
import {
CallingAudioIndicator,
SPEAKING_LINGER_MS,
@@ -13,9 +13,18 @@ import { useValueAtFixedRate } from '../hooks/useValueAtFixedRate';
export default {
title: 'Components/CallingAudioIndicator',
};
component: CallingAudioIndicator,
argTypes: {
hasAudio: {
control: { type: 'boolean' },
},
},
args: {
hasAudio: true,
},
} satisfies Meta<Props>;
export function Extreme(): JSX.Element {
export function Extreme(args: Props): JSX.Element {
const [audioLevel, setAudioLevel] = useState(1);
useEffect(() => {
@@ -32,14 +41,14 @@ export function Extreme(): JSX.Element {
return (
<CallingAudioIndicator
hasAudio={boolean('hasAudio', true)}
hasAudio={args.hasAudio}
audioLevel={audioLevel}
shouldShowSpeaking={isSpeaking}
/>
);
}
export function Random(): JSX.Element {
export function Random(args: Props): JSX.Element {
const [audioLevel, setAudioLevel] = useState(1);
useEffect(() => {
@@ -56,7 +65,7 @@ export function Random(): JSX.Element {
return (
<CallingAudioIndicator
hasAudio={boolean('hasAudio', true)}
hasAudio={args.hasAudio}
audioLevel={audioLevel}
shouldShowSpeaking={isSpeaking}
/>

View File

@@ -99,15 +99,17 @@ function Bars({ audioLevel }: { audioLevel: number }): ReactElement {
);
}
export type Props = Readonly<{
hasAudio: boolean;
audioLevel: number;
shouldShowSpeaking: boolean;
}>;
export function CallingAudioIndicator({
hasAudio,
audioLevel,
shouldShowSpeaking,
}: Readonly<{
hasAudio: boolean;
audioLevel: number;
shouldShowSpeaking: boolean;
}>): ReactElement {
}: Props): ReactElement {
if (!hasAudio) {
return (
<div

View File

@@ -2,9 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { select } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './CallingButton';
import { CallingButton, CallingButtonType } from './CallingButton';
import { TooltipPlacement } from './Tooltip';
@@ -13,101 +12,79 @@ import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
buttonType:
overrideProps.buttonType ||
select('buttonType', CallingButtonType, CallingButtonType.HANG_UP),
i18n,
onClick: action('on-click'),
onMouseEnter: action('on-mouse-enter'),
onMouseLeave: action('on-mouse-leave'),
tooltipDirection: select(
'tooltipDirection',
TooltipPlacement,
overrideProps.tooltipDirection || TooltipPlacement.Bottom
),
});
export default {
title: 'Components/CallingButton',
};
component: CallingButton,
argTypes: {
buttonType: {
control: { type: 'select' },
options: Object.values(CallingButtonType),
},
tooltipDirection: {
control: { type: 'select' },
options: Object.values(TooltipPlacement),
},
},
args: {
buttonType: CallingButtonType.HANG_UP,
i18n,
onClick: action('on-click'),
onMouseEnter: action('on-mouse-enter'),
onMouseLeave: action('on-mouse-leave'),
tooltipDirection: TooltipPlacement.Bottom,
},
} satisfies Meta<PropsType>;
export function KitchenSink(): JSX.Element {
export function KitchenSink(args: PropsType): JSX.Element {
return (
<>
{Object.keys(CallingButtonType).map(buttonType => (
<CallingButton
key={buttonType}
{...createProps({ buttonType: buttonType as CallingButtonType })}
/>
{Object.values(CallingButtonType).map(buttonType => (
<CallingButton key={buttonType} {...args} buttonType={buttonType} />
))}
</>
);
}
export function AudioOn(): JSX.Element {
const props = createProps({
buttonType: CallingButtonType.AUDIO_ON,
});
return <CallingButton {...props} />;
export function AudioOn(args: PropsType): JSX.Element {
return <CallingButton {...args} buttonType={CallingButtonType.AUDIO_ON} />;
}
export function AudioOff(): JSX.Element {
const props = createProps({
buttonType: CallingButtonType.AUDIO_OFF,
});
return <CallingButton {...props} />;
export function AudioOff(args: PropsType): JSX.Element {
return <CallingButton {...args} buttonType={CallingButtonType.AUDIO_OFF} />;
}
export function AudioDisabled(): JSX.Element {
const props = createProps({
buttonType: CallingButtonType.AUDIO_DISABLED,
});
return <CallingButton {...props} />;
export function AudioDisabled(args: PropsType): JSX.Element {
return (
<CallingButton {...args} buttonType={CallingButtonType.AUDIO_DISABLED} />
);
}
export function VideoOn(): JSX.Element {
const props = createProps({
buttonType: CallingButtonType.VIDEO_ON,
});
return <CallingButton {...props} />;
export function VideoOn(args: PropsType): JSX.Element {
return <CallingButton {...args} buttonType={CallingButtonType.VIDEO_ON} />;
}
export function VideoOff(): JSX.Element {
const props = createProps({
buttonType: CallingButtonType.VIDEO_OFF,
});
return <CallingButton {...props} />;
export function VideoOff(args: PropsType): JSX.Element {
return <CallingButton {...args} buttonType={CallingButtonType.VIDEO_OFF} />;
}
export function VideoDisabled(): JSX.Element {
const props = createProps({
buttonType: CallingButtonType.VIDEO_DISABLED,
});
return <CallingButton {...props} />;
export function VideoDisabled(args: PropsType): JSX.Element {
return (
<CallingButton {...args} buttonType={CallingButtonType.VIDEO_DISABLED} />
);
}
export function TooltipRight(): JSX.Element {
const props = createProps({
tooltipDirection: TooltipPlacement.Right,
});
return <CallingButton {...props} />;
export function TooltipRight(args: PropsType): JSX.Element {
return <CallingButton {...args} tooltipDirection={TooltipPlacement.Right} />;
}
TooltipRight.story = {
name: 'Tooltip right',
};
export function PresentingOn(): JSX.Element {
const props = createProps({
buttonType: CallingButtonType.PRESENTING_ON,
});
return <CallingButton {...props} />;
export function PresentingOn(args: PropsType): JSX.Element {
return (
<CallingButton {...args} buttonType={CallingButtonType.PRESENTING_ON} />
);
}
export function PresentingOff(): JSX.Element {
const props = createProps({
buttonType: CallingButtonType.PRESENTING_OFF,
});
return <CallingButton {...props} />;
export function PresentingOff(args: PropsType): JSX.Element {
return (
<CallingButton {...args} buttonType={CallingButtonType.PRESENTING_OFF} />
);
}

View File

@@ -4,6 +4,7 @@
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { Props } from './CallingDeviceSelection';
import { CallingDeviceSelection } from './CallingDeviceSelection';
import { setupI18n } from '../util/setupI18n';
@@ -39,7 +40,7 @@ const createProps = ({
export default {
title: 'Components/CallingDeviceSelection',
};
} satisfies Meta<Props>;
export function Default(): JSX.Element {
return <CallingDeviceSelection {...createProps()} />;

View File

@@ -2,9 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { boolean, number } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './CallingHeader';
import { CallingHeader } from './CallingHeader';
import { setupI18n } from '../util/setupI18n';
@@ -12,36 +11,35 @@ import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
i18n,
isGroupCall: boolean('isGroupCall', Boolean(overrideProps.isGroupCall)),
message: overrideProps.message,
participantCount: number(
'participantCount',
overrideProps.participantCount || 0
),
showParticipantsList: boolean(
'showParticipantsList',
Boolean(overrideProps.showParticipantsList)
),
title: overrideProps.title || 'With Someone',
toggleParticipants: () => action('toggle-participants'),
togglePip: () => action('toggle-pip'),
toggleSettings: () => action('toggle-settings'),
});
export default {
title: 'Components/CallingHeader',
};
component: CallingHeader,
argTypes: {
isGroupCall: { control: { type: 'boolean' } },
participantCount: { control: { type: 'number' } },
title: { control: { type: 'text' } },
},
args: {
i18n,
isGroupCall: false,
message: '',
participantCount: 0,
showParticipantsList: false,
title: 'With Someone',
toggleParticipants: action('toggle-participants'),
togglePip: action('toggle-pip'),
toggleSettings: action('toggle-settings'),
},
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
return <CallingHeader {...createProps()} />;
export function Default(args: PropsType): JSX.Element {
return <CallingHeader {...args} />;
}
export function LobbyStyle(): JSX.Element {
export function LobbyStyle(args: PropsType): JSX.Element {
return (
<CallingHeader
{...createProps()}
{...args}
title={undefined}
togglePip={undefined}
onCancel={action('onClose')}
@@ -49,59 +47,32 @@ export function LobbyStyle(): JSX.Element {
);
}
LobbyStyle.story = {
name: 'Lobby style',
};
export function WithParticipants(args: PropsType): JSX.Element {
return <CallingHeader {...args} isGroupCall participantCount={10} />;
}
export function WithParticipants(): JSX.Element {
export function WithParticipantsShown(args: PropsType): JSX.Element {
return (
<CallingHeader
{...createProps({
isGroupCall: true,
participantCount: 10,
})}
{...args}
isGroupCall
participantCount={10}
showParticipantsList
/>
);
}
export function WithParticipantsShown(): JSX.Element {
export function LongTitle(args: PropsType): JSX.Element {
return (
<CallingHeader
{...createProps({
isGroupCall: true,
participantCount: 10,
showParticipantsList: true,
})}
{...args}
title="What do I got to, what do I got to do to wake you up? To shake you up, to break the structure up?"
/>
);
}
WithParticipantsShown.story = {
name: 'With Participants (shown)',
};
export function LongTitle(): JSX.Element {
export function TitleWithMessage(args: PropsType): JSX.Element {
return (
<CallingHeader
{...createProps({
title:
'What do I got to, what do I got to do to wake you up? To shake you up, to break the structure up?',
})}
/>
<CallingHeader {...args} title="Hello world" message="Goodbye earth" />
);
}
export function TitleWithMessage(): JSX.Element {
return (
<CallingHeader
{...createProps({
title: 'Hello world',
message: 'Goodbye earth',
})}
/>
);
}
TitleWithMessage.story = {
name: 'Title with message',
};

View File

@@ -3,10 +3,10 @@
import * as React from 'react';
import { times } from 'lodash';
import { boolean } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import { v4 as generateUuid } from 'uuid';
import type { Meta } from '@storybook/react';
import { AvatarColors } from '../types/Colors';
import type { ConversationType } from '../state/ducks/conversations';
import type { PropsType } from './CallingLobby';
@@ -32,10 +32,7 @@ const camera = {
};
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
const isGroupCall = boolean(
'isGroupCall',
overrideProps.isGroupCall || false
);
const isGroupCall = overrideProps.isGroupCall ?? false;
const conversation = isGroupCall
? getDefaultConversation({
title: 'Tahoe Trip',
@@ -49,19 +46,13 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
groupMembers:
overrideProps.groupMembers ||
(isGroupCall ? times(3, () => getDefaultConversation()) : undefined),
hasLocalAudio: boolean(
'hasLocalAudio',
overrideProps.hasLocalAudio ?? true
),
hasLocalVideo: boolean(
'hasLocalVideo',
overrideProps.hasLocalVideo ?? false
),
hasLocalAudio: overrideProps.hasLocalAudio ?? true,
hasLocalVideo: overrideProps.hasLocalVideo ?? false,
i18n,
isGroupCall,
isGroupCallOutboundRingEnabled: true,
isConversationTooBigToRing: false,
isCallFull: boolean('isCallFull', overrideProps.isCallFull || false),
isCallFull: overrideProps.isCallFull ?? false,
me:
overrideProps.me ||
getDefaultConversation({
@@ -71,16 +62,13 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
}),
onCallCanceled: action('on-call-canceled'),
onJoinCall: action('on-join-call'),
outgoingRing: boolean('outgoingRing', Boolean(overrideProps.outgoingRing)),
outgoingRing: overrideProps.outgoingRing ?? false,
peekedParticipants: overrideProps.peekedParticipants || [],
setLocalAudio: action('set-local-audio'),
setLocalPreview: action('set-local-preview'),
setLocalVideo: action('set-local-video'),
setOutgoingRing: action('set-outgoing-ring'),
showParticipantsList: boolean(
'showParticipantsList',
Boolean(overrideProps.showParticipantsList)
),
showParticipantsList: overrideProps.showParticipantsList ?? false,
toggleParticipants: action('toggle-participants'),
toggleSettings: action('toggle-settings'),
};
@@ -93,7 +81,9 @@ const fakePeekedParticipant = (conversationProps: Partial<ConversationType>) =>
export default {
title: 'Components/CallingLobby',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
const props = createProps();
@@ -107,10 +97,6 @@ export function NoCameraNoAvatar(): JSX.Element {
return <CallingLobby {...props} />;
}
NoCameraNoAvatar.story = {
name: 'No Camera, no avatar',
};
export function NoCameraLocalAvatar(): JSX.Element {
const props = createProps({
availableCameras: [],
@@ -124,10 +110,6 @@ export function NoCameraLocalAvatar(): JSX.Element {
return <CallingLobby {...props} />;
}
NoCameraLocalAvatar.story = {
name: 'No Camera, local avatar',
};
export function LocalVideo(): JSX.Element {
const props = createProps({
hasLocalVideo: true,
@@ -142,20 +124,12 @@ export function InitiallyMuted(): JSX.Element {
return <CallingLobby {...props} />;
}
InitiallyMuted.story = {
name: 'Initially muted',
};
export function GroupCall0PeekedParticipants(): JSX.Element {
export function GroupCallWithNoPeekedParticipants(): JSX.Element {
const props = createProps({ isGroupCall: true, peekedParticipants: [] });
return <CallingLobby {...props} />;
}
GroupCall0PeekedParticipants.story = {
name: 'Group Call - 0 peeked participants',
};
export function GroupCall1PeekedParticipant(): JSX.Element {
export function GroupCallWith1PeekedParticipant(): JSX.Element {
const props = createProps({
isGroupCall: true,
peekedParticipants: [{ title: 'Sam' }].map(fakePeekedParticipant),
@@ -163,11 +137,7 @@ export function GroupCall1PeekedParticipant(): JSX.Element {
return <CallingLobby {...props} />;
}
GroupCall1PeekedParticipant.story = {
name: 'Group Call - 1 peeked participant',
};
export function GroupCall1PeekedParticipantSelf(): JSX.Element {
export function GroupCallWith1PeekedParticipantSelf(): JSX.Element {
const serviceId = generateAci();
const props = createProps({
isGroupCall: true,
@@ -180,11 +150,7 @@ export function GroupCall1PeekedParticipantSelf(): JSX.Element {
return <CallingLobby {...props} />;
}
GroupCall1PeekedParticipantSelf.story = {
name: 'Group Call - 1 peeked participant (self)',
};
export function GroupCall4PeekedParticipants(): JSX.Element {
export function GroupCallWith4PeekedParticipants(): JSX.Element {
const props = createProps({
isGroupCall: true,
peekedParticipants: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'].map(title =>
@@ -194,11 +160,7 @@ export function GroupCall4PeekedParticipants(): JSX.Element {
return <CallingLobby {...props} />;
}
GroupCall4PeekedParticipants.story = {
name: 'Group Call - 4 peeked participants',
};
export function GroupCall4PeekedParticipantsParticipantsList(): JSX.Element {
export function GroupCallWith4PeekedParticipantsParticipantsList(): JSX.Element {
const props = createProps({
isGroupCall: true,
peekedParticipants: ['Sam', 'Cayce', 'April', 'Logan', 'Carl'].map(title =>
@@ -209,11 +171,7 @@ export function GroupCall4PeekedParticipantsParticipantsList(): JSX.Element {
return <CallingLobby {...props} />;
}
GroupCall4PeekedParticipantsParticipantsList.story = {
name: 'Group Call - 4 peeked participants (participants list)',
};
export function GroupCallCallFull(): JSX.Element {
export function GroupCallWithCallFull(): JSX.Element {
const props = createProps({
isGroupCall: true,
isCallFull: true,
@@ -224,18 +182,10 @@ export function GroupCallCallFull(): JSX.Element {
return <CallingLobby {...props} />;
}
GroupCallCallFull.story = {
name: 'Group Call - call full',
};
export function GroupCall0PeekedParticipantsBigGroup(): JSX.Element {
export function GroupCallWith0PeekedParticipantsBigGroup(): JSX.Element {
const props = createProps({
isGroupCall: true,
groupMembers: times(100, () => getDefaultConversation()),
});
return <CallingLobby {...props} />;
}
GroupCall0PeekedParticipantsBigGroup.story = {
name: 'Group Call - 0 peeked participants, big group',
};

View File

@@ -5,6 +5,7 @@ import * as React from 'react';
import { sample } from 'lodash';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './CallingParticipantsList';
import { CallingParticipantsList } from './CallingParticipantsList';
import { AvatarColors } from '../types/Colors';
@@ -47,17 +48,13 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/CallingParticipantsList',
};
} satisfies Meta<PropsType>;
export function NoOne(): JSX.Element {
const props = createProps();
return <CallingParticipantsList {...props} />;
}
NoOne.story = {
name: 'No one',
};
export function SoloCall(): JSX.Element {
const props = createProps({
participants: [

View File

@@ -3,9 +3,8 @@
import * as React from 'react';
import { times } from 'lodash';
import { boolean, select } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { AvatarColors } from '../types/Colors';
import type { ConversationType } from '../state/ducks/conversations';
import type { PropsType } from './CallingPip';
@@ -35,16 +34,19 @@ const conversation: ConversationType = getDefaultConversation({
profileName: 'Rick Sanchez',
});
const getCommonActiveCallData = () => ({
type Overrides = {
hasLocalAudio?: boolean;
hasLocalVideo?: boolean;
localAudioLevel?: number;
viewMode?: CallViewMode;
};
const getCommonActiveCallData = (overrides: Overrides) => ({
conversation,
hasLocalAudio: boolean('hasLocalAudio', true),
hasLocalVideo: boolean('hasLocalVideo', false),
localAudioLevel: select('localAudioLevel', [0, 0.5, 1], 0),
viewMode: select(
'viewMode',
[CallViewMode.Grid, CallViewMode.Speaker, CallViewMode.Presentation],
CallViewMode.Grid
),
hasLocalAudio: overrides.hasLocalAudio ?? true,
hasLocalVideo: overrides.hasLocalVideo ?? false,
localAudioLevel: overrides.localAudioLevel ?? 0,
viewMode: overrides.viewMode ?? CallViewMode.Grid,
joinedAt: Date.now(),
outgoingRing: true,
pip: true,
@@ -52,92 +54,93 @@ const getCommonActiveCallData = () => ({
showParticipantsList: false,
});
const defaultCall: ActiveDirectCallType = {
...getCommonActiveCallData(),
callMode: CallMode.Direct as CallMode.Direct,
callState: CallState.Accepted,
peekedParticipants: [],
remoteParticipants: [
{ hasRemoteVideo: true, presenting: false, title: 'Arsene' },
],
const getDefaultCall = (overrides: Overrides): ActiveDirectCallType => {
return {
...getCommonActiveCallData(overrides),
callMode: CallMode.Direct as CallMode.Direct,
callState: CallState.Accepted,
peekedParticipants: [],
remoteParticipants: [
{ hasRemoteVideo: true, presenting: false, title: 'Arsene' },
],
};
};
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
activeCall: overrideProps.activeCall || defaultCall,
getGroupCallVideoFrameSource: fakeGetGroupCallVideoFrameSource,
hangUpActiveCall: action('hang-up-active-call'),
hasLocalVideo: boolean('hasLocalVideo', overrideProps.hasLocalVideo || false),
i18n,
setGroupCallVideoRequest: action('set-group-call-video-request'),
setLocalPreview: action('set-local-preview'),
setRendererCanvas: action('set-renderer-canvas'),
switchFromPresentationView: action('switch-to-presentation-view'),
switchToPresentationView: action('switch-to-presentation-view'),
togglePip: action('toggle-pip'),
});
export default {
title: 'Components/CallingPip',
};
argTypes: {
hasLocalVideo: { control: { type: 'boolean' } },
},
args: {
activeCall: getDefaultCall({}),
getGroupCallVideoFrameSource: fakeGetGroupCallVideoFrameSource,
hangUpActiveCall: action('hang-up-active-call'),
hasLocalVideo: false,
i18n,
setGroupCallVideoRequest: action('set-group-call-video-request'),
setLocalPreview: action('set-local-preview'),
setRendererCanvas: action('set-renderer-canvas'),
switchFromPresentationView: action('switch-to-presentation-view'),
switchToPresentationView: action('switch-to-presentation-view'),
togglePip: action('toggle-pip'),
},
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
const props = createProps({});
return <CallingPip {...props} />;
export function Default(args: PropsType): JSX.Element {
return <CallingPip {...args} />;
}
export function ContactWithAvatarAndNoVideo(): JSX.Element {
const props = createProps({
activeCall: {
...defaultCall,
conversation: {
...conversation,
avatarPath: 'https://www.fillmurray.com/64/64',
},
remoteParticipants: [
{ hasRemoteVideo: false, presenting: false, title: 'Julian' },
],
},
});
return <CallingPip {...props} />;
export function ContactWithAvatarAndNoVideo(args: PropsType): JSX.Element {
return (
<CallingPip
{...args}
activeCall={{
...getDefaultCall({}),
conversation: {
...conversation,
avatarPath: 'https://www.fillmurray.com/64/64',
},
remoteParticipants: [
{ hasRemoteVideo: false, presenting: false, title: 'Julian' },
],
}}
/>
);
}
ContactWithAvatarAndNoVideo.story = {
name: 'Contact (with avatar and no video)',
};
export function ContactNoColor(): JSX.Element {
const props = createProps({
activeCall: {
...defaultCall,
conversation: {
...conversation,
color: undefined,
},
},
});
return <CallingPip {...props} />;
export function ContactNoColor(args: PropsType): JSX.Element {
return (
<CallingPip
{...args}
activeCall={{
...getDefaultCall({}),
conversation: {
...conversation,
color: undefined,
},
}}
/>
);
}
ContactNoColor.story = {
name: 'Contact (no color)',
};
export function GroupCall(): JSX.Element {
const props = createProps({
activeCall: {
...getCommonActiveCallData(),
callMode: CallMode.Group as CallMode.Group,
connectionState: GroupCallConnectionState.Connected,
conversationsWithSafetyNumberChanges: [],
groupMembers: times(3, () => getDefaultConversation()),
isConversationTooBigToRing: false,
joinState: GroupCallJoinState.Joined,
maxDevices: 5,
deviceCount: 0,
peekedParticipants: [],
remoteParticipants: [],
remoteAudioLevels: new Map<number, number>(),
},
});
return <CallingPip {...props} />;
export function GroupCall(args: PropsType): JSX.Element {
return (
<CallingPip
{...args}
activeCall={{
...getCommonActiveCallData({}),
callMode: CallMode.Group as CallMode.Group,
connectionState: GroupCallConnectionState.Connected,
conversationsWithSafetyNumberChanges: [],
groupMembers: times(3, () => getDefaultConversation()),
isConversationTooBigToRing: false,
joinState: GroupCallJoinState.Joined,
maxDevices: 5,
deviceCount: 0,
peekedParticipants: [],
remoteParticipants: [],
remoteAudioLevels: new Map<number, number>(),
}}
/>
);
}

View File

@@ -3,10 +3,11 @@
import React from 'react';
import { times } from 'lodash';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import type { PropsType } from './CallingPreCallInfo';
import { CallingPreCallInfo, RingMode } from './CallingPreCallInfo';
const i18n = setupI18n('en', enMessages);
@@ -22,7 +23,7 @@ const otherMembers = times(6, () => getDefaultConversation());
export default {
title: 'Components/CallingPreCallInfo',
};
} satisfies Meta<PropsType>;
export function DirectConversation(): JSX.Element {
return (
@@ -35,10 +36,6 @@ export function DirectConversation(): JSX.Element {
);
}
DirectConversation.story = {
name: 'Direct conversation',
};
export function Ring0(): JSX.Element {
return (
<CallingPreCallInfo
@@ -52,10 +49,6 @@ export function Ring0(): JSX.Element {
);
}
Ring0.story = {
name: 'Group call: Will ring 0 people',
};
export function Ring1(): JSX.Element {
return (
<CallingPreCallInfo
@@ -69,10 +62,6 @@ export function Ring1(): JSX.Element {
);
}
Ring1.story = {
name: 'Group call: Will ring 1 person',
};
export function Ring2(): JSX.Element {
return (
<CallingPreCallInfo
@@ -86,10 +75,6 @@ export function Ring2(): JSX.Element {
);
}
Ring2.story = {
name: 'Group call: Will ring 2 people',
};
export function Ring3(): JSX.Element {
return (
<CallingPreCallInfo
@@ -103,10 +88,6 @@ export function Ring3(): JSX.Element {
);
}
Ring3.story = {
name: 'Group call: Will ring 3 people',
};
export function Ring4(): JSX.Element {
return (
<CallingPreCallInfo
@@ -120,10 +101,6 @@ export function Ring4(): JSX.Element {
);
}
Ring3.story = {
name: 'Group call: Will ring 4 people',
};
export function Notify0(): JSX.Element {
return (
<CallingPreCallInfo
@@ -137,10 +114,6 @@ export function Notify0(): JSX.Element {
);
}
Notify0.story = {
name: 'Group call: Will notify 0 people',
};
export function Notify1(): JSX.Element {
return (
<CallingPreCallInfo
@@ -154,10 +127,6 @@ export function Notify1(): JSX.Element {
);
}
Notify1.story = {
name: 'Group call: Will notify 1 person',
};
export function Notify2(): JSX.Element {
return (
<CallingPreCallInfo
@@ -171,10 +140,6 @@ export function Notify2(): JSX.Element {
);
}
Notify2.story = {
name: 'Group call: Will notify 2 people',
};
export function Notify3(): JSX.Element {
return (
<CallingPreCallInfo
@@ -188,10 +153,6 @@ export function Notify3(): JSX.Element {
);
}
Notify3.story = {
name: 'Group call: Will notify 3 people',
};
export function Notify4(): JSX.Element {
return (
<CallingPreCallInfo
@@ -205,10 +166,6 @@ export function Notify4(): JSX.Element {
);
}
Notify4.story = {
name: 'Group call: Will notify 4 people',
};
export function Peek1(): JSX.Element {
return (
<CallingPreCallInfo
@@ -222,10 +179,6 @@ export function Peek1(): JSX.Element {
);
}
Peek1.story = {
name: 'Group call: 1 participant peeked',
};
export function Peek2(): JSX.Element {
return (
<CallingPreCallInfo
@@ -239,10 +192,6 @@ export function Peek2(): JSX.Element {
);
}
Peek2.story = {
name: 'Group call: 2 participants peeked',
};
export function Peek3(): JSX.Element {
return (
<CallingPreCallInfo
@@ -256,10 +205,6 @@ export function Peek3(): JSX.Element {
);
}
Peek3.story = {
name: 'Group call: 3 participants peeked',
};
export function Peek4(): JSX.Element {
return (
<CallingPreCallInfo
@@ -273,10 +218,6 @@ export function Peek4(): JSX.Element {
);
}
Peek4.story = {
name: 'Group call: 4 participants peeked',
};
export function GroupConversationYouOnAnOtherDevice(): JSX.Element {
const me = getDefaultConversation();
return (
@@ -291,10 +232,6 @@ export function GroupConversationYouOnAnOtherDevice(): JSX.Element {
);
}
GroupConversationYouOnAnOtherDevice.story = {
name: 'Group conversation, you on an other device',
};
export function GroupConversationCallIsFull(): JSX.Element {
return (
<CallingPreCallInfo
@@ -308,7 +245,3 @@ export function GroupConversationCallIsFull(): JSX.Element {
/>
);
}
GroupConversationCallIsFull.story = {
name: 'Group conversation, call is full',
};

View File

@@ -15,7 +15,7 @@ export enum RingMode {
IsRinging,
}
type PropsType = {
export type PropsType = {
conversation: Pick<
ConversationType,
| 'acceptedMessageRequest'

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './CallingScreenSharingController';
import { CallingScreenSharingController } from './CallingScreenSharingController';
@@ -21,7 +22,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/CallingScreenSharingController',
};
} satisfies Meta<PropsType>;
export function Controller(): JSX.Element {
return <CallingScreenSharingController {...createProps()} />;
@@ -37,7 +38,3 @@ export function ReallyLongAppName(): JSX.Element {
/>
);
}
ReallyLongAppName.story = {
name: 'Really long app name',
};

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './CallingSelectPresentingSourcesModal';
import { CallingSelectPresentingSourcesModal } from './CallingSelectPresentingSourcesModal';
@@ -55,7 +56,7 @@ const createProps = (): PropsType => ({
export default {
title: 'Components/CallingSelectPresentingSourcesModal',
};
} satisfies Meta<PropsType>;
export function Modal(): JSX.Element {
return <CallingSelectPresentingSourcesModal {...createProps()} />;

View File

@@ -3,36 +3,33 @@
import React, { useState } from 'react';
import { action } from '@storybook/addon-actions';
import { boolean } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import type { PropsType } from './CaptchaDialog';
import { CaptchaDialog } from './CaptchaDialog';
import { Button } from './Button';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
export default {
title: 'Components/CaptchaDialog',
};
const i18n = setupI18n('en', enMessages);
export const _CaptchaDialog = (): JSX.Element => {
export default {
title: 'Components/CaptchaDialog',
argTypes: {
isPending: { control: { type: 'boolean' } },
},
args: {
i18n,
isPending: false,
onContinue: action('onContinue'),
},
} satisfies Meta<PropsType>;
export function Basic(args: PropsType): JSX.Element {
const [isSkipped, setIsSkipped] = useState(false);
if (isSkipped) {
return <Button onClick={() => setIsSkipped(false)}>Show again</Button>;
}
return (
<CaptchaDialog
i18n={i18n}
isPending={boolean('isPending', false)}
onContinue={action('onContinue')}
onSkip={() => setIsSkipped(true)}
/>
);
};
_CaptchaDialog.story = {
name: 'CaptchaDialog',
};
return <CaptchaDialog {...args} onSkip={() => setIsSkipped(true)} />;
}

View File

@@ -8,7 +8,7 @@ import { Button, ButtonVariant } from './Button';
import { Modal } from './Modal';
import { Spinner } from './Spinner';
type PropsType = {
export type PropsType = {
i18n: LocalizerType;
isPending: boolean;

View File

@@ -2,21 +2,45 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { action } from '@storybook/addon-actions';
import { select } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './ChatColorPicker';
import { ChatColorPicker } from './ChatColorPicker';
import { ConversationColors } from '../types/Colors';
import { setupI18n } from '../util/setupI18n';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/ChatColorPicker',
};
const i18n = setupI18n('en', enMessages);
argTypes: {
selectedColor: {
control: {
type: 'select',
options: ConversationColors,
},
},
},
args: {
addCustomColor: action('addCustomColor'),
colorSelected: action('colorSelected'),
editCustomColor: action('editCustomColor'),
getConversationsWithCustomColor: (_: string) => Promise.resolve([]),
i18n,
removeCustomColor: action('removeCustomColor'),
removeCustomColorOnConversations: action(
'removeCustomColorOnConversations'
),
resetAllChatColors: action('resetAllChatColors'),
resetDefaultChatColor: action('resetDefaultChatColor'),
selectedColor: 'basil',
selectedCustomColor: {},
setGlobalDefaultConversationColor: action(
'setGlobalDefaultConversationColor'
),
},
} satisfies Meta<PropsType>;
const SAMPLE_CUSTOM_COLOR = {
deg: 90,
@@ -24,25 +48,8 @@ const SAMPLE_CUSTOM_COLOR = {
start: { hue: 315, saturation: 78 },
};
const createProps = (): PropsType => ({
addCustomColor: action('addCustomColor'),
colorSelected: action('colorSelected'),
editCustomColor: action('editCustomColor'),
getConversationsWithCustomColor: (_: string) => Promise.resolve([]),
i18n,
removeCustomColor: action('removeCustomColor'),
removeCustomColorOnConversations: action('removeCustomColorOnConversations'),
resetAllChatColors: action('resetAllChatColors'),
resetDefaultChatColor: action('resetDefaultChatColor'),
selectedColor: select('selectedColor', ConversationColors, 'basil' as const),
selectedCustomColor: {},
setGlobalDefaultConversationColor: action(
'setGlobalDefaultConversationColor'
),
});
export function Default(): JSX.Element {
return <ChatColorPicker {...createProps()} />;
export function Default(args: PropsType): JSX.Element {
return <ChatColorPicker {...args} />;
}
const CUSTOM_COLORS = {
@@ -62,10 +69,10 @@ const CUSTOM_COLORS = {
},
};
export function CustomColors(): JSX.Element {
export function CustomColors(args: PropsType): JSX.Element {
return (
<ChatColorPicker
{...createProps()}
{...args}
customColors={CUSTOM_COLORS}
selectedColor="custom"
selectedCustomColor={{

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './Checkbox';
import { Checkbox } from './Checkbox';
@@ -16,7 +17,7 @@ const createProps = (): PropsType => ({
export default {
title: 'Components/Checkbox',
};
} satisfies Meta<PropsType>;
export function Normal(): JSX.Element {
return <Checkbox {...createProps()} />;

View File

@@ -3,7 +3,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { Props } from './CircleCheckbox';
import { CircleCheckbox, Variant } from './CircleCheckbox';
@@ -15,7 +15,7 @@ const createProps = (): Props => ({
export default {
title: 'Components/CircleCheckbox',
};
} satisfies Meta<Props>;
export function Normal(): JSX.Element {
return <CircleCheckbox {...createProps()} />;

View File

@@ -2,23 +2,19 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './ClearingData';
import { ClearingData } from './ClearingData';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/ClearingData',
};
} satisfies Meta<PropsType>;
export const _ClearingData = (): JSX.Element => (
<ClearingData deleteAllData={action('deleteAllData')} i18n={i18n} />
);
_ClearingData.story = {
name: 'Clearing data',
};
export function Basic(): JSX.Element {
return <ClearingData deleteAllData={action('deleteAllData')} i18n={i18n} />;
}

View File

@@ -1,12 +1,9 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { DecoratorFunction } from '@storybook/addons';
import * as React from 'react';
import React, { useContext } from 'react';
import { action } from '@storybook/addon-actions';
import { boolean, select } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import { IMAGE_JPEG } from '../types/MIME';
import type { Props } from './CompositionArea';
import { CompositionArea } from './CompositionArea';
@@ -28,287 +25,256 @@ export default {
decorators: [
// necessary for the add attachment button to render properly
storyFn => <div className="file-input">{storyFn()}</div>,
] as Array<DecoratorFunction<JSX.Element>>,
};
],
argTypes: {
recordingState: {
control: { type: 'select' },
options: Object.keys(RecordingState),
mappings: RecordingState,
},
messageRequestsEnabled: { control: { type: 'boolean' } },
announcementsOnly: { control: { type: 'boolean' } },
areWePendingApproval: { control: { type: 'boolean' } },
},
args: {
addAttachment: action('addAttachment'),
conversationId: '123',
convertDraftBodyRangesIntoHydrated: () => undefined,
discardEditMessage: action('discardEditMessage'),
focusCounter: 0,
sendCounter: 0,
i18n,
isDisabled: false,
isFormattingFlagEnabled: true,
isFormattingSpoilersFlagEnabled: true,
isFormattingEnabled: true,
messageCompositionId: '456',
sendEditedMessage: action('sendEditedMessage'),
sendMultiMediaMessage: action('sendMultiMediaMessage'),
platform: 'darwin',
processAttachments: action('processAttachments'),
removeAttachment: action('removeAttachment'),
setComposerFocus: action('setComposerFocus'),
setMessageToEdit: action('setMessageToEdit'),
setQuoteByMessageId: action('setQuoteByMessageId'),
showToast: action('showToast'),
const useProps = (overrideProps: Partial<Props> = {}): Props => ({
addAttachment: action('addAttachment'),
conversationId: '123',
convertDraftBodyRangesIntoHydrated: () => undefined,
discardEditMessage: action('discardEditMessage'),
focusCounter: 0,
sendCounter: 0,
i18n,
isDisabled: false,
isFormattingFlagEnabled:
overrideProps.isFormattingFlagEnabled === false
? overrideProps.isFormattingFlagEnabled
: true,
isFormattingSpoilersFlagEnabled:
overrideProps.isFormattingSpoilersFlagEnabled === false
? overrideProps.isFormattingSpoilersFlagEnabled
: true,
isFormattingEnabled:
overrideProps.isFormattingEnabled === false
? overrideProps.isFormattingEnabled
: true,
messageCompositionId: '456',
sendEditedMessage: action('sendEditedMessage'),
sendMultiMediaMessage: action('sendMultiMediaMessage'),
platform: 'darwin',
processAttachments: action('processAttachments'),
removeAttachment: action('removeAttachment'),
theme: React.useContext(StorybookThemeContext),
setComposerFocus: action('setComposerFocus'),
setMessageToEdit: action('setMessageToEdit'),
setQuoteByMessageId: action('setQuoteByMessageId'),
showToast: action('showToast'),
// AttachmentList
draftAttachments: [],
onClearAttachments: action('onClearAttachments'),
// AudioCapture
cancelRecording: action('cancelRecording'),
completeRecording: action('completeRecording'),
errorRecording: action('errorRecording'),
recordingState: RecordingState.Idle,
startRecording: action('startRecording'),
// StagedLinkPreview
linkPreviewLoading: false,
linkPreviewResult: undefined,
onCloseLinkPreview: action('onCloseLinkPreview'),
// Quote
quotedMessageProps: undefined,
scrollToMessage: action('scrollToMessage'),
// MediaEditor
imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]',
// MediaQualitySelector
setMediaQualitySetting: action('setMediaQualitySetting'),
shouldSendHighQualityAttachments: false,
// CompositionInput
onEditorStateChange: action('onEditorStateChange'),
onTextTooLong: action('onTextTooLong'),
draftText: undefined,
clearQuotedMessage: action('clearQuotedMessage'),
getPreferredBadge: () => undefined,
getQuotedMessage: action('getQuotedMessage'),
sortedGroupMembers: [],
// EmojiButton
onPickEmoji: action('onPickEmoji'),
onSetSkinTone: action('onSetSkinTone'),
recentEmojis: [],
skinTone: 1,
// StickerButton
knownPacks: [],
receivedPacks: [],
installedPacks: [],
blessedPacks: [],
recentStickers: [],
clearInstalledStickerPack: action('clearInstalledStickerPack'),
pushPanelForConversation: action('pushPanelForConversation'),
sendStickerMessage: action('sendStickerMessage'),
clearShowIntroduction: action('clearShowIntroduction'),
showPickerHint: false,
clearShowPickerHint: action('clearShowPickerHint'),
// Message Requests
conversationType: 'direct',
acceptConversation: action('acceptConversation'),
blockConversation: action('blockConversation'),
blockAndReportSpam: action('blockAndReportSpam'),
deleteConversation: action('deleteConversation'),
messageRequestsEnabled: false,
title: '',
// GroupV1 Disabled Actions
showGV2MigrationDialog: action('showGV2MigrationDialog'),
// GroupV2
announcementsOnly: false,
areWeAdmin: false,
areWePendingApproval: false,
groupAdmins: [],
cancelJoinRequest: action('cancelJoinRequest'),
showConversation: action('showConversation'),
// SMS-only
isSMSOnly: false,
isFetchingUUID: false,
renderSmartCompositionRecording: _ => <div>RECORDING</div>,
renderSmartCompositionRecordingDraft: _ => <div>RECORDING DRAFT</div>,
// Select mode
selectedMessageIds: undefined,
toggleSelectMode: action('toggleSelectMode'),
toggleForwardMessagesModal: action('toggleForwardMessagesModal'),
},
} satisfies Meta<Props>;
// AttachmentList
draftAttachments: overrideProps.draftAttachments || [],
onClearAttachments: action('onClearAttachments'),
// AudioCapture
cancelRecording: action('cancelRecording'),
completeRecording: action('completeRecording'),
errorRecording: action('errorRecording'),
recordingState: select(
'recordingState',
RecordingState,
overrideProps.recordingState || RecordingState.Idle
),
startRecording: action('startRecording'),
// StagedLinkPreview
linkPreviewLoading: Boolean(overrideProps.linkPreviewLoading),
linkPreviewResult: overrideProps.linkPreviewResult,
onCloseLinkPreview: action('onCloseLinkPreview'),
// Quote
quotedMessageProps: overrideProps.quotedMessageProps,
scrollToMessage: action('scrollToMessage'),
// MediaEditor
imageToBlurHash: async () => 'LDA,FDBnm+I=p{tkIUI;~UkpELV]',
// MediaQualitySelector
setMediaQualitySetting: action('setMediaQualitySetting'),
shouldSendHighQualityAttachments: Boolean(
overrideProps.shouldSendHighQualityAttachments
),
// CompositionInput
onEditorStateChange: action('onEditorStateChange'),
onTextTooLong: action('onTextTooLong'),
draftText: overrideProps.draftText || undefined,
clearQuotedMessage: action('clearQuotedMessage'),
getPreferredBadge: () => undefined,
getQuotedMessage: action('getQuotedMessage'),
sortedGroupMembers: [],
// EmojiButton
onPickEmoji: action('onPickEmoji'),
onSetSkinTone: action('onSetSkinTone'),
recentEmojis: [],
skinTone: 1,
// StickerButton
knownPacks: overrideProps.knownPacks || [],
receivedPacks: [],
installedPacks: [],
blessedPacks: [],
recentStickers: [],
clearInstalledStickerPack: action('clearInstalledStickerPack'),
pushPanelForConversation: action('pushPanelForConversation'),
sendStickerMessage: action('sendStickerMessage'),
clearShowIntroduction: action('clearShowIntroduction'),
showPickerHint: false,
clearShowPickerHint: action('clearShowPickerHint'),
// Message Requests
conversationType: 'direct',
acceptConversation: action('acceptConversation'),
blockConversation: action('blockConversation'),
blockAndReportSpam: action('blockAndReportSpam'),
deleteConversation: action('deleteConversation'),
messageRequestsEnabled: boolean(
'messageRequestsEnabled',
overrideProps.messageRequestsEnabled || false
),
title: '',
// GroupV1 Disabled Actions
showGV2MigrationDialog: action('showGV2MigrationDialog'),
// GroupV2
announcementsOnly: boolean(
'announcementsOnly',
Boolean(overrideProps.announcementsOnly)
),
areWeAdmin: boolean('areWeAdmin', Boolean(overrideProps.areWeAdmin)),
areWePendingApproval: boolean(
'areWePendingApproval',
Boolean(overrideProps.areWePendingApproval)
),
groupAdmins: [],
cancelJoinRequest: action('cancelJoinRequest'),
showConversation: action('showConversation'),
// SMS-only
isSMSOnly: overrideProps.isSMSOnly || false,
isFetchingUUID: overrideProps.isFetchingUUID || false,
renderSmartCompositionRecording: _ => <div>RECORDING</div>,
renderSmartCompositionRecordingDraft: _ => <div>RECORDING DRAFT</div>,
// Select mode
selectedMessageIds: undefined,
toggleSelectMode: action('toggleSelectMode'),
toggleForwardMessagesModal: action('toggleForwardMessagesModal'),
});
export function Default(): JSX.Element {
const props = useProps();
return <CompositionArea {...props} />;
export function Default(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return <CompositionArea {...args} theme={theme} />;
}
export function StartingText(): JSX.Element {
const props = useProps({
draftText: "here's some starting text",
});
return <CompositionArea {...props} />;
}
export function StickerButton(): JSX.Element {
const props = useProps({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
knownPacks: [{} as any],
});
return <CompositionArea {...props} />;
}
export function MessageRequest(): JSX.Element {
const props = useProps({
messageRequestsEnabled: true,
});
return <CompositionArea {...props} />;
}
export function SmsOnlyFetchingUuid(): JSX.Element {
const props = useProps({
isSMSOnly: true,
isFetchingUUID: true,
});
return <CompositionArea {...props} />;
}
SmsOnlyFetchingUuid.story = {
name: 'SMS-only fetching UUID',
};
export function SmsOnly(): JSX.Element {
const props = useProps({
isSMSOnly: true,
});
return <CompositionArea {...props} />;
}
SmsOnly.story = {
name: 'SMS-only',
};
export function Attachments(): JSX.Element {
const props = useProps({
draftAttachments: [
fakeDraftAttachment({
contentType: IMAGE_JPEG,
url: landscapeGreenUrl,
}),
],
});
return <CompositionArea {...props} />;
}
export function PendingApproval(): JSX.Element {
export function StartingText(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea
{...useProps({
areWePendingApproval: true,
})}
{...args}
theme={theme}
draftText="here's some starting text"
/>
);
}
AnnouncementsOnlyGroup.story = {
name: 'Announcements Only group',
};
export function AnnouncementsOnlyGroup(): JSX.Element {
export function StickerButton(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea
{...useProps({
announcementsOnly: true,
areWeAdmin: false,
})}
{...args}
theme={theme}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
knownPacks={[{} as any]}
/>
);
}
AnnouncementsOnlyGroup.story = {
name: 'Announcements Only group',
};
export function MessageRequest(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return <CompositionArea {...args} theme={theme} messageRequestsEnabled />;
}
export function Quote(): JSX.Element {
export function SmsOnlyFetchingUuid(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return <CompositionArea {...args} theme={theme} isSMSOnly isFetchingUUID />;
}
export function SmsOnly(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return <CompositionArea {...args} theme={theme} isSMSOnly />;
}
export function Attachments(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea
{...useProps({
quotedMessageProps: {
text: 'something',
conversationColor: ConversationColors[10],
conversationTitle: getDefaultConversation().title,
isGiftBadge: false,
isViewOnce: false,
referencedMessageNotFound: false,
authorTitle: 'Someone',
isFromMe: false,
{...args}
theme={theme}
draftAttachments={[
fakeDraftAttachment({
contentType: IMAGE_JPEG,
url: landscapeGreenUrl,
}),
]}
/>
);
}
export function PendingApproval(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return <CompositionArea {...args} theme={theme} areWePendingApproval />;
}
export function AnnouncementsOnlyGroup(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea
{...args}
theme={theme}
announcementsOnly
areWeAdmin={false}
/>
);
}
export function Quote(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea
{...args}
theme={theme}
quotedMessageProps={{
text: 'something',
conversationColor: ConversationColors[10],
conversationTitle: getDefaultConversation().title,
isGiftBadge: false,
isViewOnce: false,
referencedMessageNotFound: false,
authorTitle: 'Someone',
isFromMe: false,
}}
/>
);
}
export function QuoteWithPayment(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea
{...args}
theme={theme}
quotedMessageProps={{
text: '',
conversationColor: ConversationColors[10],
conversationTitle: getDefaultConversation().title,
isGiftBadge: false,
isViewOnce: false,
referencedMessageNotFound: false,
authorTitle: 'Someone',
isFromMe: false,
payment: {
kind: PaymentEventKind.Notification,
note: 'Thanks',
},
})}
}}
/>
);
}
export function QuoteWithPayment(): JSX.Element {
export function NoFormattingMenu(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea {...args} theme={theme} isFormattingEnabled={false} />
);
}
export function NoFormattingFlag(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea {...args} theme={theme} isFormattingFlagEnabled={false} />
);
}
export function NoSpoilerFormattingFlag(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext);
return (
<CompositionArea
{...useProps({
quotedMessageProps: {
text: '',
conversationColor: ConversationColors[10],
conversationTitle: getDefaultConversation().title,
isGiftBadge: false,
isViewOnce: false,
referencedMessageNotFound: false,
authorTitle: 'Someone',
isFromMe: false,
payment: {
kind: PaymentEventKind.Notification,
note: 'Thanks',
},
},
})}
/>
);
}
QuoteWithPayment.story = {
name: 'Quote with payment',
};
export function NoFormattingMenu(): JSX.Element {
return <CompositionArea {...useProps({ isFormattingEnabled: false })} />;
}
export function NoFormattingFlag(): JSX.Element {
return <CompositionArea {...useProps({ isFormattingFlagEnabled: false })} />;
}
export function NoSpoilerFormattingFlag(): JSX.Element {
return (
<CompositionArea
{...useProps({ isFormattingSpoilersFlagEnabled: false })}
{...args}
theme={theme}
isFormattingSpoilersFlagEnabled={false}
/>
);
}

View File

@@ -2,11 +2,9 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import 'react-quill/dist/quill.core.css';
import { boolean, select } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import type { Props } from './CompositionInput';
import { CompositionInput } from './CompositionInput';
@@ -19,11 +17,13 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/CompositionInput',
};
argTypes: {},
args: {},
} satisfies Meta<Props>;
const useProps = (overrideProps: Partial<Props> = {}): Props => ({
i18n,
disabled: boolean('disabled', overrideProps.disabled || false),
disabled: overrideProps.disabled ?? false,
draftText: overrideProps.draftText || undefined,
draftBodyRanges: overrideProps.draftBodyRanges || [],
clearQuotedMessage: action('clearQuotedMessage'),
@@ -41,7 +41,7 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
overrideProps.isFormattingEnabled === false
? overrideProps.isFormattingEnabled
: true,
large: boolean('large', overrideProps.large || false),
large: overrideProps.large ?? false,
onCloseLinkPreview: action('onCloseLinkPreview'),
onEditorStateChange: action('onEditorStateChange'),
onPickEmoji: action('onPickEmoji'),
@@ -49,19 +49,8 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
onTextTooLong: action('onTextTooLong'),
platform: 'darwin',
sendCounter: 0,
sortedGroupMembers: overrideProps.sortedGroupMembers || [],
skinTone: select(
'skinTone',
{
skinTone0: 0,
skinTone1: 1,
skinTone2: 2,
skinTone3: 3,
skinTone4: 4,
skinTone5: 5,
},
overrideProps.skinTone || undefined
),
sortedGroupMembers: overrideProps.sortedGroupMembers ?? [],
skinTone: overrideProps.skinTone ?? undefined,
theme: React.useContext(StorybookThemeContext),
});

View File

@@ -3,8 +3,9 @@
import React, { useState } from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { Props } from './CompositionRecording';
import { CompositionRecording } from './CompositionRecording';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -13,7 +14,7 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'components/CompositionRecording',
component: CompositionRecording,
};
} satisfies Meta<Props>;
export function Default(): JSX.Element {
const [active, setActive] = useState(false);

View File

@@ -3,8 +3,9 @@
import React, { useState } from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { Props } from './CompositionRecordingDraft';
import { CompositionRecordingDraft } from './CompositionRecordingDraft';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -13,7 +14,7 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'components/CompositionRecordingDraft',
component: CompositionRecordingDraft,
};
} satisfies Meta<Props>;
export function Default(): JSX.Element {
const [isPlaying, setIsPlaying] = useState(false);

View File

@@ -11,7 +11,7 @@ import * as log from '../logging/log';
import type { Size } from '../hooks/useSizeObserver';
import { SizeObserver } from '../hooks/useSizeObserver';
type Props = {
export type Props = {
i18n: LocalizerType;
audioUrl: string | undefined;
active:

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -20,7 +21,7 @@ const createProps = (): PropsType => ({
export default {
title: 'Components/ConfirmDiscardDialog',
};
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
return <ConfirmDiscardDialog {...createProps()} />;

View File

@@ -4,6 +4,8 @@
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { Props } from './ConfirmationDialog';
import { ConfirmationDialog } from './ConfirmationDialog';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -12,9 +14,9 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/ConfirmationDialog',
};
} satisfies Meta<Props>;
export const _ConfirmationDialog = (): JSX.Element => {
export function Basic(): JSX.Element {
return (
<ConfirmationDialog
dialogName="test"
@@ -37,11 +39,7 @@ export const _ConfirmationDialog = (): JSX.Element => {
asdf blip
</ConfirmationDialog>
);
};
_ConfirmationDialog.story = {
name: 'ConfirmationDialog',
};
}
export function CustomCancelText(): JSX.Element {
return (
@@ -64,10 +62,6 @@ export function CustomCancelText(): JSX.Element {
);
}
CustomCancelText.story = {
name: 'Custom cancel text',
};
export function NoDefaultCancel(): JSX.Element {
return (
<ConfirmationDialog

View File

@@ -6,6 +6,7 @@ import { times } from 'lodash';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import { ContactPills } from './ContactPills';
@@ -18,7 +19,7 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/Contact Pills',
};
} satisfies Meta<ContactPillPropsType>;
type ContactType = Omit<ContactPillPropsType, 'i18n' | 'onClickRemove'>;
@@ -54,10 +55,6 @@ export function EmptyList(): JSX.Element {
return <ContactPills />;
}
EmptyList.story = {
name: 'Empty list',
};
export function OneContact(): JSX.Element {
return (
<ContactPills>
@@ -66,10 +63,6 @@ export function OneContact(): JSX.Element {
);
}
OneContact.story = {
name: 'One contact',
};
export function ThreeContacts(): JSX.Element {
return (
<ContactPills>
@@ -80,10 +73,6 @@ export function ThreeContacts(): JSX.Element {
);
}
ThreeContacts.story = {
name: 'Three contacts',
};
export function FourContactsOneWithALongName(): JSX.Element {
return (
<ContactPills>
@@ -101,10 +90,6 @@ export function FourContactsOneWithALongName(): JSX.Element {
);
}
FourContactsOneWithALongName.story = {
name: 'Four contacts, one with a long name',
};
export function FiftyContacts(): JSX.Element {
return (
<ContactPills>
@@ -114,7 +99,3 @@ export function FiftyContacts(): JSX.Element {
</ContactPills>
);
}
FiftyContacts.story = {
name: 'Fifty contacts',
};

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './ContextMenu';
import { ContextMenu } from './ContextMenu';
import enMessages from '../../_locales/en/messages.json';
@@ -13,7 +14,7 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/ContextMenu',
};
} satisfies Meta<PropsType<unknown>>;
const getDefaultProps = (): PropsType<number> => ({
i18n,

View File

@@ -4,11 +4,9 @@
import React, { useContext } from 'react';
import { times, omit } from 'lodash';
import { v4 as generateUuid } from 'uuid';
import { action } from '@storybook/addon-actions';
import { boolean, date, select, text } from '@storybook/addon-knobs';
import type { Row } from './ConversationList';
import type { Meta } from '@storybook/react';
import type { Row, PropsType } from './ConversationList';
import { ConversationList, RowType } from './ConversationList';
import { MessageSearchResult } from './conversationList/MessageSearchResult';
import type { PropsData as ConversationListItemPropsType } from './conversationList/ConversationListItem';
@@ -25,7 +23,9 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/ConversationList',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
const defaultConversations: Array<ConversationListItemPropsType> = [
getDefaultConversation({
@@ -105,15 +105,13 @@ function Wrapper({
);
}
export const _ArchiveButton = (): JSX.Element => (
<Wrapper
rows={[{ type: RowType.ArchiveButton, archivedConversationsCount: 123 }]}
/>
);
_ArchiveButton.story = {
name: 'Archive button',
};
export function ArchiveButton(): JSX.Element {
return (
<Wrapper
rows={[{ type: RowType.ArchiveButton, archivedConversationsCount: 123 }]}
/>
);
}
export function ContactNoteToSelf(): JSX.Element {
return (
@@ -132,10 +130,6 @@ export function ContactNoteToSelf(): JSX.Element {
);
}
ContactNoteToSelf.story = {
name: 'Contact: note to self',
};
export function ContactDirect(): JSX.Element {
return (
<Wrapper
@@ -144,10 +138,6 @@ export function ContactDirect(): JSX.Element {
);
}
ContactDirect.story = {
name: 'Contact: direct',
};
export function ContactDirectWithContextMenu(): JSX.Element {
return (
<Wrapper
@@ -162,10 +152,6 @@ export function ContactDirectWithContextMenu(): JSX.Element {
);
}
ContactDirectWithContextMenu.story = {
name: 'Contact: context menu',
};
export function ContactDirectWithShortAbout(): JSX.Element {
return (
<Wrapper
@@ -179,10 +165,6 @@ export function ContactDirectWithShortAbout(): JSX.Element {
);
}
ContactDirectWithShortAbout.story = {
name: 'Contact: direct with short about',
};
export function ContactDirectWithLongAbout(): JSX.Element {
return (
<Wrapper
@@ -200,10 +182,6 @@ export function ContactDirectWithLongAbout(): JSX.Element {
);
}
ContactDirectWithLongAbout.story = {
name: 'Contact: direct with long about',
};
export function ContactGroup(): JSX.Element {
return (
<Wrapper
@@ -217,10 +195,6 @@ export function ContactGroup(): JSX.Element {
);
}
ContactGroup.story = {
name: 'Contact: group',
};
export function ContactCheckboxes(): JSX.Element {
return (
<Wrapper
@@ -248,10 +222,6 @@ export function ContactCheckboxes(): JSX.Element {
);
}
ContactCheckboxes.story = {
name: 'Contact checkboxes',
};
export function ContactCheckboxesDisabled(): JSX.Element {
return (
<Wrapper
@@ -279,42 +249,29 @@ export function ContactCheckboxesDisabled(): JSX.Element {
);
}
ContactCheckboxesDisabled.story = {
name: 'Contact checkboxes: disabled',
};
const createConversation = (
overrideProps: Partial<ConversationListItemPropsType> = {}
): ConversationListItemPropsType => ({
...overrideProps,
acceptedMessageRequest: boolean(
'acceptedMessageRequest',
acceptedMessageRequest:
overrideProps.acceptedMessageRequest !== undefined
? overrideProps.acceptedMessageRequest
: true
),
: true,
badges: [],
isMe: boolean('isMe', overrideProps.isMe || false),
avatarPath: text('avatarPath', overrideProps.avatarPath || ''),
isMe: overrideProps.isMe ?? false,
avatarPath: overrideProps.avatarPath ?? '',
id: overrideProps.id || '',
isSelected: boolean('isSelected', overrideProps.isSelected || false),
title: text('title', overrideProps.title || 'Some Person'),
isSelected: overrideProps.isSelected ?? false,
title: overrideProps.title ?? 'Some Person',
profileName: overrideProps.profileName || 'Some Person',
type: overrideProps.type || 'direct',
markedUnread: boolean('markedUnread', overrideProps.markedUnread || false),
markedUnread: overrideProps.markedUnread ?? false,
lastMessage: overrideProps.lastMessage || {
text: text('lastMessage.text', 'Hi there!'),
status: select(
'status',
MessageStatuses.reduce((m, s) => ({ ...m, [s]: s }), {}),
'read'
),
text: 'Hi there!',
status: 'read',
deletedForEveryone: false,
},
lastUpdated: date(
'lastUpdated',
new Date(overrideProps.lastUpdated || Date.now() - 5 * 60 * 1000)
),
lastUpdated: overrideProps.lastUpdated ?? Date.now() - 5 * 60 * 1000,
sharedGroupNames: [],
});
@@ -333,19 +290,11 @@ const renderConversation = (
export const ConversationName = (): JSX.Element => renderConversation();
ConversationName.story = {
name: 'Conversation: name',
};
export const ConversationNameAndAvatar = (): JSX.Element =>
renderConversation({
avatarPath: '/fixtures/kitten-1-64-64.jpg',
});
ConversationNameAndAvatar.story = {
name: 'Conversation: name and avatar',
};
export const ConversationWithYourself = (): JSX.Element =>
renderConversation({
lastMessage: {
@@ -358,10 +307,6 @@ export const ConversationWithYourself = (): JSX.Element =>
isMe: true,
});
ConversationWithYourself.story = {
name: 'Conversation: with yourself',
};
export function ConversationsMessageStatuses(): JSX.Element {
return (
<Wrapper
@@ -375,21 +320,13 @@ export function ConversationsMessageStatuses(): JSX.Element {
);
}
ConversationsMessageStatuses.story = {
name: 'Conversations: Message Statuses',
};
export const ConversationTypingStatus = (): JSX.Element =>
renderConversation({
typingContactIdTimestamps: {
[generateUuid()]: date('timestamp', new Date()),
[generateUuid()]: Date.now(),
},
});
ConversationTypingStatus.story = {
name: 'Conversation: Typing Status',
};
export const ConversationWithDraft = (): JSX.Element =>
renderConversation({
shouldShowDraft: true,
@@ -400,19 +337,11 @@ export const ConversationWithDraft = (): JSX.Element =>
},
});
ConversationWithDraft.story = {
name: 'Conversation: With draft',
};
export const ConversationDeletedForEveryone = (): JSX.Element =>
renderConversation({
lastMessage: { deletedForEveryone: true },
});
ConversationDeletedForEveryone.story = {
name: 'Conversation: Deleted for everyone',
};
export const ConversationMessageRequest = (): JSX.Element =>
renderConversation({
acceptedMessageRequest: false,
@@ -423,10 +352,6 @@ export const ConversationMessageRequest = (): JSX.Element =>
},
});
ConversationMessageRequest.story = {
name: 'Conversation: Message Request',
};
export function ConversationsUnreadCount(): JSX.Element {
return (
<Wrapper
@@ -445,17 +370,9 @@ export function ConversationsUnreadCount(): JSX.Element {
);
}
ConversationsUnreadCount.story = {
name: 'Conversations: unread count',
};
export const ConversationMarkedUnread = (): JSX.Element =>
renderConversation({ markedUnread: true });
ConversationMarkedUnread.story = {
name: 'Conversation: marked unread',
};
export const ConversationSelected = (): JSX.Element =>
renderConversation({
lastMessage: {
@@ -466,10 +383,6 @@ export const ConversationSelected = (): JSX.Element =>
isSelected: true,
});
ConversationSelected.story = {
name: 'Conversation: Selected',
};
export const ConversationEmojiInMessage = (): JSX.Element =>
renderConversation({
lastMessage: {
@@ -479,10 +392,6 @@ export const ConversationEmojiInMessage = (): JSX.Element =>
},
});
ConversationEmojiInMessage.story = {
name: 'Conversation: Emoji in Message',
};
export const ConversationLinkInMessage = (): JSX.Element =>
renderConversation({
lastMessage: {
@@ -492,10 +401,6 @@ export const ConversationLinkInMessage = (): JSX.Element =>
},
});
ConversationLinkInMessage.story = {
name: 'Conversation: Link in Message',
};
export const ConversationLongName = (): JSX.Element => {
const name =
'Long contact name. Esquire. The third. And stuff. And more! And more!';
@@ -505,10 +410,6 @@ export const ConversationLongName = (): JSX.Element => {
});
};
ConversationLongName.story = {
name: 'Conversation: long name',
};
export function ConversationLongMessage(): JSX.Element {
const messages = [
"Long line. This is a really really really long line. Really really long. Because that's just how it is",
@@ -534,10 +435,6 @@ Line 4, well.`,
);
}
ConversationLongMessage.story = {
name: 'Conversation: Long Message',
};
export function ConversationsVariousTimes(): JSX.Element {
const pairs: Array<[number, string]> = [
[Date.now() - 5 * 60 * 60 * 1000, 'Five hours ago'],
@@ -563,10 +460,6 @@ export function ConversationsVariousTimes(): JSX.Element {
);
}
ConversationsVariousTimes.story = {
name: 'Conversations: Various Times',
};
export function ConversationMissingDate(): JSX.Element {
const row = {
type: RowType.Conversation as const,
@@ -576,10 +469,6 @@ export function ConversationMissingDate(): JSX.Element {
return <Wrapper rows={[row]} />;
}
ConversationMissingDate.story = {
name: 'Conversation: Missing Date',
};
export function ConversationMissingMessage(): JSX.Element {
const row = {
type: RowType.Conversation as const,
@@ -589,10 +478,6 @@ export function ConversationMissingMessage(): JSX.Element {
return <Wrapper rows={[row]} />;
}
ConversationMissingMessage.story = {
name: 'Conversation: Missing Message',
};
export const ConversationMissingText = (): JSX.Element =>
renderConversation({
lastMessage: {
@@ -602,19 +487,11 @@ export const ConversationMissingText = (): JSX.Element =>
},
});
ConversationMissingText.story = {
name: 'Conversation: Missing Text',
};
export const ConversationMutedConversation = (): JSX.Element =>
renderConversation({
muteExpiresAt: Date.now() + 1000 * 60 * 60,
});
ConversationMutedConversation.story = {
name: 'Conversation: Muted Conversation',
};
export const ConversationAtMention = (): JSX.Element =>
renderConversation({
title: 'The Rebellion',
@@ -626,10 +503,6 @@ export const ConversationAtMention = (): JSX.Element =>
},
});
ConversationAtMention.story = {
name: 'Conversation: At Mention',
};
export function Headers(): JSX.Element {
return (
<Wrapper
@@ -700,10 +573,6 @@ export function FindByPhoneNumber(): JSX.Element {
);
}
FindByPhoneNumber.story = {
name: 'Find by phone number',
};
export function FindByUsername(): JSX.Element {
return (
<Wrapper
@@ -728,10 +597,6 @@ export function FindByUsername(): JSX.Element {
);
}
FindByUsername.story = {
name: 'Find by username',
};
export function SearchResultsLoadingSkeleton(): JSX.Element {
return (
<Wrapper
@@ -746,10 +611,6 @@ export function SearchResultsLoadingSkeleton(): JSX.Element {
);
}
SearchResultsLoadingSkeleton.story = {
name: 'Search results loading skeleton',
};
export function KitchenSink(): JSX.Element {
return (
<Wrapper
@@ -821,7 +682,3 @@ export function KitchenSink(): JSX.Element {
/>
);
}
KitchenSink.story = {
name: 'Kitchen sink',
};

View File

@@ -4,6 +4,8 @@
import React, { useState } from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './CrashReportDialog';
import { CrashReportDialog } from './CrashReportDialog';
import { setupI18n } from '../util/setupI18n';
import { sleep } from '../util/sleep';
@@ -11,11 +13,11 @@ import enMessages from '../../_locales/en/messages.json';
export default {
title: 'Components/CrashReportDialog',
};
} satisfies Meta<PropsType>;
const i18n = setupI18n('en', enMessages);
export const _CrashReportDialog = (): JSX.Element => {
export function Basic(): JSX.Element {
const [isPending, setIsPending] = useState(false);
return (
@@ -31,8 +33,4 @@ export const _CrashReportDialog = (): JSX.Element => {
eraseCrashReports={action('eraseCrashReports')}
/>
);
};
_CrashReportDialog.story = {
name: 'CrashReportDialog',
};
}

View File

@@ -13,7 +13,7 @@ type PropsActionsType = {
eraseCrashReports: () => void;
};
type PropsType = {
export type PropsType = {
i18n: LocalizerType;
isPending: boolean;
} & PropsActionsType;

View File

@@ -5,6 +5,7 @@ import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './CustomColorEditor';
import { CustomColorEditor } from './CustomColorEditor';
@@ -12,7 +13,7 @@ import { setupI18n } from '../util/setupI18n';
export default {
title: 'Components/CustomColorEditor',
};
} satisfies Meta<PropsType>;
const i18n = setupI18n('en', enMessages);

View File

@@ -5,16 +5,18 @@ import type { ComponentProps } from 'react';
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './CustomizingPreferredReactionsModal';
import { CustomizingPreferredReactionsModal } from './CustomizingPreferredReactionsModal';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/CustomizingPreferredReactionsModal',
};
} satisfies Meta<PropsType>;
const defaultProps: ComponentProps<typeof CustomizingPreferredReactionsModal> =
{
@@ -50,10 +52,6 @@ export function DraftEmojiSelected(): JSX.Element {
);
}
DraftEmojiSelected.story = {
name: 'Draft emoji selected',
};
export function Saving(): JSX.Element {
return <CustomizingPreferredReactionsModal {...defaultProps} isSaving />;
}
@@ -61,7 +59,3 @@ export function Saving(): JSX.Element {
export function HadError(): JSX.Element {
return <CustomizingPreferredReactionsModal {...defaultProps} hadSaveError />;
}
HadError.story = {
name: 'Had error',
};

View File

@@ -19,7 +19,7 @@ import { convertShortName } from './emoji/lib';
import { offsetDistanceModifier } from '../util/popperUtil';
import { handleOutsideClick } from '../util/handleOutsideClick';
type PropsType = {
export type PropsType = {
draftPreferredReactions: ReadonlyArray<string>;
hadSaveError: boolean;
i18n: LocalizerType;

View File

@@ -4,6 +4,7 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './DebugLogWindow';
import { DebugLogWindow } from './DebugLogWindow';
@@ -31,12 +32,8 @@ const createProps = (): PropsType => ({
export default {
title: 'Components/DebugLogWindow',
};
} satisfies Meta<PropsType>;
export const _DebugLogWindow = (): JSX.Element => (
<DebugLogWindow {...createProps()} />
);
_DebugLogWindow.story = {
name: 'DebugLogWindow',
};
export function Basic(): JSX.Element {
return <DebugLogWindow {...createProps()} />;
}

View File

@@ -2,8 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { select } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import type { PropsType } from './DialogExpiredBuild';
import { DialogExpiredBuild } from './DialogExpiredBuild';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -14,14 +14,12 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/DialogExpiredBuild',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
export const _DialogExpiredBuild = (): JSX.Element => {
const containerWidthBreakpoint = select(
'containerWidthBreakpoint',
WidthBreakpoint,
WidthBreakpoint.Wide
);
export function Basic(): JSX.Element {
const containerWidthBreakpoint = WidthBreakpoint.Wide;
return (
<FakeLeftPaneContainer containerWidthBreakpoint={containerWidthBreakpoint}>
@@ -31,8 +29,4 @@ export const _DialogExpiredBuild = (): JSX.Element => {
/>
</FakeLeftPaneContainer>
);
};
_DialogExpiredBuild.story = {
name: 'DialogExpiredBuild',
};
}

View File

@@ -4,6 +4,7 @@
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './DialogNetworkStatus';
import { DialogNetworkStatus } from './DialogNetworkStatus';
import { SocketStatus } from '../types/SocketStatus';
@@ -27,7 +28,7 @@ const defaultProps = {
export default {
title: 'Components/DialogNetworkStatus',
};
} satisfies Meta<PropsType>;
export function KnobsPlayground(args: PropsType): JSX.Element {
/*
@@ -68,10 +69,6 @@ export function ConnectingWide(): JSX.Element {
);
}
ConnectingWide.story = {
name: 'Connecting Wide',
};
export function ClosingWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -84,10 +81,6 @@ export function ClosingWide(): JSX.Element {
);
}
ClosingWide.story = {
name: 'Closing Wide',
};
export function ClosedWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -100,10 +93,6 @@ export function ClosedWide(): JSX.Element {
);
}
ClosedWide.story = {
name: 'Closed Wide',
};
export function OfflineWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -116,10 +105,6 @@ export function OfflineWide(): JSX.Element {
);
}
OfflineWide.story = {
name: 'Offline Wide',
};
export function ConnectingNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -132,10 +117,6 @@ export function ConnectingNarrow(): JSX.Element {
);
}
ConnectingNarrow.story = {
name: 'Connecting Narrow',
};
export function ClosingNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -148,10 +129,6 @@ export function ClosingNarrow(): JSX.Element {
);
}
ClosingNarrow.story = {
name: 'Closing Narrow',
};
export function ClosedNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -164,10 +141,6 @@ export function ClosedNarrow(): JSX.Element {
);
}
ClosedNarrow.story = {
name: 'Closed Narrow',
};
export function OfflineNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -179,7 +152,3 @@ export function OfflineNarrow(): JSX.Element {
</FakeLeftPaneContainer>
);
}
OfflineNarrow.story = {
name: 'Offline Narrow',
};

View File

@@ -3,7 +3,8 @@
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './DialogRelink';
import { DialogRelink } from './DialogRelink';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -35,7 +36,7 @@ const permutations = [
export default {
title: 'Components/DialogRelink',
};
} satisfies Meta<PropsType>;
export function Iterations(): JSX.Element {
return (

View File

@@ -2,8 +2,9 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { select } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './DialogUpdate';
import { DialogUpdate } from './DialogUpdate';
import { DialogType } from '../types/Dialogs';
import { WidthBreakpoint } from './_util';
@@ -29,15 +30,13 @@ const defaultProps = {
export default {
title: 'Components/DialogUpdate',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
export function KnobsPlayground(): JSX.Element {
const containerWidthBreakpoint = select(
'containerWidthBreakpoint',
WidthBreakpoint,
WidthBreakpoint.Wide
);
const dialogType = select('dialogType', DialogType, DialogType.AutoUpdate);
const containerWidthBreakpoint = WidthBreakpoint.Wide;
const dialogType = DialogType.AutoUpdate;
return (
<FakeLeftPaneContainer containerWidthBreakpoint={containerWidthBreakpoint}>
@@ -64,10 +63,6 @@ export function UpdateWide(): JSX.Element {
);
}
UpdateWide.story = {
name: 'Update (Wide)',
};
export function DownloadedWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -81,10 +76,6 @@ export function DownloadedWide(): JSX.Element {
);
}
DownloadedWide.story = {
name: 'Downloaded (Wide)',
};
export function DownloadReadyWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -99,10 +90,6 @@ export function DownloadReadyWide(): JSX.Element {
);
}
DownloadReadyWide.story = {
name: 'DownloadReady (Wide)',
};
export function FullDownloadReadyWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -117,10 +104,6 @@ export function FullDownloadReadyWide(): JSX.Element {
);
}
FullDownloadReadyWide.story = {
name: 'FullDownloadReady (Wide)',
};
export function DownloadingWide(): JSX.Element {
const [downloadedSize, setDownloadedSize] = React.useState(0);
@@ -153,10 +136,6 @@ export function DownloadingWide(): JSX.Element {
);
}
DownloadingWide.story = {
name: 'Downloading (Wide)',
};
export function CannotUpdateWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -170,10 +149,6 @@ export function CannotUpdateWide(): JSX.Element {
);
}
CannotUpdateWide.story = {
name: 'Cannot_Update (Wide)',
};
export function CannotUpdateBetaWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -187,10 +162,6 @@ export function CannotUpdateBetaWide(): JSX.Element {
);
}
CannotUpdateBetaWide.story = {
name: 'Cannot_Update_Beta (Wide)',
};
export function CannotUpdateRequireManualWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -204,10 +175,6 @@ export function CannotUpdateRequireManualWide(): JSX.Element {
);
}
CannotUpdateRequireManualWide.story = {
name: 'Cannot_Update_Require_Manual (Wide)',
};
export function CannotUpdateRequireManualBetaWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -221,10 +188,6 @@ export function CannotUpdateRequireManualBetaWide(): JSX.Element {
);
}
CannotUpdateRequireManualBetaWide.story = {
name: 'Cannot_Update_Require_Manual_Beta (Wide)',
};
export function MacOSReadOnlyWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -238,10 +201,6 @@ export function MacOSReadOnlyWide(): JSX.Element {
);
}
MacOSReadOnlyWide.story = {
name: 'MacOS_Read_Only (Wide)',
};
export function UnsupportedOSWide(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Wide}>
@@ -255,10 +214,6 @@ export function UnsupportedOSWide(): JSX.Element {
);
}
UnsupportedOSWide.story = {
name: 'UnsupportedOS (Wide)',
};
export function UpdateNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -272,10 +227,6 @@ export function UpdateNarrow(): JSX.Element {
);
}
UpdateNarrow.story = {
name: 'Update (Narrow)',
};
export function DownloadedNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -289,10 +240,6 @@ export function DownloadedNarrow(): JSX.Element {
);
}
DownloadedNarrow.story = {
name: 'Downloaded (Narrow)',
};
export function DownloadReadyNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -307,10 +254,6 @@ export function DownloadReadyNarrow(): JSX.Element {
);
}
DownloadReadyNarrow.story = {
name: 'DownloadReady (Narrow)',
};
export function FullDownloadReadyNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -325,10 +268,6 @@ export function FullDownloadReadyNarrow(): JSX.Element {
);
}
FullDownloadReadyNarrow.story = {
name: 'FullDownloadReady (Narrow)',
};
export function DownloadingNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -342,10 +281,6 @@ export function DownloadingNarrow(): JSX.Element {
);
}
DownloadingNarrow.story = {
name: 'Downloading (Narrow)',
};
export function CannotUpdateNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -359,10 +294,6 @@ export function CannotUpdateNarrow(): JSX.Element {
);
}
CannotUpdateNarrow.story = {
name: 'Cannot Update (Narrow)',
};
export function CannotUpdateBetaNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -376,10 +307,6 @@ export function CannotUpdateBetaNarrow(): JSX.Element {
);
}
CannotUpdateBetaNarrow.story = {
name: 'Cannot Update Beta (Narrow)',
};
export function CannotUpdateRequireManualNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -393,10 +320,6 @@ export function CannotUpdateRequireManualNarrow(): JSX.Element {
);
}
CannotUpdateRequireManualNarrow.story = {
name: 'Cannot_Update_Require_Manual (Narrow)',
};
export function CannotUpdateRequireManualBetaNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -410,10 +333,6 @@ export function CannotUpdateRequireManualBetaNarrow(): JSX.Element {
);
}
CannotUpdateRequireManualBetaNarrow.story = {
name: 'Cannot_Update_Require_Manual_Beta (Narrow)',
};
export function MacOSReadOnlyNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -427,10 +346,6 @@ export function MacOSReadOnlyNarrow(): JSX.Element {
);
}
MacOSReadOnlyNarrow.story = {
name: 'MacOS_Read_Only (Narrow)',
};
export function UnsupportedOSNarrow(): JSX.Element {
return (
<FakeLeftPaneContainer containerWidthBreakpoint={WidthBreakpoint.Narrow}>
@@ -443,7 +358,3 @@ export function UnsupportedOSNarrow(): JSX.Element {
</FakeLeftPaneContainer>
);
}
UnsupportedOSNarrow.story = {
name: 'UnsupportedOS (Narrow)',
};

View File

@@ -3,7 +3,8 @@
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './DisappearingTimeDialog';
import { DisappearingTimeDialog } from './DisappearingTimeDialog';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -12,7 +13,7 @@ import { EXPIRE_TIMERS } from '../test-both/util/expireTimers';
export default {
title: 'Components/DisappearingTimeDialog',
};
} satisfies Meta<PropsType>;
const i18n = setupI18n('en', enMessages);

View File

@@ -2,7 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import type { Meta } from '@storybook/react';
import type { Props } from './DisappearingTimerSelect';
import { DisappearingTimerSelect } from './DisappearingTimerSelect';
import { setupI18n } from '../util/setupI18n';
import { DurationInSeconds } from '../util/durations';
@@ -10,15 +11,15 @@ import enMessages from '../../_locales/en/messages.json';
export default {
title: 'Components/DisappearingTimerSelect',
};
} satisfies Meta<Props>;
const i18n = setupI18n('en', enMessages);
type Props = {
type Args = {
initialValue: number;
};
function TimerSelectWrap({ initialValue }: Props): JSX.Element {
function TimerSelectWrap({ initialValue }: Args): JSX.Element {
const [value, setValue] = useState(initialValue);
return (
@@ -34,14 +35,6 @@ export function InitialValue1Day(): JSX.Element {
return <TimerSelectWrap initialValue={24 * 3600} />;
}
InitialValue1Day.story = {
name: 'Initial value: 1 day',
};
export function InitialValue3DaysCustomTime(): JSX.Element {
return <TimerSelectWrap initialValue={3 * 24 * 3600} />;
}
InitialValue3DaysCustomTime.story = {
name: 'Initial value 3 days (Custom time)',
};

View File

@@ -2,8 +2,9 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { Meta, Story } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import enMessages from '../../_locales/en/messages.json';
import { setupI18n } from '../util/setupI18n';
import type { UsernameReservationType } from '../types/Username';
@@ -29,11 +30,9 @@ export default {
argTypes: {
currentUsername: {
type: { name: 'string', required: false },
defaultValue: undefined,
},
state: {
control: { type: 'radio' },
defaultValue: State.Open,
options: {
Open: State.Open,
Closed: State.Closed,
@@ -43,7 +42,6 @@ export default {
},
error: {
control: { type: 'radio' },
defaultValue: undefined,
options: {
None: undefined,
NotEnoughCharacters: UsernameReservationError.NotEnoughCharacters,
@@ -54,26 +52,24 @@ export default {
General: UsernameReservationError.General,
},
},
maxUsername: {
defaultValue: 20,
},
minUsername: {
defaultValue: 3,
},
discriminator: {
reservation: {
type: { name: 'string', required: false },
defaultValue: undefined,
},
i18n: {
defaultValue: i18n,
},
onClose: { action: true },
onError: { action: true },
setUsernameReservationError: { action: true },
reserveUsername: { action: true },
confirmUsername: { action: true },
},
} as Meta;
args: {
currentUsername: undefined,
state: State.Open,
error: undefined,
maxNickname: 20,
minNickname: 3,
reservation: undefined,
i18n,
onClose: action('onClose'),
setUsernameReservationError: action('setUsernameReservationError'),
reserveUsername: action('reserveUsername'),
confirmUsername: action('confirmUsername'),
},
} satisfies Meta<PropsType>;
type ArgsType = PropsType & {
discriminator?: string;
@@ -81,7 +77,7 @@ type ArgsType = PropsType & {
};
// eslint-disable-next-line react/function-component-definition
const Template: Story<ArgsType> = args => {
const Template: StoryFn<ArgsType> = args => {
let { reservation } = args;
if (!reservation && args.discriminator) {
reservation = {
@@ -95,27 +91,16 @@ const Template: Story<ArgsType> = args => {
export const WithoutUsername = Template.bind({});
WithoutUsername.args = {};
WithoutUsername.story = {
name: 'without current username',
};
export const WithUsername = Template.bind({});
WithUsername.args = {};
WithUsername.story = {
name: 'with current username',
args: {
currentUsername: 'signaluser.12',
},
WithUsername.args = {
currentUsername: 'signaluser.12',
};
export const WithReservation = Template.bind({});
WithReservation.args = {};
WithReservation.story = {
name: 'with reservation',
args: {
currentUsername: 'reserved',
reservation: DEFAULT_RESERVATION,
},
WithReservation.args = {
currentUsername: 'reserved',
reservation: DEFAULT_RESERVATION,
};
export const UsernameEditingConfirming = Template.bind({});
@@ -123,9 +108,6 @@ UsernameEditingConfirming.args = {
state: State.Confirming,
currentUsername: 'signaluser.12',
};
UsernameEditingConfirming.story = {
name: 'Username editing, Confirming',
};
export const UsernameEditingUsernameTaken = Template.bind({});
UsernameEditingUsernameTaken.args = {
@@ -133,9 +115,6 @@ UsernameEditingUsernameTaken.args = {
error: UsernameReservationError.UsernameNotAvailable,
currentUsername: 'signaluser.12',
};
UsernameEditingUsernameTaken.story = {
name: 'Username editing, username taken',
};
export const UsernameEditingUsernameWrongCharacters = Template.bind({});
UsernameEditingUsernameWrongCharacters.args = {
@@ -143,9 +122,6 @@ UsernameEditingUsernameWrongCharacters.args = {
error: UsernameReservationError.CheckCharacters,
currentUsername: 'signaluser.12',
};
UsernameEditingUsernameWrongCharacters.story = {
name: 'Username editing, Wrong Characters',
};
export const UsernameEditingUsernameTooShort = Template.bind({});
UsernameEditingUsernameTooShort.args = {
@@ -153,9 +129,6 @@ UsernameEditingUsernameTooShort.args = {
error: UsernameReservationError.NotEnoughCharacters,
currentUsername: 'sig',
};
UsernameEditingUsernameTooShort.story = {
name: 'Username editing, username too short',
};
export const UsernameEditingGeneralError = Template.bind({});
UsernameEditingGeneralError.args = {
@@ -163,6 +136,3 @@ UsernameEditingGeneralError.args = {
error: UsernameReservationError.General,
currentUsername: 'signaluser.12',
};
UsernameEditingGeneralError.story = {
name: 'Username editing, general error',
};

View File

@@ -2,9 +2,9 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { text } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './ErrorModal';
import { ErrorModal } from './ErrorModal';
@@ -14,15 +14,17 @@ import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
title: text('title', overrideProps.title || ''),
description: text('description', overrideProps.description || ''),
title: overrideProps.title ?? '',
description: overrideProps.description ?? '',
i18n,
onClose: action('onClick'),
});
export default {
title: 'Components/ErrorModal',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
export function Normal(): JSX.Element {
return <ErrorModal {...createProps()} />;

View File

@@ -2,10 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import { text } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import type { AttachmentType } from '../types/Attachment';
import type { PropsType } from './ForwardMessagesModal';
@@ -15,25 +13,25 @@ import { getDefaultConversation } from '../test-both/helpers/getDefaultConversat
import { setupI18n } from '../util/setupI18n';
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
import { CompositionTextArea } from './CompositionTextArea';
import type { MessageForwardDraft } from '../util/maybeForwardMessages';
import type { MessageForwardDraft } from '../types/ForwardDraft';
const createAttachment = (
props: Partial<AttachmentType> = {}
): AttachmentType => ({
pending: false,
path: 'fileName.jpg',
contentType: stringToMIMEType(
text('attachment contentType', props.contentType || '')
),
fileName: text('attachment fileName', props.fileName || ''),
contentType: stringToMIMEType(props.contentType ?? ''),
fileName: props.fileName ?? '',
screenshotPath: props.pending === false ? props.screenshotPath : undefined,
url: text('attachment url', props.pending === false ? props.url || '' : ''),
url: props.pending === false ? props.url ?? '' : '',
size: 3433,
});
export default {
title: 'Components/ForwardMessageModal',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
const i18n = setupI18n('en', enMessages);
@@ -82,7 +80,7 @@ function getMessageForwardDraft(
attachments: overrideProps.attachments,
hasContact: Boolean(overrideProps.hasContact),
isSticker: Boolean(overrideProps.isSticker),
messageBody: text('messageBody', overrideProps.messageBody || ''),
messageBody: overrideProps.messageBody ?? '',
originalMessageId: '123',
previews: overrideProps.previews ?? [],
};
@@ -102,10 +100,6 @@ export function WithText(): JSX.Element {
);
}
WithText.story = {
name: 'with text',
};
export function ASticker(): JSX.Element {
return (
<ForwardMessagesModal
@@ -116,10 +110,6 @@ export function ASticker(): JSX.Element {
);
}
ASticker.story = {
name: 'a sticker',
};
export function WithAContact(): JSX.Element {
return (
<ForwardMessagesModal
@@ -130,10 +120,6 @@ export function WithAContact(): JSX.Element {
);
}
WithAContact.story = {
name: 'with a contact',
};
export function LinkPreview(): JSX.Element {
return (
<ForwardMessagesModal
@@ -162,10 +148,6 @@ export function LinkPreview(): JSX.Element {
);
}
LinkPreview.story = {
name: 'link preview',
};
export function MediaAttachments(): JSX.Element {
return (
<ForwardMessagesModal
@@ -196,10 +178,6 @@ export function MediaAttachments(): JSX.Element {
);
}
MediaAttachments.story = {
name: 'media attachments',
};
export function AnnouncementOnlyGroupsNonAdmin(): JSX.Element {
return (
<ForwardMessagesModal
@@ -213,7 +191,3 @@ export function AnnouncementOnlyGroupsNonAdmin(): JSX.Element {
/>
);
}
AnnouncementOnlyGroupsNonAdmin.story = {
name: 'announcement only groups non-admin',
};

View File

@@ -27,11 +27,6 @@ import {
shouldNeverBeCalled,
asyncShouldNeverBeCalled,
} from '../util/shouldNeverBeCalled';
import type { MessageForwardDraft } from '../util/maybeForwardMessages';
import {
isDraftEditable,
isDraftForwardable,
} from '../util/maybeForwardMessages';
import type { LinkPreviewType } from '../types/message/LinkPreviews';
import { LinkPreviewSourceType } from '../types/LinkPreview';
import { ToastType } from '../types/Toast';
@@ -41,6 +36,11 @@ import { BodyRange } from '../types/BodyRange';
import { UserText } from './UserText';
import { Modal } from './Modal';
import { SizeObserver } from '../hooks/useSizeObserver';
import {
isDraftEditable,
isDraftForwardable,
type MessageForwardDraft,
} from '../types/ForwardDraft';
export type DataPropsType = {
candidateConversations: ReadonlyArray<ConversationType>;

View File

@@ -3,9 +3,9 @@
import React from 'react';
import { memoize, times } from 'lodash';
import { number } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './GroupCallOverflowArea';
import { GroupCallOverflowArea } from './GroupCallOverflowArea';
import { setupI18n } from '../util/setupI18n';
import { getDefaultConversationWithServiceId } from '../test-both/helpers/getDefaultConversation';
@@ -34,7 +34,9 @@ const allRemoteParticipants = times(MAX_PARTICIPANTS).map(index => ({
export default {
title: 'Components/GroupCallOverflowArea',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
const defaultProps = {
getFrameBuffer: memoize(() => Buffer.alloc(FRAME_BUFFER_SIZE)),
@@ -68,10 +70,6 @@ export function NoOverflowedParticipants(): JSX.Element {
);
}
NoOverflowedParticipants.story = {
name: 'No overflowed participants',
};
export function OneOverflowedParticipant(): JSX.Element {
return (
<Container>
@@ -83,10 +81,6 @@ export function OneOverflowedParticipant(): JSX.Element {
);
}
OneOverflowedParticipant.story = {
name: 'One overflowed participant',
};
export function ThreeOverflowedParticipants(): JSX.Element {
return (
<Container>
@@ -98,10 +92,6 @@ export function ThreeOverflowedParticipants(): JSX.Element {
);
}
ThreeOverflowedParticipants.story = {
name: 'Three overflowed participants',
};
export function ManyOverflowedParticipants(): JSX.Element {
return (
<Container>
@@ -109,18 +99,9 @@ export function ManyOverflowedParticipants(): JSX.Element {
{...defaultProps}
overflowedParticipants={allRemoteParticipants.slice(
0,
number('Participant count', MAX_PARTICIPANTS, {
range: true,
min: 0,
max: MAX_PARTICIPANTS,
step: 1,
})
MAX_PARTICIPANTS
)}
/>
</Container>
);
}
ManyOverflowedParticipants.story = {
name: 'Many overflowed participants',
};

View File

@@ -15,7 +15,7 @@ const OVERFLOW_SCROLL_BUTTON_RATIO = 0.75;
// This should be an integer, as sub-pixel widths can cause performance issues.
export const OVERFLOW_PARTICIPANT_WIDTH = 140;
type PropsType = {
export type PropsType = {
getFrameBuffer: () => Buffer;
getGroupCallVideoFrameSource: (demuxId: number) => VideoFrameSource;
i18n: LocalizerType;

View File

@@ -2,9 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { memoize, noop } from 'lodash';
import { select } from '@storybook/addon-knobs';
import { memoize } from 'lodash';
import type { Meta } from '@storybook/react';
import type { PropsType } from './GroupCallRemoteParticipant';
import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
@@ -46,8 +45,9 @@ const createProps = (
} = {}
): PropsType => ({
getFrameBuffer,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getGroupCallVideoFrameSource: noop as any,
getGroupCallVideoFrameSource: () => {
return { receiveVideoFrame: () => undefined };
},
i18n,
audioLevel: 0,
remoteParticipant: {
@@ -72,7 +72,9 @@ const createProps = (
export default {
title: 'Components/GroupCallRemoteParticipant',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
return (
@@ -101,7 +103,7 @@ export function Speaking(): JSX.Element {
left: (120 + 10) * index,
top: 0,
width: 120,
audioLevel: select('audioLevel', [0, 0.5, 1], 0.5),
audioLevel: 0.5,
remoteParticipantsCount,
},
{ hasRemoteAudio: true, presenting }
@@ -126,10 +128,6 @@ export function IsInPip(): JSX.Element {
);
}
IsInPip.story = {
name: 'isInPip',
};
export function Blocked(): JSX.Element {
return (
<GroupCallRemoteParticipant

View File

@@ -2,17 +2,17 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './GroupDescriptionInput';
import { GroupDescriptionInput } from './GroupDescriptionInput';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/GroupDescriptionInput',
};
} satisfies Meta<PropsType>;
function Wrapper({
disabled,

View File

@@ -6,7 +6,7 @@ import React, { forwardRef } from 'react';
import { Input } from './Input';
import type { LocalizerType } from '../types/Util';
type PropsType = {
export type PropsType = {
disabled?: boolean;
i18n: LocalizerType;
onChangeValue: (value: string) => void;

View File

@@ -2,17 +2,17 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './GroupTitleInput';
import { GroupTitleInput } from './GroupTitleInput';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/GroupTitleInput',
};
} satisfies Meta<PropsType>;
function Wrapper({
disabled,

View File

@@ -6,7 +6,7 @@ import React, { forwardRef } from 'react';
import { Input } from './Input';
import type { LocalizerType } from '../types/Util';
type PropsType = {
export type PropsType = {
disabled?: boolean;
i18n: LocalizerType;
onChangeValue: (value: string) => void;

View File

@@ -5,6 +5,7 @@ import * as React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './GroupV1MigrationDialog';
import { GroupV1MigrationDialog } from './GroupV1MigrationDialog';
import type { ConversationType } from '../state/ducks/conversations';
@@ -48,16 +49,12 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/GroupV1MigrationDialog',
};
} satisfies Meta<PropsType>;
export function NotYetMigratedBasic(): JSX.Element {
return <GroupV1MigrationDialog {...createProps()} />;
}
NotYetMigratedBasic.story = {
name: 'Not yet migrated, basic',
};
export function MigratedBasic(): JSX.Element {
return (
<GroupV1MigrationDialog
@@ -68,10 +65,6 @@ export function MigratedBasic(): JSX.Element {
);
}
MigratedBasic.story = {
name: 'Migrated, basic',
};
export function MigratedYouAreInvited(): JSX.Element {
return (
<GroupV1MigrationDialog
@@ -83,10 +76,6 @@ export function MigratedYouAreInvited(): JSX.Element {
);
}
MigratedYouAreInvited.story = {
name: 'Migrated, you are invited',
};
export function NotYetMigratedMultipleDroppedAndInvitedMembers(): JSX.Element {
return (
<GroupV1MigrationDialog
@@ -98,10 +87,6 @@ export function NotYetMigratedMultipleDroppedAndInvitedMembers(): JSX.Element {
);
}
NotYetMigratedMultipleDroppedAndInvitedMembers.story = {
name: 'Not yet migrated, multiple dropped and invited members',
};
export function NotYetMigratedNoMembers(): JSX.Element {
return (
<GroupV1MigrationDialog
@@ -113,10 +98,6 @@ export function NotYetMigratedNoMembers(): JSX.Element {
);
}
NotYetMigratedNoMembers.story = {
name: 'Not yet migrated, no members',
};
export function NotYetMigratedJustDroppedMember(): JSX.Element {
return (
<GroupV1MigrationDialog
@@ -126,7 +107,3 @@ export function NotYetMigratedJustDroppedMember(): JSX.Element {
/>
);
}
NotYetMigratedJustDroppedMember.story = {
name: 'Not yet migrated, just dropped member',
};

View File

@@ -2,9 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { boolean, number, text } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './GroupV2JoinDialog';
import { GroupV2JoinDialog } from './GroupV2JoinDialog';
import { setupI18n } from '../util/setupI18n';
@@ -13,13 +12,10 @@ import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
memberCount: number('memberCount', overrideProps.memberCount || 12),
memberCount: overrideProps.memberCount ?? 12,
avatar: overrideProps.avatar,
title: text('title', overrideProps.title || 'Random Group!'),
approvalRequired: boolean(
'approvalRequired',
overrideProps.approvalRequired || false
),
title: overrideProps.title ?? 'Random Group!',
approvalRequired: overrideProps.approvalRequired ?? false,
groupDescription: overrideProps.groupDescription,
join: action('join'),
onClose: action('onClose'),
@@ -28,7 +24,9 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
export default {
title: 'Components/GroupV2JoinDialog',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
export function Basic(): JSX.Element {
return <GroupV2JoinDialog {...createProps()} />;
@@ -45,10 +43,6 @@ export function ApprovalRequired(): JSX.Element {
);
}
ApprovalRequired.story = {
name: 'Approval required',
};
export function WithAvatar(): JSX.Element {
return (
<GroupV2JoinDialog
@@ -62,10 +56,6 @@ export function WithAvatar(): JSX.Element {
);
}
WithAvatar.story = {
name: 'With avatar',
};
export function WithOneMember(): JSX.Element {
return (
<GroupV2JoinDialog
@@ -77,10 +67,6 @@ export function WithOneMember(): JSX.Element {
);
}
WithOneMember.story = {
name: 'With one member',
};
export function AvatarLoadingState(): JSX.Element {
return (
<GroupV2JoinDialog
@@ -94,10 +80,6 @@ export function AvatarLoadingState(): JSX.Element {
);
}
AvatarLoadingState.story = {
name: 'Avatar loading state',
};
export function Full(): JSX.Element {
return (
<GroupV2JoinDialog

View File

@@ -2,13 +2,13 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { Meta } from '@storybook/react';
import { IdenticonSVGForContact, IdenticonSVGForGroup } from './IdenticonSVG';
import { AvatarColorMap } from '../types/Colors';
export default {
title: 'Components/IdenticonSVG',
};
} satisfies Meta;
export function AllColorsForContact(): JSX.Element {
const stories: Array<JSX.Element> = [];

View File

@@ -2,16 +2,17 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './InContactsIcon';
import { InContactsIcon } from './InContactsIcon';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/InContactsIcon',
};
} satisfies Meta<PropsType>;
export function Default(): JSX.Element {
return <InContactsIcon i18n={i18n} />;

View File

@@ -7,7 +7,7 @@ import classNames from 'classnames';
import { Tooltip } from './Tooltip';
import type { LocalizerType } from '../types/Util';
type PropsType = {
export type PropsType = {
className?: string;
tooltipContainerRef?: React.RefObject<HTMLElement>;
i18n: LocalizerType;

View File

@@ -2,7 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState, useEffect, useMemo } from 'react';
import type { Meta, Story } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import { noop } from 'lodash';
import { Inbox } from './Inbox';
@@ -16,41 +16,15 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/Inbox',
argTypes: {
i18n: {
defaultValue: i18n,
},
hasInitialLoadCompleted: {
defaultValue: false,
},
daysAgo: {
control: 'select',
defaultValue: undefined,
options: [undefined, 1, 2, 3, 7, 14, 21],
},
isCustomizingPreferredReactions: {
defaultValue: false,
},
onConversationClosed: {
action: true,
},
onConversationOpened: {
action: true,
},
scrollToMessage: {
action: true,
},
showConversation: {
action: true,
},
showWhatsNewModal: {
action: true,
},
args: {
i18n,
hasInitialLoadCompleted: false,
isCustomizingPreferredReactions: false,
},
} as Meta;
} satisfies Meta<PropsType>;
// eslint-disable-next-line react/function-component-definition
const Template: Story<PropsType & { daysAgo?: number }> = ({
const Template: StoryFn<PropsType & { daysAgo?: number }> = ({
daysAgo,
...args
}) => {
@@ -90,6 +64,3 @@ const Template: Story<PropsType & { daysAgo?: number }> = ({
};
export const Default = Template.bind({});
Default.story = {
name: 'Default',
};

View File

@@ -3,7 +3,8 @@
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './IncomingCallBar';
import { IncomingCallBar } from './IncomingCallBar';
import { CallMode } from '../types/Calling';
import { setupI18n } from '../util/setupI18n';
@@ -53,7 +54,7 @@ const groupConversation = getDefaultConversation({
export default {
title: 'Components/IncomingCallBar',
};
} satisfies Meta<PropsType>;
export function IncomingDirectCallVideo(): JSX.Element {
return (
@@ -66,10 +67,6 @@ export function IncomingDirectCallVideo(): JSX.Element {
);
}
IncomingDirectCallVideo.story = {
name: 'Incoming direct call (video)',
};
export function IncomingDirectCallAudio(): JSX.Element {
return (
<IncomingCallBar
@@ -81,10 +78,6 @@ export function IncomingDirectCallAudio(): JSX.Element {
);
}
IncomingDirectCallAudio.story = {
name: 'Incoming direct call (audio)',
};
export function IncomingGroupCallOnlyCallingYou(): JSX.Element {
return (
<IncomingCallBar
@@ -97,10 +90,6 @@ export function IncomingGroupCallOnlyCallingYou(): JSX.Element {
);
}
IncomingGroupCallOnlyCallingYou.story = {
name: 'Incoming group call (only calling you)',
};
export function IncomingGroupCallCallingYouAnd1Other(): JSX.Element {
return (
<IncomingCallBar
@@ -113,10 +102,6 @@ export function IncomingGroupCallCallingYouAnd1Other(): JSX.Element {
);
}
IncomingGroupCallCallingYouAnd1Other.story = {
name: 'Incoming group call (calling you and 1 other)',
};
export function IncomingGroupCallCallingYouAnd2Others(): JSX.Element {
return (
<IncomingCallBar
@@ -132,10 +117,6 @@ export function IncomingGroupCallCallingYouAnd2Others(): JSX.Element {
);
}
IncomingGroupCallCallingYouAnd2Others.story = {
name: 'Incoming group call (calling you and 2 others)',
};
export function IncomingGroupCallCallingYouAnd3Others(): JSX.Element {
return (
<IncomingCallBar
@@ -152,10 +133,6 @@ export function IncomingGroupCallCallingYouAnd3Others(): JSX.Element {
);
}
IncomingGroupCallCallingYouAnd3Others.story = {
name: 'Incoming group call (calling you and 3 others)',
};
export function IncomingGroupCallCallingYouAnd4Others(): JSX.Element {
return (
<IncomingCallBar
@@ -172,7 +149,3 @@ export function IncomingGroupCallCallingYouAnd4Others(): JSX.Element {
/>
);
}
IncomingGroupCallCallingYouAnd4Others.story = {
name: 'Incoming group call (calling you and 4 others)',
};

View File

@@ -2,10 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import { text } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './Input';
import { Input } from './Input';
import { setupI18n } from '../util/setupI18n';
@@ -15,7 +13,9 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/Input',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
disabled: Boolean(overrideProps.disabled),
@@ -26,11 +26,8 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
icon: overrideProps.icon,
maxLengthCount: overrideProps.maxLengthCount,
onChange: action('onChange'),
placeholder: text(
'placeholder',
overrideProps.placeholder || 'Enter some text here'
),
value: text('value', overrideProps.value || ''),
placeholder: overrideProps.placeholder ?? 'Enter some text here',
value: overrideProps.value ?? '',
whenToShowRemainingCount: overrideProps.whenToShowRemainingCount,
});
@@ -55,10 +52,6 @@ export function HasClearButton(): JSX.Element {
);
}
HasClearButton.story = {
name: 'hasClearButton',
};
export function CharacterCount(): JSX.Element {
return (
<Controller
@@ -69,10 +62,6 @@ export function CharacterCount(): JSX.Element {
);
}
CharacterCount.story = {
name: 'character count',
};
export function CharacterCountCustomizableShow(): JSX.Element {
return (
<Controller
@@ -84,10 +73,6 @@ export function CharacterCountCustomizableShow(): JSX.Element {
);
}
CharacterCountCustomizableShow.story = {
name: 'character count (customizable show)',
};
export function Expandable(): JSX.Element {
return (
<Controller
@@ -98,10 +83,6 @@ export function Expandable(): JSX.Element {
);
}
Expandable.story = {
name: 'expandable',
};
export function ExpandableWCount(): JSX.Element {
return (
<Controller
@@ -115,10 +96,6 @@ export function ExpandableWCount(): JSX.Element {
);
}
ExpandableWCount.story = {
name: 'expandable w/count',
};
export function Disabled(): JSX.Element {
return (
<Controller
@@ -129,10 +106,6 @@ export function Disabled(): JSX.Element {
);
}
Disabled.story = {
name: 'disabled',
};
export function SpellcheckDisabled(): JSX.Element {
return (
<Controller
@@ -142,7 +115,3 @@ export function SpellcheckDisabled(): JSX.Element {
/>
);
}
SpellcheckDisabled.story = {
name: 'spellcheck disabled',
};

View File

@@ -1,7 +1,7 @@
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Meta, Story } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import * as React from 'react';
import type { Props } from './Intl';
@@ -14,7 +14,7 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/Intl',
component: Intl,
} as Meta;
} satisfies Meta<Props>;
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
i18n,
@@ -24,7 +24,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
// eslint-disable-next-line max-len
// eslint-disable-next-line react/function-component-definition, local-rules/valid-i18n-keys
const Template: Story<Props> = args => <Intl {...args} />;
const Template: StoryFn<Props> = args => <Intl {...args} />;
export const NoReplacements = Template.bind({});
NoReplacements.args = createProps({

View File

@@ -2,10 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react';
import { action } from '@storybook/addon-actions';
import { boolean, select } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import type { PropsType } from './LeftPane';
import { LeftPane, LeftPaneMode } from './LeftPane';
import { CaptchaDialog } from './CaptchaDialog';
@@ -45,7 +43,9 @@ type OverridePropsType = Partial<PropsType> & {
export default {
title: 'Components/LeftPane',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
const defaultConversations: Array<ConversationType> = [
getDefaultConversation({
@@ -126,11 +126,8 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => {
};
}
const isUpdateDownloaded = boolean('isUpdateDownloaded', false);
const isContactManagementEnabled = boolean(
'isContactManagementEnabled',
true
);
const isUpdateDownloaded = false;
const isContactManagementEnabled = true;
return {
otherTabsUnreadStats: {
@@ -151,28 +148,20 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => {
hasFailedStorySends: false,
hasPendingUpdate: false,
i18n,
isMacOS: boolean('isMacOS', false),
isMacOS: false,
preferredWidthFromStorage: 320,
regionCode: 'US',
challengeStatus: select(
'challengeStatus',
['idle', 'required', 'pending'],
'idle'
),
crashReportCount: select('challengeReportCount', [0, 1], 0),
challengeStatus: 'idle',
crashReportCount: 0,
hasNetworkDialog: boolean('hasNetworkDialog', false),
hasExpiredDialog: boolean('hasExpiredDialog', false),
hasRelinkDialog: boolean('hasRelinkDialog', false),
hasUpdateDialog: boolean('hasUpdateDialog', false),
unsupportedOSDialogType: select(
'unsupportedOSDialogType',
['error', 'warning', undefined],
undefined
),
hasNetworkDialog: false,
hasExpiredDialog: false,
hasRelinkDialog: false,
hasUpdateDialog: false,
unsupportedOSDialogType: undefined,
isUpdateDownloaded,
isContactManagementEnabled,
navTabsCollapsed: boolean('navTabsCollapsed', false),
navTabsCollapsed: false,
setChallengeStatus: action('setChallengeStatus'),
lookupConversationWithoutServiceId:
@@ -315,10 +304,6 @@ export function InboxNoConversations(): JSX.Element {
);
}
InboxNoConversations.story = {
name: 'Inbox: no conversations',
};
export function InboxOnlyPinnedConversations(): JSX.Element {
return (
<LeftPaneInContainer
@@ -336,10 +321,6 @@ export function InboxOnlyPinnedConversations(): JSX.Element {
);
}
InboxOnlyPinnedConversations.story = {
name: 'Inbox: only pinned conversations',
};
export function InboxOnlyNonPinnedConversations(): JSX.Element {
return (
<LeftPaneInContainer
@@ -357,10 +338,6 @@ export function InboxOnlyNonPinnedConversations(): JSX.Element {
);
}
InboxOnlyNonPinnedConversations.story = {
name: 'Inbox: only non-pinned conversations',
};
export function InboxOnlyArchivedConversations(): JSX.Element {
return (
<LeftPaneInContainer
@@ -378,10 +355,6 @@ export function InboxOnlyArchivedConversations(): JSX.Element {
);
}
InboxOnlyArchivedConversations.story = {
name: 'Inbox: only archived conversations',
};
export function InboxPinnedAndArchivedConversations(): JSX.Element {
return (
<LeftPaneInContainer
@@ -399,10 +372,6 @@ export function InboxPinnedAndArchivedConversations(): JSX.Element {
);
}
InboxPinnedAndArchivedConversations.story = {
name: 'Inbox: pinned and archived conversations',
};
export function InboxNonPinnedAndArchivedConversations(): JSX.Element {
return (
<LeftPaneInContainer
@@ -420,10 +389,6 @@ export function InboxNonPinnedAndArchivedConversations(): JSX.Element {
);
}
InboxNonPinnedAndArchivedConversations.story = {
name: 'Inbox: non-pinned and archived conversations',
};
export function InboxPinnedAndNonPinnedConversations(): JSX.Element {
return (
<LeftPaneInContainer
@@ -441,18 +406,10 @@ export function InboxPinnedAndNonPinnedConversations(): JSX.Element {
);
}
InboxPinnedAndNonPinnedConversations.story = {
name: 'Inbox: pinned and non-pinned conversations',
};
export function InboxPinnedNonPinnedAndArchivedConversations(): JSX.Element {
return <LeftPaneInContainer {...useProps()} />;
}
InboxPinnedNonPinnedAndArchivedConversations.story = {
name: 'Inbox: pinned, non-pinned, and archived conversations',
};
export function SearchNoResultsWhenSearchingEverywhere(): JSX.Element {
return (
<LeftPaneInContainer
@@ -470,10 +427,6 @@ export function SearchNoResultsWhenSearchingEverywhere(): JSX.Element {
);
}
SearchNoResultsWhenSearchingEverywhere.story = {
name: 'Search: no results when searching everywhere',
};
export function SearchNoResultsWhenSearchingEverywhereSms(): JSX.Element {
return (
<LeftPaneInContainer
@@ -491,10 +444,6 @@ export function SearchNoResultsWhenSearchingEverywhereSms(): JSX.Element {
);
}
SearchNoResultsWhenSearchingEverywhereSms.story = {
name: 'Search: no results when searching everywhere (SMS)',
};
export function SearchNoResultsWhenSearchingInAConversation(): JSX.Element {
return (
<LeftPaneInContainer
@@ -513,10 +462,6 @@ export function SearchNoResultsWhenSearchingInAConversation(): JSX.Element {
);
}
SearchNoResultsWhenSearchingInAConversation.story = {
name: 'Search: no results when searching in a conversation',
};
export function SearchAllResultsLoading(): JSX.Element {
return (
<LeftPaneInContainer
@@ -534,10 +479,6 @@ export function SearchAllResultsLoading(): JSX.Element {
);
}
SearchAllResultsLoading.story = {
name: 'Search: all results loading',
};
export function SearchSomeResultsLoading(): JSX.Element {
return (
<LeftPaneInContainer
@@ -558,10 +499,6 @@ export function SearchSomeResultsLoading(): JSX.Element {
);
}
SearchSomeResultsLoading.story = {
name: 'Search: some results loading',
};
export function SearchHasConversationsAndContactsButNotMessages(): JSX.Element {
return (
<LeftPaneInContainer
@@ -582,10 +519,6 @@ export function SearchHasConversationsAndContactsButNotMessages(): JSX.Element {
);
}
SearchHasConversationsAndContactsButNotMessages.story = {
name: 'Search: has conversations and contacts, but not messages',
};
export function SearchAllResults(): JSX.Element {
return (
<LeftPaneInContainer
@@ -612,10 +545,6 @@ export function SearchAllResults(): JSX.Element {
);
}
SearchAllResults.story = {
name: 'Search: all results',
};
export function ArchiveNoArchivedConversations(): JSX.Element {
return (
<LeftPaneInContainer
@@ -632,10 +561,6 @@ export function ArchiveNoArchivedConversations(): JSX.Element {
);
}
ArchiveNoArchivedConversations.story = {
name: 'Archive: no archived conversations',
};
export function ArchiveArchivedConversations(): JSX.Element {
return (
<LeftPaneInContainer
@@ -652,10 +577,6 @@ export function ArchiveArchivedConversations(): JSX.Element {
);
}
ArchiveArchivedConversations.story = {
name: 'Archive: archived conversations',
};
export function ArchiveSearchingAConversation(): JSX.Element {
return (
<LeftPaneInContainer
@@ -672,10 +593,6 @@ export function ArchiveSearchingAConversation(): JSX.Element {
);
}
ArchiveSearchingAConversation.story = {
name: 'Archive: searching a conversation',
};
export function ComposeNoResults(): JSX.Element {
return (
<LeftPaneInContainer
@@ -694,10 +611,6 @@ export function ComposeNoResults(): JSX.Element {
);
}
ComposeNoResults.story = {
name: 'Compose: no results',
};
export function ComposeSomeContactsNoSearchTerm(): JSX.Element {
return (
<LeftPaneInContainer
@@ -716,10 +629,6 @@ export function ComposeSomeContactsNoSearchTerm(): JSX.Element {
);
}
ComposeSomeContactsNoSearchTerm.story = {
name: 'Compose: some contacts, no search term',
};
export function ComposeSomeContactsWithASearchTerm(): JSX.Element {
return (
<LeftPaneInContainer
@@ -738,10 +647,6 @@ export function ComposeSomeContactsWithASearchTerm(): JSX.Element {
);
}
ComposeSomeContactsWithASearchTerm.story = {
name: 'Compose: some contacts, with a search term',
};
export function ComposeSomeGroupsNoSearchTerm(): JSX.Element {
return (
<LeftPaneInContainer
@@ -760,10 +665,6 @@ export function ComposeSomeGroupsNoSearchTerm(): JSX.Element {
);
}
ComposeSomeGroupsNoSearchTerm.story = {
name: 'Compose: some groups, no search term',
};
export function ComposeSomeGroupsWithSearchTerm(): JSX.Element {
return (
<LeftPaneInContainer
@@ -782,10 +683,6 @@ export function ComposeSomeGroupsWithSearchTerm(): JSX.Element {
);
}
ComposeSomeGroupsWithSearchTerm.story = {
name: 'Compose: some groups, with search term',
};
export function ComposeSearchIsValidUsername(): JSX.Element {
return (
<LeftPaneInContainer
@@ -804,10 +701,6 @@ export function ComposeSearchIsValidUsername(): JSX.Element {
);
}
ComposeSearchIsValidUsername.story = {
name: 'Compose: search is valid username',
};
export function ComposeSearchIsValidUsernameFetchingUsername(): JSX.Element {
return (
<LeftPaneInContainer
@@ -828,10 +721,6 @@ export function ComposeSearchIsValidUsernameFetchingUsername(): JSX.Element {
);
}
ComposeSearchIsValidUsernameFetchingUsername.story = {
name: 'Compose: search is valid username, fetching username',
};
export function ComposeSearchIsValidUsernameButFlagIsNotEnabled(): JSX.Element {
return (
<LeftPaneInContainer
@@ -850,10 +739,6 @@ export function ComposeSearchIsValidUsernameButFlagIsNotEnabled(): JSX.Element {
);
}
ComposeSearchIsValidUsernameButFlagIsNotEnabled.story = {
name: 'Compose: search is valid username, but flag is not enabled',
};
export function ComposeSearchIsPartialPhoneNumber(): JSX.Element {
return (
<LeftPaneInContainer
@@ -872,10 +757,6 @@ export function ComposeSearchIsPartialPhoneNumber(): JSX.Element {
);
}
ComposeSearchIsPartialPhoneNumber.story = {
name: 'Compose: search is partial phone number',
};
export function ComposeSearchIsValidPhoneNumber(): JSX.Element {
return (
<LeftPaneInContainer
@@ -894,10 +775,6 @@ export function ComposeSearchIsValidPhoneNumber(): JSX.Element {
);
}
ComposeSearchIsValidPhoneNumber.story = {
name: 'Compose: search is valid phone number',
};
export function ComposeSearchIsValidPhoneNumberFetchingPhoneNumber(): JSX.Element {
return (
<LeftPaneInContainer
@@ -918,10 +795,6 @@ export function ComposeSearchIsValidPhoneNumberFetchingPhoneNumber(): JSX.Elemen
);
}
ComposeSearchIsValidPhoneNumberFetchingPhoneNumber.story = {
name: 'Compose: search is valid phone number, fetching phone number',
};
export function ComposeAllKindsOfResultsNoSearchTerm(): JSX.Element {
return (
<LeftPaneInContainer
@@ -940,10 +813,6 @@ export function ComposeAllKindsOfResultsNoSearchTerm(): JSX.Element {
);
}
ComposeAllKindsOfResultsNoSearchTerm.story = {
name: 'Compose: all kinds of results, no search term',
};
export function ComposeAllKindsOfResultsWithASearchTerm(): JSX.Element {
return (
<LeftPaneInContainer
@@ -962,10 +831,6 @@ export function ComposeAllKindsOfResultsWithASearchTerm(): JSX.Element {
);
}
ComposeAllKindsOfResultsWithASearchTerm.story = {
name: 'Compose: all kinds of results, with a search term',
};
export function CaptchaDialogRequired(): JSX.Element {
return (
<LeftPaneInContainer
@@ -985,10 +850,6 @@ export function CaptchaDialogRequired(): JSX.Element {
);
}
CaptchaDialogRequired.story = {
name: 'Captcha dialog: required',
};
export function CaptchaDialogPending(): JSX.Element {
return (
<LeftPaneInContainer
@@ -1008,30 +869,24 @@ export function CaptchaDialogPending(): JSX.Element {
);
}
CaptchaDialogPending.story = {
name: 'Captcha dialog: pending',
};
export const _CrashReportDialog = (): JSX.Element => (
<LeftPaneInContainer
{...useProps({
modeSpecificProps: {
...defaultSearchProps,
mode: LeftPaneMode.Inbox,
pinnedConversations,
conversations: defaultConversations,
archivedConversations: [],
isAboutToSearch: false,
searchTerm: '',
},
crashReportCount: 42,
})}
/>
);
_CrashReportDialog.story = {
name: 'Crash report dialog',
};
export function _CrashReportDialog(): JSX.Element {
return (
<LeftPaneInContainer
{...useProps({
modeSpecificProps: {
...defaultSearchProps,
mode: LeftPaneMode.Inbox,
pinnedConversations,
conversations: defaultConversations,
archivedConversations: [],
isAboutToSearch: false,
searchTerm: '',
},
crashReportCount: 42,
})}
/>
);
}
export function ChooseGroupMembersPartialPhoneNumber(): JSX.Element {
return (
@@ -1055,10 +910,6 @@ export function ChooseGroupMembersPartialPhoneNumber(): JSX.Element {
);
}
ChooseGroupMembersPartialPhoneNumber.story = {
name: 'Choose Group Members: Partial phone number',
};
export function ChooseGroupMembersValidPhoneNumber(): JSX.Element {
return (
<LeftPaneInContainer
@@ -1081,10 +932,6 @@ export function ChooseGroupMembersValidPhoneNumber(): JSX.Element {
);
}
ChooseGroupMembersValidPhoneNumber.story = {
name: 'Choose Group Members: Valid phone number',
};
export function ChooseGroupMembersUsername(): JSX.Element {
return (
<LeftPaneInContainer
@@ -1107,10 +954,6 @@ export function ChooseGroupMembersUsername(): JSX.Element {
);
}
ChooseGroupMembersUsername.story = {
name: 'Choose Group Members: username',
};
export function GroupMetadataNoTimer(): JSX.Element {
return (
<LeftPaneInContainer
@@ -1131,10 +974,6 @@ export function GroupMetadataNoTimer(): JSX.Element {
);
}
GroupMetadataNoTimer.story = {
name: 'Group Metadata: No Timer',
};
export function GroupMetadataRegularTimer(): JSX.Element {
return (
<LeftPaneInContainer
@@ -1155,10 +994,6 @@ export function GroupMetadataRegularTimer(): JSX.Element {
);
}
GroupMetadataRegularTimer.story = {
name: 'Group Metadata: Regular Timer',
};
export function GroupMetadataCustomTimer(): JSX.Element {
return (
<LeftPaneInContainer
@@ -1179,10 +1014,6 @@ export function GroupMetadataCustomTimer(): JSX.Element {
);
}
GroupMetadataCustomTimer.story = {
name: 'Group Metadata: Custom Timer',
};
export function SearchingConversation(): JSX.Element {
return (
<LeftPaneInContainer

View File

@@ -2,6 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { Meta } from '@storybook/react';
import type { PropsType } from './LeftPaneDialog';
import { LeftPaneDialog } from './LeftPaneDialog';
import { WidthBreakpoint } from './_util';
@@ -11,8 +13,7 @@ const widths = {
[WidthBreakpoint.Narrow]: '100px',
};
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function WidthDecorator(Story: any, context: any): JSX.Element {
return (
<div
@@ -32,46 +33,46 @@ export default {
argTypes: {
type: {
options: [undefined, 'warning', 'error'],
type: 'select',
control: { type: 'select' },
},
icon: {
options: [undefined, 'update', 'relink', 'network', 'warning'],
type: 'select',
control: { type: 'select' },
},
title: {
control: 'text',
control: { type: 'text' },
},
subtitle: {
control: 'text',
control: { type: 'text' },
},
hoverText: {
control: 'text',
defaultValue:
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.',
control: { type: 'text' },
},
hasXButton: {
control: 'boolean',
control: { type: 'boolean' },
},
hasAction: {
control: 'boolean',
control: { type: 'boolean' },
},
containerWidthBreakpoint: {
options: Object.keys(WidthBreakpoint),
mapping: WidthBreakpoint,
type: 'select',
control: { type: 'select' },
},
},
args: {
title: 'Lorem ipsum dolor sit amet',
subtitle:
'Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
hoverText:
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.',
hasXButton: true,
hasAction: true,
onClick: (): null => null,
clickLabel: 'Click me',
containerWidthBreakpoint: WidthBreakpoint.Wide,
},
};
} satisfies Meta<PropsType>;
export const Update = {
args: {

View File

@@ -2,11 +2,9 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useState } from 'react';
import { action } from '@storybook/addon-actions';
import { number } from '@storybook/addon-knobs';
import { noop } from 'lodash';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './Lightbox';
import { Lightbox } from './Lightbox';
@@ -26,7 +24,9 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/Lightbox',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
type OverridePropsMediaItemType = Partial<MediaItemType> & { caption?: string };
@@ -57,9 +57,7 @@ function createMediaItem(
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [selectedIndex, setSelectedIndex] = useState(
number('selectedIndex', overrideProps.selectedIndex || 0)
);
const [selectedIndex, setSelectedIndex] = useState(0);
const media = overrideProps.media || [];
return {
closeLightbox: action('closeLightbox'),
@@ -196,10 +194,6 @@ export function ImageWithCaptionNormalImage(): JSX.Element {
);
}
ImageWithCaptionNormalImage.story = {
name: 'Image with Caption (normal image)',
};
export function ImageWithCaptionAllWhiteImage(): JSX.Element {
return (
<Lightbox
@@ -216,10 +210,6 @@ export function ImageWithCaptionAllWhiteImage(): JSX.Element {
);
}
ImageWithCaptionAllWhiteImage.story = {
name: 'Image with Caption (all-white image)',
};
export function SingleVideo(): JSX.Element {
return (
<Lightbox
@@ -252,10 +242,6 @@ export function SingleVideoWCaption(): JSX.Element {
);
}
SingleVideoWCaption.story = {
name: 'Single Video w/caption',
};
export function UnsupportedImageType(): JSX.Element {
return (
<Lightbox
@@ -318,10 +304,6 @@ export function CustomChildren(): JSX.Element {
);
}
CustomChildren.story = {
name: 'Custom children',
};
export function ConversationHeader(): JSX.Element {
return (
<Lightbox

View File

@@ -17,6 +17,7 @@ import type { LocalizerType } from '../types/Util';
import type { MediaItemType, MediaItemMessageType } from '../types/MediaItem';
import * as GoogleChrome from '../util/GoogleChrome';
import * as log from '../logging/log';
import * as Errors from '../types/errors';
import { Avatar, AvatarSize } from './Avatar';
import { IMAGE_PNG, isImage, isVideo } from '../types/MIME';
import { formatDateTimeForAttachment } from '../util/timestamp';
@@ -230,7 +231,9 @@ export function Lightbox({
if (videoElement.paused) {
onMediaPlaybackStart();
void videoElement.play();
void videoElement.play().catch(error => {
log.error('Lightbox: Failed to play video', Errors.toLogFormat(error));
});
} else {
videoElement.pause();
}

View File

@@ -1,7 +1,7 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Story } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { ListTile } from './ListTile';
import type { Props } from './ListTile';
@@ -11,12 +11,12 @@ import { UserText } from './UserText';
export default {
title: 'Components/ListTile',
component: ListTile,
};
} satisfies Meta<Props>;
const lorem =
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam feugiat quam vitae semper facilisis. Praesent eu efficitur dui. Donec semper mattis nisl non hendrerit.';
function TemplateList(width: number): Story<Props> {
function TemplateList(width: number): StoryFn<Props> {
// eslint-disable-next-line react/display-name
return args => {
return (

View File

@@ -1,9 +1,10 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Meta, Story } from '@storybook/react';
import type { Meta, StoryFn } from '@storybook/react';
import React from 'react';
import { action } from '@storybook/addon-actions';
import type { PropsType } from './MediaEditor';
import { MediaEditor } from './MediaEditor';
import enMessages from '../../_locales/en/messages.json';
@@ -19,48 +20,28 @@ const IMAGE_4 = '/fixtures/snow.jpg';
export default {
title: 'Components/MediaEditor',
component: MediaEditor,
argTypes: {
getPreferredBadge: { action: true },
i18n: {
defaultValue: i18n,
},
imageToBlurHash: { action: true },
imageSrc: {
defaultValue: IMAGE_2,
},
installedPacks: {
defaultValue: installedPacks,
},
isFormattingEnabled: {
defaultValue: true,
},
isFormattingFlagEnabled: {
defaultValue: true,
},
isFormattingSpoilersFlagEnabled: {
defaultValue: true,
},
isSending: {
defaultValue: false,
},
onClose: { action: true },
onDone: { action: true },
onPickEmoji: { action: true },
onTextTooLong: { action: true },
platform: {
defaultValue: 'darwin',
},
recentStickers: {
defaultValue: [Stickers.wide, Stickers.tall, Stickers.abe],
},
skinTone: {
defaultValue: 0,
},
args: {
getPreferredBadge: () => undefined,
i18n,
imageToBlurHash: input => Promise.resolve(input.toString()),
imageSrc: IMAGE_2,
installedPacks,
isFormattingEnabled: true,
isFormattingFlagEnabled: true,
isFormattingSpoilersFlagEnabled: true,
isSending: false,
onClose: action('onClose'),
onDone: action('onDone'),
onPickEmoji: action('onPickEmoji'),
onTextTooLong: action('onTextTooLong'),
platform: 'darwin',
recentStickers: [Stickers.wide, Stickers.tall, Stickers.abe],
skinTone: 0,
},
} as Meta;
} satisfies Meta<PropsType>;
// eslint-disable-next-line react/function-component-definition
const Template: Story<PropsType> = args => <MediaEditor {...args} />;
const Template: StoryFn<PropsType> = args => <MediaEditor {...args} />;
export const ExtraLarge = Template.bind({});

View File

@@ -2,10 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { action } from '@storybook/addon-actions';
import { boolean } from '@storybook/addon-knobs';
import type { Meta } from '@storybook/react';
import enMessages from '../../_locales/en/messages.json';
import type { PropsType } from './MediaQualitySelector';
import { MediaQualitySelector } from './MediaQualitySelector';
@@ -13,14 +11,16 @@ import { setupI18n } from '../util/setupI18n';
export default {
title: 'Components/MediaQualitySelector',
};
argTypes: {},
args: {},
} satisfies Meta<PropsType>;
const i18n = setupI18n('en', enMessages);
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
conversationId: 'abc123',
i18n,
isHighQuality: boolean('isHighQuality', Boolean(overrideProps.isHighQuality)),
isHighQuality: overrideProps.isHighQuality ?? false,
onSelectQuality: action('onSelectQuality'),
});

View File

@@ -2,6 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useEffect, useState } from 'react';
import type { Meta } from '@storybook/react';
import type { Props } from './MiniPlayer';
import { MiniPlayer, PlayerState } from './MiniPlayer';
import { setupI18n } from '../util/setupI18n';
@@ -15,7 +17,7 @@ audio.src = '/fixtures/incompetech-com-Agnus-Dei-X.mp3';
export default {
title: 'components/MiniPlayer',
component: MiniPlayer,
};
} satisfies Meta<Props>;
export function Default(): JSX.Element {
const [active, setActive] = useState(false);

View File

@@ -3,19 +3,19 @@
import React from 'react';
import { noop } from 'lodash';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
import { Button } from './Button';
import type { ModalPropsType } from './Modal';
import { Modal } from './Modal';
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/Modal',
};
} satisfies Meta<ModalPropsType>;
const onClose = action('onClose');
@@ -30,10 +30,6 @@ export function BareBonesShort(): JSX.Element {
);
}
BareBonesShort.story = {
name: 'Bare bones, short',
};
export function BareBonesLong(): JSX.Element {
return (
<Modal modalName="test" i18n={i18n} useFocusTrap={false}>
@@ -45,10 +41,6 @@ export function BareBonesLong(): JSX.Element {
);
}
BareBonesLong.story = {
name: 'Bare bones, long',
};
export function BareBonesLongWithButton(): JSX.Element {
return (
<Modal
@@ -64,10 +56,6 @@ export function BareBonesLongWithButton(): JSX.Element {
);
}
BareBonesLongWithButton.story = {
name: 'Bare bones, long, with button',
};
export function TitleXButtonBodyAndButtonFooter(): JSX.Element {
return (
<Modal
@@ -83,10 +71,6 @@ export function TitleXButtonBodyAndButtonFooter(): JSX.Element {
);
}
TitleXButtonBodyAndButtonFooter.story = {
name: 'Title, X button, body, and button footer',
};
export function LotsOfButtonsInTheFooter(): JSX.Element {
return (
<Modal
@@ -114,10 +98,6 @@ export function LotsOfButtonsInTheFooter(): JSX.Element {
);
}
LotsOfButtonsInTheFooter.story = {
name: 'Lots of buttons in the footer',
};
export function LongBodyWithTitle(): JSX.Element {
return (
<Modal
@@ -135,10 +115,6 @@ export function LongBodyWithTitle(): JSX.Element {
);
}
LongBodyWithTitle.story = {
name: 'Long body with title',
};
export function LongBodyWithTitleAndButton(): JSX.Element {
return (
<Modal
@@ -156,10 +132,6 @@ export function LongBodyWithTitleAndButton(): JSX.Element {
);
}
LongBodyWithTitleAndButton.story = {
name: 'Long body with title and button',
};
export function LongBodyWithLongTitleAndXButton(): JSX.Element {
return (
<Modal
@@ -177,10 +149,6 @@ export function LongBodyWithLongTitleAndXButton(): JSX.Element {
);
}
LongBodyWithLongTitleAndXButton.story = {
name: 'Long body with long title and X button',
};
export function WithStickyButtonsLongBody(): JSX.Element {
return (
<Modal
@@ -203,10 +171,6 @@ export function WithStickyButtonsLongBody(): JSX.Element {
);
}
WithStickyButtonsLongBody.story = {
name: 'With sticky buttons long body',
};
export function WithStickyButtonsShortBody(): JSX.Element {
return (
<Modal
@@ -226,10 +190,6 @@ export function WithStickyButtonsShortBody(): JSX.Element {
);
}
WithStickyButtonsShortBody.story = {
name: 'With sticky buttons short body',
};
export function StickyFooterLotsOfButtons(): JSX.Element {
return (
<Modal
@@ -258,10 +218,6 @@ export function StickyFooterLotsOfButtons(): JSX.Element {
);
}
StickyFooterLotsOfButtons.story = {
name: 'Sticky footer, Lots of buttons',
};
export function WithBackButton(): JSX.Element {
return (
<Modal
@@ -276,7 +232,3 @@ export function WithBackButton(): JSX.Element {
</Modal>
);
}
WithBackButton.story = {
name: 'Back Button',
};

View File

@@ -1,13 +1,14 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Meta, ReactFramework, Story } from '@storybook/react';
import type { Meta, ReactRenderer, StoryFn } from '@storybook/react';
import type { PlayFunction } from '@storybook/csf';
import React from 'react';
import { expect } from '@storybook/jest';
import { expect, jest } from '@storybook/jest';
import { v4 as uuid } from 'uuid';
import { within, userEvent } from '@storybook/testing-library';
import { action } from '@storybook/addon-actions';
import type { PropsType } from './MyStories';
import enMessages from '../../_locales/en/messages.json';
import { MY_STORY_ID } from '../types/Stories';
@@ -24,50 +25,32 @@ export default {
title: 'Components/MyStories',
component: MyStories,
argTypes: {
i18n: {
defaultValue: i18n,
},
onBack: {
action: true,
},
onDelete: {
action: true,
},
onForward: {
action: true,
},
onSave: {
action: true,
},
ourConversationId: {
defaultValue: getDefaultConversation().id,
},
hasViewReceiptSetting: {
control: 'boolean',
defaultValue: false,
},
queueStoryDownload: {
action: true,
},
retryMessageSend: {
action: true,
},
viewStory: { action: true },
},
} as Meta;
args: {
i18n,
onBack: jest.fn(action('onBack')),
onDelete: action('onDelete'),
onForward: jest.fn(action('onForward')),
onSave: jest.fn(action('onSave')),
hasViewReceiptSetting: false,
queueStoryDownload: action('queueStoryDownload'),
retryMessageSend: action('retryMessageSend'),
viewStory: action('viewStory'),
},
} satisfies Meta<PropsType>;
// eslint-disable-next-line react/function-component-definition
const Template: Story<PropsType> = args => <MyStories {...args} />;
const Template: StoryFn<PropsType> = args => <MyStories {...args} />;
export const NoStories = Template.bind({});
NoStories.args = {
myStories: [],
};
NoStories.story = {
name: 'No Stories',
};
const interactionTest: PlayFunction<ReactFramework, PropsType> = async ({
const interactionTest: PlayFunction<ReactRenderer, PropsType> = async ({
args,
canvasElement,
}) => {
@@ -76,7 +59,7 @@ const interactionTest: PlayFunction<ReactFramework, PropsType> = async ({
await userEvent.click(btnDownload);
await expect(args.onSave).toHaveBeenCalled();
const [btnBack] = canvas.getAllByLabelText('Back');
const btnBack = canvas.getByText('Back');
await userEvent.click(btnBack);
await expect(args.onBack).toHaveBeenCalled();
@@ -94,9 +77,6 @@ SingleListStories.args = {
myStories: [getFakeMyStory(MY_STORY_ID)],
};
SingleListStories.play = interactionTest;
SingleListStories.story = {
name: 'One distribution list',
};
export const MultiListStories = Template.bind({});
MultiListStories.args = {
@@ -107,9 +87,6 @@ MultiListStories.args = {
],
};
MultiListStories.play = interactionTest;
MultiListStories.story = {
name: 'Multiple distribution lists',
};
export const FailedSentStory = Template.bind({});
{

View File

@@ -1,12 +1,13 @@
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Meta, ReactFramework, Story } from '@storybook/react';
import type { Meta, ReactRenderer, StoryFn } from '@storybook/react';
import type { PlayFunction } from '@storybook/csf';
import React from 'react';
import { expect } from '@storybook/jest';
import { expect, jest } from '@storybook/jest';
import { within, userEvent } from '@storybook/testing-library';
import { action } from '@storybook/addon-actions';
import type { PropsType } from './MyStoryButton';
import enMessages from '../../_locales/en/messages.json';
import { MyStoryButton } from './MyStoryButton';
@@ -21,27 +22,21 @@ const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/MyStoriesButton',
component: MyStoryButton,
argTypes: {
i18n: {
defaultValue: i18n,
},
me: {
defaultValue: getDefaultConversation(),
},
myStories: {
defaultValue: [getFakeMyStory()],
},
onAddStory: { action: true },
onClick: { action: true },
queueStoryDownload: { action: true },
showToast: { action: true },
args: {
i18n,
me: getDefaultConversation(),
myStories: [getFakeMyStory()],
onAddStory: jest.fn(action('onAddStory')),
onClick: jest.fn(action('onClick')),
queueStoryDownload: action('queueStoryDownload'),
showToast: action('showToast'),
},
} as Meta;
} satisfies Meta<PropsType>;
// eslint-disable-next-line react/function-component-definition
const Template: Story<PropsType> = args => <MyStoryButton {...args} />;
const Template: StoryFn<PropsType> = args => <MyStoryButton {...args} />;
const interactionTest: PlayFunction<ReactFramework, PropsType> = async ({
const interactionTest: PlayFunction<ReactRenderer, PropsType> = async ({
args,
canvasElement,
}) => {
@@ -51,40 +46,34 @@ const interactionTest: PlayFunction<ReactFramework, PropsType> = async ({
const textStory = canvas.getByText('Text story');
await userEvent.click(textStory);
await expect(args.onAddStory).toHaveBeenCalled();
const btnStory = canvas.getByText('My Stories');
await userEvent.click(btnStory);
await expect(args.onClick).toHaveBeenCalled();
if (args.myStories.length > 0) {
const btnStory = canvas.getByText('My Stories');
await userEvent.click(btnStory);
await expect(args.onClick).toHaveBeenCalled();
}
};
export const NoStory = Template.bind({});
NoStory.args = {
myStories: [],
};
NoStory.story = {
name: 'No Story',
};
NoStory.play = interactionTest;
export const OneStory = Template.bind({});
OneStory.args = {};
OneStory.story = {
name: 'One Story',
};
OneStory.play = interactionTest;
export const ManyStories = Template.bind({});
ManyStories.args = {
myStories: [getFakeMyStory(), getFakeMyStory()],
};
ManyStories.story = {
name: 'Many Stories',
};
ManyStories.play = interactionTest;
export const SendingStory = Template.bind({});
SendingStory.story = {
name: 'Sending Story',
};
{
const myStory = getFakeMyStory();
SendingStory.args = {
@@ -115,9 +104,7 @@ SendingStory.story = {
SendingStory.play = interactionTest;
export const FailedSendStory = Template.bind({});
FailedSendStory.story = {
name: 'Failed Send Story',
};
{
const myStory = getFakeMyStory();
FailedSendStory.args = {

View File

@@ -5,6 +5,8 @@ import React from 'react';
import { action } from '@storybook/addon-actions';
import type { Meta } from '@storybook/react';
import type { PropsType } from './NewlyCreatedGroupInvitedContactsDialog';
import { NewlyCreatedGroupInvitedContactsDialog } from './NewlyCreatedGroupInvitedContactsDialog';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
@@ -21,7 +23,7 @@ const conversations: Array<ConversationType> = [
export default {
title: 'Components/NewlyCreatedGroupInvitedContactsDialog',
};
} satisfies Meta<PropsType>;
export function OneContact(): JSX.Element {
return (
@@ -35,10 +37,6 @@ export function OneContact(): JSX.Element {
);
}
OneContact.story = {
name: 'One contact',
};
export function TwoContacts(): JSX.Element {
return (
<NewlyCreatedGroupInvitedContactsDialog
@@ -50,7 +48,3 @@ export function TwoContacts(): JSX.Element {
/>
);
}
TwoContacts.story = {
name: 'Two contacts',
};

View File

@@ -12,7 +12,7 @@ import { ContactName } from './conversation/ContactName';
import { GroupDialog } from './GroupDialog';
import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
type PropsType = {
export type PropsType = {
contacts: Array<ConversationType>;
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType;

Some files were not shown because too many files have changed in this diff Show More