Migrate Timeline, TimelineLoadingRow to storybook

This commit is contained in:
Sidney Keese 2020-08-26 13:31:41 -07:00 committed by Josh Perez
parent 25dabd56fd
commit 1894ff0dc1
6 changed files with 396 additions and 388 deletions

View file

@ -1,358 +0,0 @@
## With oldest and newest
```jsx
window.itemLookup = {
'id-1': {
type: 'message',
data: {
id: 'id-1',
direction: 'incoming',
timestamp: Date.now(),
authorPhoneNumber: '(202) 555-2001',
authorColor: 'green',
text: '🔥',
},
},
'id-2': {
type: 'message',
data: {
id: 'id-2',
conversationType: 'group',
direction: 'incoming',
timestamp: Date.now(),
authorColor: 'green',
text: 'Hello there from the new world! http://somewhere.com',
},
},
'id-2.5': {
type: 'unsupportedMessage',
data: {
id: 'id-2.5',
canProcessNow: false,
contact: {
phoneNumber: '(202) 555-1000',
profileName: 'Mr. Pig',
},
},
},
'id-3': {
type: 'message',
data: {
id: 'id-3',
collapseMetadata: true,
direction: 'incoming',
timestamp: Date.now(),
authorColor: 'red',
text: 'Hello there from the new world!',
},
},
'id-4': {
type: 'timerNotification',
data: {
type: 'fromMe',
timespan: '5 minutes',
},
},
'id-5': {
type: 'timerNotification',
data: {
type: 'fromOther',
phoneNumber: '(202) 555-0000',
timespan: '1 hour',
},
},
'id-6': {
type: 'safetyNumberNotification',
data: {
contact: {
id: '+1202555000',
phoneNumber: '(202) 555-0000',
profileName: 'Mr. Fire',
},
},
},
'id-7': {
type: 'verificationNotification',
data: {
contact: {
phoneNumber: '(202) 555-0001',
name: 'Mrs. Ice',
},
isLocal: true,
type: 'markVerified',
},
},
'id-8': {
type: 'groupNotification',
data: {
changes: [
{
type: 'name',
newName: 'Squirrels and their uses',
},
{
type: 'add',
contacts: [
{
phoneNumber: '(202) 555-0002',
},
{
phoneNumber: '(202) 555-0003',
profileName: 'Ms. Water',
},
],
},
],
isMe: false,
},
},
'id-9': {
type: 'resetSessionNotification',
data: null,
},
'id-10': {
type: 'message',
data: {
id: 'id-6',
direction: 'outgoing',
timestamp: Date.now(),
status: 'sent',
authorColor: 'pink',
text: '🔥',
},
},
'id-11': {
type: 'message',
data: {
id: 'id-7',
direction: 'outgoing',
timestamp: Date.now(),
status: 'read',
authorColor: 'pink',
text: 'Hello there from the new world! http://somewhere.com',
},
},
'id-12': {
type: 'message',
data: {
id: 'id-8',
collapseMetadata: true,
direction: 'outgoing',
status: 'sent',
timestamp: Date.now(),
text: 'Hello there from the new world! 🔥',
},
},
'id-13': {
type: 'message',
data: {
id: 'id-9',
direction: 'outgoing',
status: 'sent',
timestamp: Date.now(),
authorColor: 'blue',
text:
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
},
},
'id-14': {
type: 'message',
data: {
id: 'id-10',
direction: 'outgoing',
status: 'read',
timestamp: Date.now(),
collapseMetadata: true,
text:
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
},
},
'id-15': {
type: 'linkNotification',
data: null,
},
};
window.actions = {
// For messages
downloadAttachment: options => console.log('onDownload', options),
replyToitem: id => console.log('onReply', id),
showMessageDetail: id => console.log('onShowDetail', id),
deleteMessage: id => console.log('onDelete', id),
downloadNewVersion: () => console.log('downloadNewVersion'),
// For Timeline
clearChangedMessages: (...args) => console.log('clearChangedMessages', args),
setLoadCountdownStart: (...args) =>
console.log('setLoadCountdownStart', args),
loadAndScroll: (...args) => console.log('loadAndScroll', args),
loadOlderMessages: (...args) => console.log('loadOlderMessages', args),
loadNewerMessages: (...args) => console.log('loadNewerMessages', args),
loadNewestMessages: (...args) => console.log('loadNewestMessages', args),
markMessageRead: (...args) => console.log('markMessageRead', args),
};
const props = {
id: 'conversationId-1',
haveNewest: true,
haveOldest: true,
isLoadingMessages: false,
items: util._.keys(window.itemLookup),
messagesHaveChanged: false,
oldestUnreadIndex: null,
resetCounter: 0,
scrollToIndex: null,
scrollToIndexCounter: 0,
totalUnread: 0,
renderItem: id => (
<TimelineItem item={window.itemLookup[id]} i18n={util.i18n} {...actions} />
),
};
<div style={{ height: '300px' }}>
<Timeline {...props} {...window.actions} i18n={util.i18n} />
</div>;
```
## With last seen indicator
```
const props = {
id: 'conversationId-1',
haveNewest: true,
haveOldest: true,
isLoadingMessages: false,
items: util._.keys(window.itemLookup),
messagesHaveChanged: false,
oldestUnreadIndex: 2,
resetCounter: 0,
scrollToIndex: null,
scrollToIndexCounter: 0,
totalUnread: 2,
renderItem: id => (
<TimelineItem item={window.itemLookup[id]} i18n={util.i18n} {...actions} />
),
renderLastSeenIndicator: () => (
<LastSeenIndicator count={2} i18n={util.i18n} />
),
};
<div style={{ height: '300px' }}>
<Timeline {...props} {...window.actions} i18n={util.i18n} />
</div>;
```
## With target index = 0
```
const props = {
id: 'conversationId-1',
haveNewest: true,
haveOldest: true,
isLoadingMessages: false,
items: util._.keys(window.itemLookup),
messagesHaveChanged: false,
oldestUnreadIndex: null,
resetCounter: 0,
scrollToIndex: 0,
scrollToIndexCounter: 0,
totalUnread: 0,
renderItem: id => (
<TimelineItem item={window.itemLookup[id]} i18n={util.i18n} {...actions} />
),
};
<div style={{ height: '300px' }}>
<Timeline {...props} {...window.actions} i18n={util.i18n} />
</div>;
```
## With typing indicator
```
const props = {
id: 'conversationId-1',
haveNewest: true,
haveOldest: true,
isLoadingMessages: false,
items: util._.keys(window.itemLookup),
messagesHaveChanged: false,
oldestUnreadIndex: null,
resetCounter: 0,
scrollToIndex: null,
scrollToIndexCounter: 0,
totalUnread: 0,
typingContact: true,
renderItem: id => (
<TimelineItem item={window.itemLookup[id]} i18n={util.i18n} {...actions} />
),
renderTypingBubble: () => (
<TypingBubble color="red" conversationType="direct" phoneNumber="+18005552222" i18n={util.i18n} />
),
};
<div style={{ height: '300px' }}>
<Timeline {...props} {...window.actions} i18n={util.i18n} />
</div>;
```
## Without newest message
```
const props = {
id: 'conversationId-1',
haveNewest: false,
haveOldest: true,
isLoadingMessages: false,
items: util._.keys(window.itemLookup),
messagesHaveChanged: false,
oldestUnreadIndex: null,
resetCounter: 0,
scrollToIndex: 3,
scrollToIndexCounter: 0,
totalUnread: 0,
renderItem: id => (
<TimelineItem item={window.itemLookup[id]} i18n={util.i18n} {...actions} />
),
};
<div style={{ height: '300px' }}>
<Timeline {...props} {...window.actions} i18n={util.i18n} />
</div>;
```
## Without oldest message
```
const props = {
id: 'conversationId-1',
haveNewest: true,
haveOldest: false,
isLoadingMessages: false,
items: util._.keys(window.itemLookup),
messagesHaveChanged: false,
oldestUnreadIndex: null,
resetCounter: 0,
scrollToIndex: null,
scrollToIndexCounter: 0,
totalUnread: 0,
renderItem: id => (
<TimelineItem item={window.itemLookup[id]} i18n={util.i18n} {...actions} />
),
renderLoadingRow: () => (
<TimelineLoadingRow state="idle" />
),
};
<div style={{ height: '300px' }}>
<Timeline {...props} {...window.actions} i18n={util.i18n} />
</div>;
```

View file

@ -0,0 +1,353 @@
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { boolean, number } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
// @ts-ignore
import { setup as setupI18n } from '../../../js/modules/i18n';
// @ts-ignore
import enMessages from '../../../_locales/en/messages.json';
import { Props, Timeline } from './Timeline';
import { TimelineItem, TimelineItemType } from './TimelineItem';
import { LastSeenIndicator } from './LastSeenIndicator';
import { TimelineLoadingRow } from './TimelineLoadingRow';
import { TypingBubble } from './TypingBubble';
const i18n = setupI18n('en', enMessages);
const story = storiesOf('Components/Conversation/Timeline', module);
// tslint:disable-next-line
const noop = () => {};
Object.assign(window, {
registerForActive: noop,
unregisterForActive: noop,
});
const items: Record<string, TimelineItemType> = {
'id-1': {
type: 'message',
data: {
id: 'id-1',
direction: 'incoming',
timestamp: Date.now(),
authorPhoneNumber: '(202) 555-2001',
authorColor: 'green',
text: '🔥',
},
},
'id-2': {
type: 'message',
data: {
id: 'id-2',
conversationType: 'group',
direction: 'incoming',
timestamp: Date.now(),
authorColor: 'green',
text: 'Hello there from the new world! http://somewhere.com',
},
},
'id-2.5': {
type: 'unsupportedMessage',
data: {
id: 'id-2.5',
canProcessNow: false,
contact: {
phoneNumber: '(202) 555-1000',
profileName: 'Mr. Pig',
title: 'Mr. Pig',
},
},
},
'id-3': {
type: 'message',
data: {
id: 'id-3',
collapseMetadata: true,
direction: 'incoming',
timestamp: Date.now(),
authorColor: 'red',
text: 'Hello there from the new world!',
},
},
'id-4': {
type: 'timerNotification',
data: {
type: 'fromMe',
timespan: '5 minutes',
},
},
'id-5': {
type: 'timerNotification',
data: {
type: 'fromOther',
title: '(202) 555-0000',
phoneNumber: '(202) 555-0000',
timespan: '1 hour',
},
},
'id-6': {
type: 'safetyNumberNotification',
data: {
contact: {
id: '+1202555000',
phoneNumber: '(202) 555-0000',
profileName: 'Mr. Fire',
},
},
},
'id-7': {
type: 'verificationNotification',
data: {
contact: {
phoneNumber: '(202) 555-0001',
name: 'Mrs. Ice',
},
isLocal: true,
type: 'markVerified',
},
},
'id-8': {
type: 'groupNotification',
data: {
changes: [
{
type: 'name',
newName: 'Squirrels and their uses',
},
{
type: 'add',
contacts: [
{
phoneNumber: '(202) 555-0002',
profileName: 'Mr. Fire',
title: 'Mr. Fire',
},
{
phoneNumber: '(202) 555-0003',
profileName: 'Ms. Water',
title: 'Ms. Water',
},
],
},
],
from: {
phoneNumber: '(202) 555-0001',
name: 'Mrs. Ice',
title: 'Mrs. Ice',
},
isMe: false,
},
},
'id-9': {
type: 'resetSessionNotification',
data: null,
},
'id-10': {
type: 'message',
data: {
id: 'id-6',
direction: 'outgoing',
timestamp: Date.now(),
status: 'sent',
authorColor: 'pink',
text: '🔥',
},
},
'id-11': {
type: 'message',
data: {
id: 'id-7',
direction: 'outgoing',
timestamp: Date.now(),
status: 'read',
authorColor: 'pink',
text: 'Hello there from the new world! http://somewhere.com',
},
},
'id-12': {
type: 'message',
data: {
id: 'id-8',
collapseMetadata: true,
direction: 'outgoing',
status: 'sent',
timestamp: Date.now(),
text: 'Hello there from the new world! 🔥',
},
},
'id-13': {
type: 'message',
data: {
id: 'id-9',
direction: 'outgoing',
status: 'sent',
timestamp: Date.now(),
authorColor: 'blue',
text:
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
},
},
'id-14': {
type: 'message',
data: {
id: 'id-10',
direction: 'outgoing',
status: 'read',
timestamp: Date.now(),
collapseMetadata: true,
text:
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
},
},
'id-15': {
type: 'linkNotification',
data: null,
},
} as any;
const actions = () => ({
clearChangedMessages: action('clearChangedMessages'),
setLoadCountdownStart: action('setLoadCountdownStart'),
setIsNearBottom: action('setIsNearBottom'),
loadAndScroll: action('loadAndScroll'),
loadOlderMessages: action('loadOlderMessages'),
loadNewerMessages: action('loadNewerMessages'),
loadNewestMessages: action('loadNewestMessages'),
markMessageRead: action('markMessageRead'),
selectMessage: action('selectMessage'),
clearSelectedMessage: action('clearSelectedMessage'),
updateSharedGroups: action('updateSharedGroups'),
reactToMessage: action('reactToMessage'),
replyToMessage: action('replyToMessage'),
retrySend: action('retrySend'),
deleteMessage: action('deleteMessage'),
showMessageDetail: action('showMessageDetail'),
openConversation: action('openConversation'),
showContactDetail: action('showContactDetail'),
showVisualAttachment: action('showVisualAttachment'),
downloadAttachment: action('downloadAttachment'),
displayTapToViewMessage: action('displayTapToViewMessage'),
openLink: action('openLink'),
scrollToQuotedMessage: action('scrollToQuotedMessage'),
showExpiredIncomingTapToViewToast: action(
'showExpiredIncomingTapToViewToast'
),
showExpiredOutgoingTapToViewToast: action(
'showExpiredOutgoingTapToViewToast'
),
showIdentity: action('showIdentity'),
downloadNewVersion: action('downloadNewVersion'),
});
const renderItem = (id: string) => (
<TimelineItem
id=""
isSelected={false}
renderEmojiPicker={() => <div />}
item={items[id]}
i18n={i18n}
conversationId=""
conversationAccepted
{...actions()}
/>
);
const renderLastSeenIndicator = () => (
<LastSeenIndicator count={2} i18n={i18n} />
);
const renderHeroRow = () => <div />;
const renderLoadingRow = () => <TimelineLoadingRow state="loading" />;
const renderTypingBubble = () => (
<TypingBubble
color="red"
conversationType="direct"
phoneNumber="+18005552222"
i18n={i18n}
title="title"
/>
);
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
i18n,
haveNewest: boolean('haveNewest', overrideProps.haveNewest !== false),
haveOldest: boolean('haveOldest', overrideProps.haveOldest !== false),
isLoadingMessages: false,
items: Object.keys(items),
resetCounter: 0,
scrollToIndex: overrideProps.scrollToIndex,
scrollToIndexCounter: 0,
totalUnread: number('totalUnread', overrideProps.totalUnread || 0),
oldestUnreadIndex:
number('oldestUnreadIndex', overrideProps.oldestUnreadIndex || 0) ||
undefined,
id: '',
renderItem,
renderLastSeenIndicator,
renderHeroRow,
renderLoadingRow,
renderTypingBubble,
typingContact: boolean(
'typingContact',
!!overrideProps.typingContact || false
),
...actions(),
});
story.add('Oldest and Newest', () => {
const props = createProps();
return <Timeline {...props} />;
});
story.add('Last Seen', () => {
const props = createProps({
oldestUnreadIndex: 13,
totalUnread: 2,
});
return <Timeline {...props} />;
});
story.add('Target Index to Top', () => {
const props = createProps({
scrollToIndex: 0,
});
return <Timeline {...props} />;
});
story.add('Typing Indicator', () => {
const props = createProps({
typingContact: true,
});
return <Timeline {...props} />;
});
story.add('Without Newest Message', () => {
const props = createProps({
haveNewest: false,
});
return <Timeline {...props} />;
});
story.add('Without Oldest Message', () => {
const props = createProps({
haveOldest: false,
scrollToIndex: -1,
});
return <Timeline {...props} />;
});

View file

@ -78,7 +78,7 @@ type PropsActionsType = {
} & MessageActionsType &
SafetyNumberActionsType;
type Props = PropsDataType & PropsHousekeepingType & PropsActionsType;
export type Props = PropsDataType & PropsHousekeepingType & PropsActionsType;
// from https://github.com/bvaughn/react-virtualized/blob/fb3484ed5dcc41bffae8eab029126c0fb8f7abc0/source/List/types.js#L5
type RowRendererParamsType = {

View file

@ -1,28 +0,0 @@
### Idle
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<TimelineLoadingRow state="idle" />
</util.ConversationContext>
```
### Countdown
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<TimelineLoadingRow
state="countdown"
duration={30000}
expiresAt={Date.now() + 20000}
onComplete={() => console.log('onComplete')}
/>
</util.ConversationContext>
```
### Loading
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<TimelineLoadingRow state="loading" />
</util.ConversationContext>
```

View file

@ -0,0 +1,41 @@
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { date, number, select } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import { Props, TimelineLoadingRow } from './TimelineLoadingRow';
const story = storiesOf('Components/Conversation/TimelineLoadingRow', module);
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
state: select(
'state',
{ idle: 'idle', countdown: 'countdown', loading: 'loading' },
overrideProps.state || 'idle'
),
duration: number('duration', overrideProps.duration || 0),
expiresAt: date('expiresAt', new Date(overrideProps.expiresAt || Date.now())),
onComplete: action('onComplete'),
});
story.add('Idle', () => {
const props = createProps();
return <TimelineLoadingRow {...props} />;
});
story.add('Countdown', () => {
const props = createProps({
state: 'countdown',
duration: 40000,
expiresAt: Date.now() + 20000,
});
return <TimelineLoadingRow {...props} />;
});
story.add('Loading', () => {
const props = createProps({ state: 'loading' });
return <TimelineLoadingRow {...props} />;
});

View file

@ -6,7 +6,7 @@ import { Spinner } from '../Spinner';
export type STATE_ENUM = 'idle' | 'countdown' | 'loading';
type Props = {
export type Props = {
state: STATE_ENUM;
duration?: number;
expiresAt?: number;