Convert <Avatar /> component to Storybook

Co-authored-by: Chris Svenningsen <chris@carbonfive.com>
Co-authored-by: Sidney Keese <me@sidke.com>
This commit is contained in:
Chris Svenningsen 2020-08-13 13:53:45 -07:00 committed by GitHub
parent 4169c120fc
commit d3d3c41f94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 183 additions and 540 deletions

View File

@ -1,468 +0,0 @@
### With avatar
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={112}
color="blue"
avatarPath={util.gifObjectUrl}
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={80}
color="blue"
avatarPath={util.gifObjectUrl}
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={52}
color="blue"
avatarPath={util.gifObjectUrl}
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="blue"
avatarPath={util.gifObjectUrl}
conversationType="direct"
i18n={util.i18n}
/>
<hr />
<Avatar
size={80}
color="blue"
avatarPath={util.gifObjectUrl}
conversationType="direct"
onClick={() => console.log('onClick')}
i18n={util.i18n}
/>
<Avatar
size={52}
color="blue"
avatarPath={util.gifObjectUrl}
conversationType="direct"
onClick={() => console.log('onClick')}
i18n={util.i18n}
/>
<Avatar
size={28}
color="blue"
avatarPath={util.gifObjectUrl}
conversationType="direct"
onClick={() => console.log('onClick')}
i18n={util.i18n}
/>
</util.ConversationContext>
```
### With only name
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={28}
color="blue"
name="John"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="green"
name="John Smith"
conversationType="direct"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### Just phone number
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={28}
color="pink"
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### Letters
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={112}
color="blue"
name="One"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={80}
color="blue"
name="One"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={52}
color="blue"
name="One"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="blue"
name="One"
conversationType="direct"
i18n={util.i18n}
/>
<hr />
<Avatar
size={80}
color="blue"
name="One"
conversationType="direct"
onClick={() => console.log('onClick')}
i18n={util.i18n}
/>
<Avatar
size={52}
color="blue"
name="One"
conversationType="direct"
onClick={() => console.log('onClick')}
i18n={util.i18n}
/>
<Avatar
size={28}
color="blue"
name="One"
conversationType="direct"
onClick={() => console.log('onClick')}
i18n={util.i18n}
/>
</util.ConversationContext>
```
### Note to self
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={112}
color="pink"
noteToSelf={true}
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={80}
color="pink"
noteToSelf={true}
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={52}
color="pink"
noteToSelf={true}
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="pink"
noteToSelf={true}
phoneNumber="(555) 353-3433"
conversationType="direct"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### Group Icon
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar size={112} color="blue" conversationType="group" i18n={util.i18n} />
<Avatar size={80} color="blue" conversationType="group" i18n={util.i18n} />
<Avatar size={52} color="blue" conversationType="group" i18n={util.i18n} />
<Avatar size={28} color="blue" conversationType="group" i18n={util.i18n} />
</util.ConversationContext>
```
### Contact Icon
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar size={112} color="blue" conversationType="direct" i18n={util.i18n} />
<Avatar size={80} color="blue" conversationType="direct" i18n={util.i18n} />
<Avatar size={52} color="blue" conversationType="direct" i18n={util.i18n} />
<Avatar size={28} color="blue" conversationType="direct" i18n={util.i18n} />
</util.ConversationContext>
```
### All colors, 28px
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={28}
color="signal-blue"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="signal-blue"
name="Group"
conversationType="group"
i18n={util.i18n}
/>
<Avatar
size={28}
color="red"
name="Red"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="deep_orange"
name="Deep Orange"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="brown"
name="Broen"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="pink"
name="Pink"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="purple"
name="Purple"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="indigo"
name="Indigo"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="blue"
name="Blue"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="teal"
name="Teal"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="green"
name="Green"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="light_green"
name="Light Green"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={28}
color="blue_grey"
name="Blue Grey"
conversationType="direct"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### 52px
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={52}
color="teal"
name="John Smith"
avatarPath={util.gifObjectUrl}
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={52}
color="teal"
name="John"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={52}
color="teal"
name="John Smith"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar size={52} color="teal" conversationType="direct" i18n={util.i18n} />
<Avatar
size={52}
color="teal"
name="Pupplies"
conversationType="group"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### 80px
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={80}
color="teal"
name="John Smith"
avatarPath={util.gifObjectUrl}
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={80}
color="teal"
name="John"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={80}
color="teal"
name="John Smith"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar size={80} color="teal" conversationType="direct" i18n={util.i18n} />
<Avatar
size={80}
color="teal"
name="Pupplies"
conversationType="group"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### 112px
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={112}
color="teal"
name="John Smith"
avatarPath={util.gifObjectUrl}
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={112}
color="teal"
name="John"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar
size={112}
color="teal"
name="John Smith"
conversationType="direct"
i18n={util.i18n}
/>
<Avatar size={112} color="teal" conversationType="direct" i18n={util.i18n} />
<Avatar
size={112}
color="teal"
name="Pupplies"
conversationType="group"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### Broken color
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={28}
color="fake"
name="F"
conversationType="direct"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### Broken image
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={28}
color="pink"
name="John Smith"
avatarPath="nonexistent"
conversationType="direct"
i18n={util.i18n}
/>
</util.ConversationContext>
```
### Broken image for group
```jsx
<util.ConversationContext theme={util.theme} ios={util.ios}>
<Avatar
size={28}
avatarPath="nonexistent"
color="pink"
name="Puppies"
avatarPath="nonexistent"
conversationType="group"
i18n={util.i18n}
/>
</util.ConversationContext>
```

View File

@ -0,0 +1,126 @@
import * as React from 'react';
import { Avatar, Props } from './Avatar';
import { storiesOf } from '@storybook/react';
import { boolean, select, text } 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 { Colors, ColorType } from '../types/Colors';
const i18n = setupI18n('en', enMessages);
const story = storiesOf('Components/Avatar', module);
const colorMap: Record<string, ColorType> = Colors.reduce(
(m, color) => ({
...m,
[color]: color,
}),
{}
);
const conversationTypeMap: Record<string, Props['conversationType']> = {
direct: 'direct',
group: 'group',
};
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
avatarPath: text('avatarPath', overrideProps.avatarPath || ''),
color: select('color', colorMap, overrideProps.color || 'blue'),
conversationType: select(
'conversationType',
conversationTypeMap,
overrideProps.conversationType || 'direct'
),
i18n,
name: text('name', overrideProps.name || ''),
noteToSelf: boolean('noteToSelf', overrideProps.noteToSelf || false),
onClick: action('onClick'),
phoneNumber: text('phoneNumber', overrideProps.phoneNumber || ''),
size: 80,
title: '',
});
const sizes: Array<Props['size']> = [112, 80, 52, 32, 28];
story.add('Avatar', () => {
const props = createProps({
avatarPath: '/fixtures/giphy-GVNvOUpeYmI7e.gif',
});
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});
story.add('One-word Name', () => {
const props = createProps({
name: 'John',
});
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});
story.add('Multi-word Name', () => {
const props = createProps({
name: 'John Smith',
});
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});
story.add('Note to Self', () => {
const props = createProps({
noteToSelf: true,
});
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});
story.add('Contact Icon', () => {
const props = createProps();
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});
story.add('Group Icon', () => {
const props = createProps({
conversationType: 'group',
});
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});
story.add('Colors', () => {
const props = createProps();
return Colors.map(color => <Avatar key={color} {...props} color={color} />);
});
story.add('Broken Color', () => {
const props = createProps({
color: 'nope' as ColorType,
});
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});
story.add('Broken Avatar', () => {
const props = createProps({
avatarPath: 'badimage.png',
});
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});
story.add('Broken Avatar for Group', () => {
const props = createProps({
avatarPath: 'badimage.png',
conversationType: 'group',
});
return sizes.map(size => <Avatar key={size} {...props} size={size} />);
});

View File

@ -2,7 +2,8 @@ import * as React from 'react';
import classNames from 'classnames';
import { getInitials } from '../util/getInitials';
import { ColorType, LocalizerType } from '../types/Util';
import { LocalizerType } from '../types/Util';
import { ColorType } from '../types/Colors';
export type Props = {
avatarPath?: string;

View File

@ -1,7 +1,7 @@
import * as React from 'react';
import { CallManager } from './CallManager';
import { CallState } from '../types/Calling';
import { ColorType } from '../types/Util';
import { ColorType } from '../types/Colors';
// @ts-ignore
import { setup as setupI18n } from '../../js/modules/i18n';

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import { CallState } from '../types/Calling';
import { ColorType } from '../types/Util';
import { ColorType } from '../types/Colors';
import { CallScreen } from './CallScreen';
// @ts-ignore

View File

@ -5,7 +5,8 @@ import { Avatar } from './Avatar';
import { Emojify } from './conversation/Emojify';
import { InContactsIcon } from './InContactsIcon';
import { ColorType, LocalizerType } from '../types/Util';
import { LocalizerType } from '../types/Util';
import { ColorType } from '../types/Colors';
interface Props {
title: string;

View File

@ -9,7 +9,8 @@ import { ContactName } from './conversation/ContactName';
import { TypingAnimation } from './conversation/TypingAnimation';
import { cleanId } from './_util';
import { ColorType, LocalizerType } from '../types/Util';
import { LocalizerType } from '../types/Util';
import { ColorType } from '../types/Colors';
export type PropsData = {
id: string;

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import { IncomingCallBar } from './IncomingCallBar';
import { ColorType } from '../types/Util';
import { Colors, ColorType } from '../types/Colors';
// @ts-ignore
import { setup as setupI18n } from '../../js/modules/i18n';
@ -31,22 +31,6 @@ const defaultProps = {
i18n,
};
const colors: Array<ColorType> = [
'blue',
'blue_grey',
'brown',
'deep_orange',
'green',
'grey',
'indigo',
'light_green',
'pink',
'purple',
'red',
'teal',
'ultramarine',
];
const permutations = [
{
title: 'Incoming Call Bar (no call details)',
@ -74,7 +58,7 @@ const permutations = [
storiesOf('Components/IncomingCallBar', module)
.add('Knobs Playground', () => {
const color = select('color', colors, 'ultramarine');
const color = select('color', Colors, 'ultramarine');
const isVideoCall = boolean('isVideoCall', false);
const name = text(
'name',

View File

@ -7,7 +7,8 @@ import { createPortal } from 'react-dom';
import { showSettings } from '../shims/Whisper';
import { Avatar } from './Avatar';
import { AvatarPopup } from './AvatarPopup';
import { ColorType, LocalizerType } from '../types/Util';
import { LocalizerType } from '../types/Util';
import { ColorType } from '../types/Colors';
export interface PropsType {
searchTerm: string;

View File

@ -6,7 +6,8 @@ import { MessageBodyHighlight } from './MessageBodyHighlight';
import { Timestamp } from './conversation/Timestamp';
import { ContactName } from './conversation/ContactName';
import { ColorType, LocalizerType } from '../types/Util';
import { LocalizerType } from '../types/Util';
import { ColorType } from '../types/Colors';
export type PropsDataType = {
isSelected?: boolean;

View File

@ -11,7 +11,8 @@ import { Emojify } from './Emojify';
import { Avatar } from '../Avatar';
import { InContactsIcon } from '../InContactsIcon';
import { ColorType, LocalizerType } from '../../types/Util';
import { LocalizerType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
interface TimerOption {
name: string;

View File

@ -39,7 +39,8 @@ import { ContactType } from '../../types/Contact';
import { getIncrement } from '../../util/timer';
import { isFileDangerous } from '../../util/isFileDangerous';
import { ColorType, LocalizerType } from '../../types/Util';
import { LocalizerType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
import { createRefMerger } from '../_util';
import { ContextMenu, ContextMenuTrigger, MenuItem } from 'react-contextmenu';

View File

@ -5,7 +5,8 @@ import moment from 'moment';
import { Avatar } from '../Avatar';
import { ContactName } from './ContactName';
import { Message, Props as MessageProps } from './Message';
import { ColorType, LocalizerType } from '../../types/Util';
import { LocalizerType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
interface Contact {
status: string;

View File

@ -7,7 +7,8 @@ import * as MIME from '../../../ts/types/MIME';
import * as GoogleChrome from '../../../ts/util/GoogleChrome';
import { MessageBody } from './MessageBody';
import { ColorType, LocalizerType } from '../../types/Util';
import { LocalizerType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
import { ContactName } from './ContactName';
interface Props {

View File

@ -5,7 +5,7 @@ import { ContactName } from './ContactName';
import { Avatar, Props as AvatarProps } from '../Avatar';
import { Emoji } from '../emoji/Emoji';
import { useRestoreFocus } from '../../util/hooks';
import { ColorType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
export type Reaction = {
emoji: string;

View File

@ -4,7 +4,8 @@ import classNames from 'classnames';
import { TypingAnimation } from './TypingAnimation';
import { Avatar } from '../Avatar';
import { ColorType, LocalizerType } from '../../types/Util';
import { LocalizerType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
interface Props {
avatarPath?: string;

3
ts/model-types.d.ts vendored
View File

@ -1,6 +1,7 @@
import * as Backbone from 'backbone';
import { ColorType, LocalizerType } from './types/Util';
import { LocalizerType } from './types/Util';
import { ColorType } from './types/Colors';
import { ConversationType } from './state/ducks/conversations';
import { CallingClass, CallHistoryDetailsType } from './services/calling';
import { SendOptionsType } from './textsecure/SendMessage';

View File

@ -2,7 +2,7 @@ import { notify } from '../../services/notify';
import { calling, VideoCapturer, VideoRenderer } from '../../services/calling';
import { CallState } from '../../types/Calling';
import { CanvasVideoRenderer, GumVideoCapturer } from '../../window.d';
import { ColorType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
import { NoopActionType } from './noop';
import { callingTones } from '../../util/callingTones';
import { requestCameraPermissions } from '../../util/callingPermissions';

View File

@ -12,7 +12,7 @@ import {
import { trigger } from '../../shims/events';
import { NoopActionType } from './noop';
import { AttachmentType } from '../../types/Attachment';
import { ColorType } from '../../types/Util';
import { ColorType } from '../../types/Colors';
// State

18
ts/types/Colors.ts Normal file
View File

@ -0,0 +1,18 @@
export const Colors = [
'red',
'deep_orange',
'brown',
'pink',
'purple',
'indigo',
'blue',
'teal',
'green',
'light_green',
'blue_grey',
'grey',
'ultramarine',
'signal-blue',
] as const;
export type ColorType = typeof Colors[number];

View File

@ -11,19 +11,3 @@ export type LocalizerType = (
key: string,
values?: Array<string> | ReplacementValuesType
) => string;
export type ColorType =
| 'red'
| 'deep_orange'
| 'brown'
| 'pink'
| 'purple'
| 'indigo'
| 'blue'
| 'teal'
| 'green'
| 'light_green'
| 'blue_grey'
| 'grey'
| 'ultramarine'
| 'signal-blue';

View File

@ -11770,7 +11770,7 @@
"rule": "React-createRef",
"path": "ts/components/MainHeader.tsx",
"line": " this.inputRef = React.createRef();",
"lineNumber": 70,
"lineNumber": 71,
"reasonCategory": "usageTrusted",
"updated": "2020-02-14T20:02:37.507Z",
"reasonDetail": "Used only to set focus"
@ -11806,7 +11806,7 @@
"rule": "React-createRef",
"path": "ts/components/conversation/ConversationHeader.tsx",
"line": " this.menuTriggerRef = React.createRef();",
"lineNumber": 75,
"lineNumber": 76,
"reasonCategory": "usageTrusted",
"updated": "2020-05-20T20:10:43.540Z",
"reasonDetail": "Used to reference popup menu"
@ -11850,7 +11850,7 @@
"rule": "React-createRef",
"path": "ts/components/conversation/Message.tsx",
"line": " public audioRef: React.RefObject<HTMLAudioElement> = React.createRef();",
"lineNumber": 185,
"lineNumber": 186,
"reasonCategory": "usageTrusted",
"updated": "2020-05-21T16:56:07.875Z"
},
@ -11858,7 +11858,7 @@
"rule": "React-createRef",
"path": "ts/components/conversation/Message.tsx",
"line": " > = React.createRef();",
"lineNumber": 189,
"lineNumber": 190,
"reasonCategory": "usageTrusted",
"updated": "2020-05-21T16:56:07.875Z"
},

View File

@ -1,6 +1,8 @@
import { ColorType } from '../types/Colors';
// import { missingCaseError } from './missingCaseError';
type OldColor =
type OldColorType =
| 'amber'
| 'blue'
| 'blue_grey'
@ -22,22 +24,7 @@ type OldColor =
| 'yellow'
| 'ultramarine';
type NewColor =
| 'red'
| 'deep_orange'
| 'brown'
| 'pink'
| 'purple'
| 'indigo'
| 'blue'
| 'teal'
| 'green'
| 'light_green'
| 'blue_grey'
| 'grey'
| 'ultramarine';
export function migrateColor(color: OldColor): NewColor {
export function migrateColor(color: OldColorType): ColorType {
switch (color) {
// These colors no longer exist
case 'orange':

3
ts/window.d.ts vendored
View File

@ -18,7 +18,8 @@ import { ContactRecordIdentityState, TextSecureType } from './textsecure.d';
import { WebAPIConnectType } from './textsecure/WebAPI';
import { CallingClass, CallHistoryDetailsType } from './services/calling';
import * as Crypto from './Crypto';
import { ColorType, LocalizerType } from './types/Util';
import { LocalizerType } from './types/Util';
import { ColorType } from './types/Colors';
import { ConversationController } from './ConversationController';
import { SendOptionsType } from './textsecure/SendMessage';
import Data from './sql/Client';