From bcd4f6407f456f31cc1dcaa8a4bf1c8b3bde4ec4 Mon Sep 17 00:00:00 2001 From: Chris Svenningsen Date: Thu, 27 Aug 2020 11:10:35 -0700 Subject: [PATCH] Migrate MessageDetail to Storybook --- ts/components/conversation/Message.tsx | 20 +- ts/components/conversation/MessageDetail.md | 166 -------------- .../conversation/MessageDetail.stories.tsx | 215 ++++++++++++++++++ ts/components/conversation/MessageDetail.tsx | 6 +- ts/util/lint/exceptions.json | 8 +- 5 files changed, 237 insertions(+), 178 deletions(-) delete mode 100644 ts/components/conversation/MessageDetail.md create mode 100644 ts/components/conversation/MessageDetail.stories.tsx diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index be1947d4ae..05ea743e91 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -61,7 +61,7 @@ interface LinkPreviewType { image?: AttachmentType; } -export const Statuses = [ +export const MessageStatuses = [ 'delivered', 'error', 'partial-sent', @@ -69,6 +69,16 @@ export const Statuses = [ 'sending', 'sent', ] as const; +export type MessageStatusType = typeof MessageStatuses[number]; + +export const InteractionModes = ['mouse', 'keyboard'] as const; +export type InteractionModeType = typeof InteractionModes[number]; + +export const Directions = ['incoming', 'outgoing'] as const; +export type DirectionType = typeof Directions[number]; + +export const ConversationTypes = ['direct', 'group'] as const; +export type ConversationTypesType = typeof ConversationTypes[number]; export type PropsData = { id: string; @@ -78,17 +88,17 @@ export type PropsData = { isSticker?: boolean; isSelected?: boolean; isSelectedCounter?: number; - interactionMode: 'mouse' | 'keyboard'; - direction: 'incoming' | 'outgoing'; + interactionMode: InteractionModeType; + direction: DirectionType; timestamp: number; - status?: typeof Statuses[number]; + status?: MessageStatusType; contact?: ContactType; authorTitle: string; authorName?: string; authorProfileName?: string; authorPhoneNumber?: string; authorColor?: ColorType; - conversationType: 'group' | 'direct'; + conversationType: ConversationTypesType; attachments?: Array; quote?: { text: string; diff --git a/ts/components/conversation/MessageDetail.md b/ts/components/conversation/MessageDetail.md deleted file mode 100644 index d6ea7c287d..0000000000 --- a/ts/components/conversation/MessageDetail.md +++ /dev/null @@ -1,166 +0,0 @@ -### Incoming message - -```jsx - console.log('onDelete'), - }} - sentAt={Date.now() - 2 * 60 * 1000} - receivedAt={Date.now() - 10 * 1000} - contacts={[ - { - phoneNumber: '(202) 555-1001', - avatarPath: util.gifObjectUrl, - }, - ]} - i18n={util.i18n} -/> -``` - -### Message to group, multiple contacts - -```jsx - console.log('onDelete'), - }} - sentAt={Date.now()} - contacts={[ - { - phoneNumber: '(202) 555-1001', - profileName: 'Mr. Fire', - avatarPath: util.gifObjectUrl, - status: 'sending', - }, - { - phoneNumber: '(202) 555-1002', - avatarPath: util.pngObjectUrl, - status: 'delivered', - }, - { - phoneNumber: '(202) 555-1003', - color: 'teal', - status: 'read', - }, - ]} - i18n={util.i18n} -/> -``` - -### 1:1 conversation, just one recipient - -```jsx - console.log('onDelete'), - }} - contacts={[ - { - phoneNumber: '(202) 555-1001', - avatarPath: util.gifObjectUrl, - status: 'sending', - }, - ]} - sentAt={Date.now()} - i18n={util.i18n} -/> -``` - -### Errors for some users, including on OutgoingKeyError - -```jsx - console.log('onDelete'), - }} - contacts={[ - { - phoneNumber: '(202) 555-1001', - avatarPath: util.gifObjectUrl, - status: 'error', - errors: [new Error('Something went wrong'), new Error('Bad things')], - }, - { - phoneNumber: '(202) 555-1002', - avatarPath: util.pngObjectUrl, - status: 'error', - isOutgoingKeyError: true, - errors: [new Error(util.i18n('newIdentity'))], - onShowSafetyNumber: () => console.log('onShowSafetyNumber'), - onSendAnyway: () => console.log('onSendAnyway'), - }, - { - phoneNumber: '(202) 555-1003', - color: 'teal', - status: 'read', - }, - ]} - sentAt={Date.now()} - i18n={util.i18n} -/> -``` - -### Unidentified Delivery - -```jsx - console.log('onDelete'), - }} - contacts={[ - { - phoneNumber: '(202) 555-1001', - avatarPath: util.gifObjectUrl, - status: 'read', - isUnidentifiedDelivery: true, - }, - { - phoneNumber: '(202) 555-1002', - avatarPath: util.pngObjectUrl, - status: 'delivered', - isUnidentifiedDelivery: true, - }, - { - phoneNumber: '(202) 555-1003', - color: 'teal', - status: 'read', - }, - ]} - sentAt={Date.now()} - i18n={util.i18n} -/> -``` diff --git a/ts/components/conversation/MessageDetail.stories.tsx b/ts/components/conversation/MessageDetail.stories.tsx new file mode 100644 index 0000000000..09098d7c15 --- /dev/null +++ b/ts/components/conversation/MessageDetail.stories.tsx @@ -0,0 +1,215 @@ +import * as React from 'react'; + +import { action } from '@storybook/addon-actions'; +import { number } from '@storybook/addon-knobs'; +import { storiesOf } from '@storybook/react'; + +import { Props as MessageProps } from './Message'; +import { MessageDetail, Props } from './MessageDetail'; + +// @ts-ignore +import { setup as setupI18n } from '../../../js/modules/i18n'; +// @ts-ignore +import enMessages from '../../../_locales/en/messages.json'; +const i18n = setupI18n('en', enMessages); + +const story = storiesOf('Components/Conversation/MessageDetail', module); + +const defaultMessage: MessageProps = { + authorTitle: 'Max', + canReply: true, + clearSelectedMessage: () => null, + conversationId: 'my-convo', + conversationType: 'direct', + deleteMessage: action('deleteMessage'), + direction: 'incoming', + displayTapToViewMessage: () => null, + downloadAttachment: () => null, + i18n, + id: 'my-message', + interactionMode: 'keyboard', + openConversation: () => null, + openLink: () => null, + previews: [], + reactToMessage: () => null, + renderEmojiPicker: () =>
, + replyToMessage: () => null, + retrySend: () => null, + scrollToQuotedMessage: () => null, + showContactDetail: () => null, + showExpiredIncomingTapToViewToast: () => null, + showExpiredOutgoingTapToViewToast: () => null, + showMessageDetail: () => null, + showVisualAttachment: () => null, + status: 'sent', + text: 'A message from Max', + timestamp: Date.now(), +}; + +const createProps = (overrideProps: Partial = {}): Props => ({ + contacts: overrideProps.contacts || [ + { + color: 'green', + isOutgoingKeyError: false, + isUnidentifiedDelivery: false, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'delivered', + title: 'Just Max', + }, + ], + errors: overrideProps.errors || [], + i18n, + message: overrideProps.message || defaultMessage, + receivedAt: number('receivedAt', overrideProps.receivedAt || Date.now()), + sentAt: number('sentAt', overrideProps.sentAt || Date.now()), +}); + +story.add('Delivered Incoming', () => { + const props = createProps({}); + return ; +}); + +story.add('Delivered Outgoing', () => { + const props = createProps({ + message: { + ...defaultMessage, + direction: 'outgoing', + text: 'A message to Max', + }, + }); + return ; +}); + +story.add('Message Statuses', () => { + const props = createProps({ + contacts: [ + { + color: 'green', + isOutgoingKeyError: false, + isUnidentifiedDelivery: false, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'sent', + title: 'Max', + }, + { + color: 'blue', + isOutgoingKeyError: false, + isUnidentifiedDelivery: false, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'sending', + title: 'Sally', + }, + { + color: 'brown', + isOutgoingKeyError: false, + isUnidentifiedDelivery: false, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'partial-sent', + title: 'Terry', + }, + { + color: 'light_green', + isOutgoingKeyError: false, + isUnidentifiedDelivery: false, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'delivered', + title: 'Theo', + }, + { + color: 'blue_grey', + isOutgoingKeyError: false, + isUnidentifiedDelivery: false, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'read', + title: 'Nikki', + }, + ], + message: { + ...defaultMessage, + conversationType: 'group', + text: 'A message to you all!', + }, + }); + return ; +}); + +story.add('Not Delivered', () => { + const props = createProps({ + message: { + ...defaultMessage, + direction: 'outgoing', + text: 'A message to Max', + }, + }); + props.receivedAt = undefined as any; + + return ; +}); + +story.add('No Contacts', () => { + const props = createProps({ + contacts: [], + message: { + ...defaultMessage, + direction: 'outgoing', + text: 'Is anybody there?', + }, + }); + return ; +}); + +story.add('All Errors', () => { + const props = createProps({ + errors: [ + { + name: 'Another Error', + message: 'Wow, that went bad.', + }, + ], + message: { + ...defaultMessage, + }, + contacts: [ + { + color: 'green', + isOutgoingKeyError: true, + isUnidentifiedDelivery: false, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'error', + title: 'Max', + }, + { + color: 'blue', + errors: [ + { + name: 'Big Error', + message: 'Stuff happened, in a bad way.', + }, + ], + isOutgoingKeyError: false, + isUnidentifiedDelivery: true, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'error', + title: 'Sally', + }, + { + color: 'brown', + isOutgoingKeyError: true, + isUnidentifiedDelivery: true, + onSendAnyway: action('onSendAnyway'), + onShowSafetyNumber: action('onShowSafetyNumber'), + status: 'error', + title: 'Terry', + }, + ], + }); + return ; +}); diff --git a/ts/components/conversation/MessageDetail.tsx b/ts/components/conversation/MessageDetail.tsx index 65fd0f7449..a9cab4c8f7 100644 --- a/ts/components/conversation/MessageDetail.tsx +++ b/ts/components/conversation/MessageDetail.tsx @@ -4,12 +4,12 @@ import moment from 'moment'; import { Avatar } from '../Avatar'; import { ContactName } from './ContactName'; -import { Message, Props as MessageProps } from './Message'; +import { Message, MessageStatusType, Props as MessageProps } from './Message'; import { LocalizerType } from '../../types/Util'; import { ColorType } from '../../types/Colors'; interface Contact { - status: string; + status: MessageStatusType; title: string; phoneNumber?: string; @@ -26,7 +26,7 @@ interface Contact { onShowSafetyNumber: () => void; } -interface Props { +export interface Props { sentAt: number; receivedAt: number; diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index c86703cb36..2e9cb9e367 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -11464,7 +11464,7 @@ "rule": "React-createRef", "path": "ts/components/conversation/Message.js", "line": " this.audioRef = react_1.default.createRef();", - "lineNumber": 54, + "lineNumber": 57, "reasonCategory": "usageTrusted", "updated": "2020-01-21T15:46:51.245Z" }, @@ -11472,7 +11472,7 @@ "rule": "React-createRef", "path": "ts/components/conversation/Message.js", "line": " this.reactionsContainerRef = react_1.default.createRef();", - "lineNumber": 56, + "lineNumber": 59, "reasonCategory": "usageTrusted", "updated": "2020-01-21T15:46:51.245Z", "reasonDetail": "Used for detecting clicks outside reaction viewer" @@ -11481,7 +11481,7 @@ "rule": "React-createRef", "path": "ts/components/conversation/Message.tsx", "line": " public audioRef: React.RefObject = React.createRef();", - "lineNumber": 195, + "lineNumber": 205, "reasonCategory": "usageTrusted", "updated": "2020-05-21T16:56:07.875Z" }, @@ -11489,7 +11489,7 @@ "rule": "React-createRef", "path": "ts/components/conversation/Message.tsx", "line": " > = React.createRef();", - "lineNumber": 199, + "lineNumber": 209, "reasonCategory": "usageTrusted", "updated": "2020-05-21T16:56:07.875Z" },