Migrate ConversationListItem to Storybook

This commit is contained in:
Sidney Keese 2020-08-26 11:03:30 -07:00 committed by Josh Perez
parent de72a2f6c6
commit db7aa6223f
3 changed files with 261 additions and 623 deletions

View file

@ -1,615 +0,0 @@
#### With name and profile
```jsx
<util.LeftPaneContext theme={util.theme}>
<ConversationListItem
id="conversationId1"
isAccepted
title="Someone 🔥 Somewhere"
name="Someone 🔥 Somewhere"
type={'direct'}
phoneNumber="(202) 555-0011"
avatarPath={util.gifObjectUrl}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: "What's going on?",
status: 'sent',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</util.LeftPaneContext>
```
#### Profile, with name, no avatar
```jsx
<util.LeftPaneContext theme={util.theme}>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
type={'direct'}
title="Mr. Fire🔥"
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Just a second',
status: 'read',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</util.LeftPaneContext>
```
#### Conversation with yourself
```jsx
<util.LeftPaneContext theme={util.theme}>
<ConversationListItem
id="conversationId1"
isAccepted
isMe={true}
phoneNumber="(202) 555-0011"
type={'direct'}
title="Mr. Fire🔥"
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Just a second',
status: 'read',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</util.LeftPaneContext>
```
#### All types of status
```jsx
<util.LeftPaneContext theme={util.theme}>
<div>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
type={'direct'}
title="Mr. Fire🔥"
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Sending',
status: 'sending',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId2"
isAccepted
phoneNumber="(202) 555-0011"
type={'direct'}
title="Mr. Fire🔥"
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Sent',
status: 'sent',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId3"
isAccepted
phoneNumber="(202) 555-0011"
type={'direct'}
title="Mr. Fire🔥"
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Delivered',
status: 'delivered',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId4"
isAccepted
phoneNumber="(202) 555-0011"
type={'direct'}
title="Mr. Fire🔥"
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Read',
status: 'read',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId5"
isAccepted
phoneNumber="(202) 555-0011"
type={'direct'}
title="Mr. Fire🔥"
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Error',
status: 'error',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId5"
phoneNumber="(202) 555-0011"
type={'direct'}
name="Mr. Fire🔥"
color="green"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Partially Sent',
status: 'partial-sent',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```
#### Is typing
```jsx
<util.LeftPaneContext theme={util.theme}>
<div>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
unreadCount={4}
lastUpdated={Date.now() - 5 * 60 * 1000}
typingContact={{
name: 'Someone Here',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
<div>
<ConversationListItem
id="conversationId2"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
unreadCount={4}
lastUpdated={Date.now() - 5 * 60 * 1000}
typingContact={{
name: 'Someone Here',
}}
lastMessage={{
status: 'read',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```
#### Message Request
```jsx
<util.LeftPaneContext theme={util.theme}>
<div>
<ConversationListItem
id="conversationId1"
isAccepted={false}
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
unreadCount={4}
lastUpdated={Date.now() - 5 * 60 * 1000}
typingContact={{
name: 'Someone Here',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
<div>
<ConversationListItem
id="conversationId2"
isAccepted={false}
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
unreadCount={4}
lastUpdated={Date.now() - 5 * 60 * 1000}
typingContact={{
name: 'Someone Here',
}}
lastMessage={{
status: 'read',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```
#### Selected
#### With unread
```jsx
<util.LeftPaneContext theme={util.theme}>
<div>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
unreadCount={4}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Hey there!',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId2"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
unreadCount={10}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Hey there!',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId3"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
unreadCount={250}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Hey there!',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```
#### Selected
```jsx
<util.LeftPaneContext theme={util.theme}>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
isSelected={true}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Hey there!',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</util.LeftPaneContext>
```
#### With emoji/links in message, no status
We don't want Jumbomoji or links.
```jsx
<util.LeftPaneContext theme={util.theme}>
<div>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Download at http://signal.org',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId2"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: '🔥',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```
#### Long content
We only show one line.
```jsx
<util.LeftPaneContext theme={util.theme}>
<div>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
name="Long contact name. Esquire. The third. And stuff. And more! And more!"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Normal message',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId2"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text:
"Long line. This is a really really really long line. Really really long. Because that's just how it is",
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId3"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text:
"Long line. This is a really really really long line. Really really long. Because that's just how it is",
status: 'read',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId4"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
unreadCount={8}
lastMessage={{
text:
"Long line. This is a really really really long line. Really really long. Because that's just how it is",
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId5"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text:
"Many lines. This is a many-line message.\nLine 2 is really exciting but it shouldn't be seen.\nLine three is even better.\nLine 4, well.",
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId6"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text:
"Many lines. This is a many-line message.\nLine 2 is really exciting but it shouldn't be seen.\nLine three is even better.\nLine 4, well.",
status: 'delivered',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```
#### More narrow
On platforms that show scrollbars all the time, this is true all the time.
```jsx
<util.LeftPaneContext theme={util.theme}>
<div style={{ width: '280px' }}>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
name="Long contact name. Esquire. The third. And stuff. And more! And more!"
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: 'Normal message',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId2"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text:
"Long line. This is a really really really long line. Really really long. Because that's just how it is",
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```
#### With various ages
```jsx
<util.LeftPaneContext theme={util.theme}>
<div>
<ConversationListItem
id="conversationId1"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 60 * 1000}
lastMessage={{
text: 'Five hours ago',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId2"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 24 * 60 * 60 * 1000}
lastMessage={{
text: 'One day ago',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId3"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 7 * 24 * 60 * 60 * 1000}
lastMessage={{
text: 'One week ago',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId4"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 365 * 24 * 60 * 60 * 1000}
lastMessage={{
text: 'One year ago',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```
#### Missing data
```jsx
<util.LeftPaneContext theme={util.theme}>
<div>
<ConversationListItem
id="conversationId1"
isAccepted
name="John"
title="John"
type={'direct'}
lastUpdated={null}
lastMessage={{
text: 'Missing last updated',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId2"
isAccepted
name="Missing message"
title="Missing message"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: null,
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
<ConversationListItem
id="conversationId3"
isAccepted
phoneNumber="(202) 555-0011"
title="(202) 555-0011"
type={'direct'}
lastUpdated={Date.now() - 5 * 60 * 1000}
lastMessage={{
text: null,
status: 'sent',
}}
onClick={result => console.log('onClick', result)}
i18n={util.i18n}
/>
</div>
</util.LeftPaneContext>
```

View file

@ -0,0 +1,248 @@
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import {
ConversationListItem,
MessageStatuses,
Props,
} from './ConversationListItem';
// tslint:disable-next-line
import 'draft-js/dist/Draft.css';
// @ts-ignore
import { setup as setupI18n } from '../../js/modules/i18n';
// @ts-ignore
import enMessages from '../../_locales/en/messages.json';
import { action } from '@storybook/addon-actions';
import { boolean, date, select, text } from '@storybook/addon-knobs';
const i18n = setupI18n('en', enMessages);
const story = storiesOf('Components/ConversationListItem', module);
story.addDecorator(storyFn => (
<div style={{ width: '300px' }}>{storyFn()}</div>
));
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
...overrideProps,
i18n,
isAccepted: boolean(
'isAccepted',
overrideProps.isAccepted !== undefined ? overrideProps.isAccepted : true
),
isMe: boolean('isMe', overrideProps.isMe || false),
avatarPath: text('avatarPath', overrideProps.avatarPath || ''),
id: overrideProps.id || '',
isSelected: boolean('isSelected', overrideProps.isSelected || false),
title: text('title', overrideProps.title || 'Some Person'),
name: overrideProps.name || 'Some Person',
type: overrideProps.type || 'direct',
onClick: action('onClick'),
lastMessage: overrideProps.lastMessage || {
text: text('lastMessage.text', 'Hi there!'),
status: select(
'status',
MessageStatuses.reduce((m, s) => ({ ...m, [s]: s }), {}),
'read'
),
},
lastUpdated: date(
'lastUpdated',
new Date(overrideProps.lastUpdated || Date.now() - 5 * 60 * 1000)
),
});
story.add('Name', () => {
const props = createProps();
return <ConversationListItem {...props} />;
});
story.add('Name and Avatar', () => {
const props = createProps({
avatarPath: '/fixtures/kitten-1-64-64.jpg',
});
return <ConversationListItem {...props} />;
});
story.add('Conversation with Yourself', () => {
const props = createProps({
lastMessage: {
text: 'Just a second',
status: 'read',
},
name: 'Myself',
title: 'Myself',
isMe: true,
});
return <ConversationListItem {...props} />;
});
story.add('Message Statuses', () => {
return MessageStatuses.map(status => {
const props = createProps({
lastMessage: {
text: status,
status,
},
});
return <ConversationListItem key={status} {...props} />;
});
});
story.add('Typing Status', () => {
const props = createProps({
typingContact: {
name: 'Someone Here',
},
});
return <ConversationListItem {...props} />;
});
story.add('Message Request', () => {
const props = createProps({
isAccepted: false,
lastMessage: {
text: 'A Message',
status: 'delivered',
},
});
return <ConversationListItem {...props} />;
});
story.add('Unread', () => {
const counts = [4, 10, 250];
return counts.map(unreadCount => {
const props = createProps({
lastMessage: {
text: 'Hey there!',
status: 'delivered',
},
unreadCount,
});
return <ConversationListItem key={unreadCount} {...props} />;
});
});
story.add('Selected', () => {
const props = createProps({
lastMessage: {
text: 'Hey there!',
status: 'read',
},
isSelected: true,
});
return <ConversationListItem {...props} />;
});
story.add('Emoji in Message', () => {
const props = createProps({
lastMessage: {
text: '🔥',
status: 'read',
},
});
return <ConversationListItem {...props} />;
});
story.add('Link in Message', () => {
const props = createProps({
lastMessage: {
text: 'Download at http://signal.org',
status: 'read',
},
});
return <ConversationListItem {...props} />;
});
story.add('Long Name', () => {
const name =
'Long contact name. Esquire. The third. And stuff. And more! And more!';
const props = createProps({
name,
title: name,
});
return <ConversationListItem {...props} />;
});
story.add('Long Message', () => {
const messages = [
"Long line. This is a really really really long line. Really really long. Because that's just how it is",
`Many lines. This is a many-line message.
Line 2 is really exciting but it shouldn't be seen.
Line three is even better.
Line 4, well.`,
];
return messages.map(message => {
const props = createProps({
name,
lastMessage: {
text: message,
status: 'read',
},
});
return <ConversationListItem key={message.length} {...props} />;
});
});
story.add('Various Times', () => {
const times: Array<[number, string]> = [
[Date.now() - 5 * 60 * 60 * 1000, 'Five hours ago'],
[Date.now() - 24 * 60 * 60 * 1000, 'One day ago'],
[Date.now() - 7 * 24 * 60 * 60 * 1000, 'One week ago'],
[Date.now() - 365 * 24 * 60 * 60 * 1000, 'One year ago'],
];
return times.map(([lastUpdated, messageText]) => {
const props = createProps({
name,
lastUpdated,
lastMessage: {
text: messageText,
status: 'read',
},
});
return <ConversationListItem key={lastUpdated} {...props} />;
});
});
story.add('Missing Date', () => {
const props = createProps();
return <ConversationListItem {...props} lastUpdated={undefined as any} />;
});
story.add('Missing Message', () => {
const props = createProps();
return <ConversationListItem {...props} lastMessage={undefined as any} />;
});
story.add('Missing Text', () => {
const props = createProps();
return (
<ConversationListItem
{...props}
lastMessage={{ text: undefined as any, status: 'sent' }}
/>
);
});

View file

@ -12,6 +12,17 @@ import { cleanId } from './_util';
import { LocalizerType } from '../types/Util';
import { ColorType } from '../types/Colors';
export const MessageStatuses = [
'sending',
'sent',
'delivered',
'read',
'error',
'partial-sent',
] as const;
export type MessageStatusType = typeof MessageStatuses[number];
export type PropsData = {
id: string;
phoneNumber?: string;
@ -33,13 +44,7 @@ export type PropsData = {
typingContact?: Object;
lastMessage?: {
status:
| 'sending'
| 'sent'
| 'delivered'
| 'read'
| 'error'
| 'partial-sent';
status: MessageStatusType;
text: string;
deletedForEveryone?: boolean;
};
@ -51,7 +56,7 @@ type PropsHousekeeping = {
onClick?: (id: string) => void;
};
type Props = PropsData & PropsHousekeeping;
export type Props = PropsData & PropsHousekeeping;
export class ConversationListItem extends React.PureComponent<Props> {
public renderAvatar() {