signal-desktop/ts/components/conversationList/MessageSearchResult.stories.tsx
Jamie Kyle 502ea174ab
Upgrade Storybook
Co-authored-by: Scott Nonnenberg <scott@signal.org>
2023-10-11 12:06:43 -07:00

336 lines
8.6 KiB
TypeScript

// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as 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 { StorybookThemeContext } from '../../../.storybook/StorybookThemeContext';
import { strictAssert } from '../../util/assert';
import { getFakeBadge } from '../../test-both/helpers/getFakeBadge';
import type { PropsType } from './MessageSearchResult';
import { MessageSearchResult } from './MessageSearchResult';
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
import { BodyRange } from '../../types/BodyRange';
import { generateAci } from '../../types/ServiceId';
const SERVICE_ID_1 = generateAci();
const SERVICE_ID_2 = generateAci();
const SERVICE_ID_3 = generateAci();
const i18n = setupI18n('en', enMessages);
export default {
title: 'Components/MessageSearchResult',
} satisfies Meta<PropsType>;
const someone = getDefaultConversation({
title: 'Some Person',
name: 'Some Person',
phoneNumber: '(202) 555-0011',
});
const me = getDefaultConversation({
title: 'Me',
name: 'Me',
isMe: true,
});
const group = getDefaultConversation({
title: 'Group Chat',
name: 'Group Chat',
type: 'group',
});
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
i18n,
id: '',
conversationId: '',
sentAt: Date.now() - 24 * 60 * 1000,
snippet: overrideProps.snippet || "What's <<left>>going<<right>> on?",
body: overrideProps.body || "What's going on?",
bodyRanges: overrideProps.bodyRanges || [],
from: overrideProps.from as PropsType['from'],
to: overrideProps.to as PropsType['to'],
getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined),
isSelected: overrideProps.isSelected || false,
showConversation: action('showConversation'),
isSearchingInConversation: overrideProps.isSearchingInConversation || false,
theme: React.useContext(StorybookThemeContext),
});
export function Default(): JSX.Element {
const props = useProps({
from: someone,
to: me,
});
return <MessageSearchResult {...props} />;
}
export function SenderHasABadge(): JSX.Element {
const props = useProps({
from: { ...someone, badges: [{ id: 'sender badge' }] },
to: me,
getPreferredBadge: badges => {
strictAssert(
badges[0]?.id === 'sender badge',
'Rendering the wrong badge!'
);
return getFakeBadge();
},
});
return <MessageSearchResult {...props} />;
}
export function Selected(): JSX.Element {
const props = useProps({
from: someone,
to: me,
isSelected: true,
});
return <MessageSearchResult {...props} />;
}
export function FromYou(): JSX.Element {
const props = useProps({
from: me,
to: someone,
});
return <MessageSearchResult {...props} />;
}
export function SearchingInConversation(): JSX.Element {
const props = useProps({
from: me,
to: someone,
isSearchingInConversation: true,
});
return <MessageSearchResult {...props} />;
}
export function FromYouToYourself(): JSX.Element {
const props = useProps({
from: me,
to: me,
});
return <MessageSearchResult {...props} />;
}
export function FromYouToGroup(): JSX.Element {
const props = useProps({
from: me,
to: group,
});
return <MessageSearchResult {...props} />;
}
export function FromSomeoneToGroup(): JSX.Element {
const props = useProps({
from: someone,
to: group,
});
return <MessageSearchResult {...props} />;
}
export function LongSearchResult(): JSX.Element {
const snippets = [
'This is a really <<left>>detail<<right>>ed long line which will wrap and only be cut off after it gets to three lines. So maybe this will make it in as well?',
"Okay, here are the <<left>>detail<<right>>s:\n\n1355 Ridge Way\nCode: 234\n\nI'm excited!",
];
const props1 = useProps({
from: someone,
to: me,
snippet: snippets[0],
});
const props2 = useProps({
from: someone,
to: me,
snippet: snippets[1],
});
return (
<>
<MessageSearchResult {...props1} />
<MessageSearchResult {...props2} />
</>
);
}
export function EmptyShouldBeInvalid(): JSX.Element {
const props = useProps();
return <MessageSearchResult {...props} />;
}
export function Mention(): JSX.Element {
const props = useProps({
body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
bodyRanges: [
{
length: 1,
mentionAci: SERVICE_ID_3,
replacementText: 'Shoe',
conversationID: 'x',
start: 113,
},
{
length: 1,
mentionAci: SERVICE_ID_3,
replacementText: 'Shoe',
conversationID: 'x',
start: 237,
},
],
from: someone,
to: me,
snippet:
'<<truncation>>forget hair dry diary years no <<left>>results<<right>> \uFFFC <<left>>elephant<<right>> sorry umbrella potato igloo kangaroo home Georgia<<truncation>>',
});
return <MessageSearchResult {...props} />;
}
export function MentionRegexp(): JSX.Element {
const props = useProps({
body: '\uFFFC This is a (long) /text/ ^$ that is ... specially **crafted** to (test) our regexp escaping mechanism! Making sure that the code we write works in all sorts of scenarios',
bodyRanges: [
{
length: 1,
mentionAci: SERVICE_ID_3,
replacementText: 'RegExp',
conversationID: 'x',
start: 0,
},
],
from: someone,
to: me,
snippet:
'\uFFFC This is a (long) /text/ ^$ that is ... <<left>>specially<<right>> **crafted** to (test) our regexp escaping mechanism<<truncation>>',
});
return <MessageSearchResult {...props} />;
}
export function MentionNoMatches(): JSX.Element {
const props = useProps({
body: '\uFFFC hello',
bodyRanges: [
{
length: 1,
mentionAci: SERVICE_ID_3,
replacementText: 'Neo',
conversationID: 'x',
start: 0,
},
],
from: someone,
to: me,
snippet: '\uFFFC hello',
});
return <MessageSearchResult {...props} />;
}
export const _MentionNoMatches = (): JSX.Element => {
const props = useProps({
body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
bodyRanges: [
{
length: 1,
mentionAci: SERVICE_ID_3,
replacementText: 'Shoe',
conversationID: 'x',
start: 113,
},
{
length: 1,
mentionAci: SERVICE_ID_3,
replacementText: 'Shoe',
conversationID: 'x',
start: 237,
},
],
from: someone,
to: me,
snippet:
'<<truncation>>forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia<<truncation>>',
});
return <MessageSearchResult {...props} />;
};
export function DoubleMention(): JSX.Element {
const props = useProps({
body: 'Hey \uFFFC \uFFFC --- test! Two mentions!',
bodyRanges: [
{
length: 1,
mentionAci: SERVICE_ID_2,
replacementText: 'Alice',
conversationID: 'x',
start: 4,
},
{
length: 1,
mentionAci: SERVICE_ID_1,
replacementText: 'Bob',
conversationID: 'x',
start: 6,
},
],
from: someone,
to: me,
snippet: '<<left>>Hey<<right>> \uFFFC \uFFFC --- test! <<truncation>>',
});
return <MessageSearchResult {...props} />;
}
export function WithFormatting(): JSX.Element {
const props = useProps({
body: "We're playing with formatting in fun ways like you do!",
bodyRanges: [
{
// Overlaps just start
start: 0,
length: 19,
style: BodyRange.Style.BOLD,
},
{
// Contains snippet entirely
start: 0,
length: 54,
style: BodyRange.Style.ITALIC,
},
{
// Contained by snippet
start: 19,
length: 10,
style: BodyRange.Style.MONOSPACE,
},
{
// Overlaps just end
start: 29,
length: 25,
style: BodyRange.Style.STRIKETHROUGH,
},
],
from: someone,
to: me,
snippet:
'<<truncation>>playing with formatting in <<left>>fun<<right>> ways<<truncation>>',
});
return <MessageSearchResult {...props} />;
}