Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Rename `toggleButton` prop to `toggleIndicator` and make it visible by default @layershifter ([#729](https://github.com/stardust-ui/react/pull/729))
- Remove `props` from variables resolution process @kuzhelov ([#770](https://github.com/stardust-ui/react/pull/770))
- Update Fela and is deps to latest, `10.1.3` is required @layershifter ([#768](https://github.com/stardust-ui/react/pull/768))
- Replaced `gutterPosition` with `contentPosition` in ChatItem (`contentPosition='end'` should be added on the ChatItems containing ChatMessage with `mine` prop for teams theme) @mnajdova ([#767](https://github.com/stardust-ui/react/pull/767))

### Features
- Add `loading` prop for `Dropdown` @layershifter ([#729](https://github.com/stardust-ui/react/pull/729))
- Export `close` icon in Teams theme @alinais ([#774](https://github.com/stardust-ui/react/pull/774))
- Add `attached` prop for ChatItem @mnajdova ([#767](https://github.com/stardust-ui/react/pull/767))

### Fixes
- Make `headerMedia` visible for screen readers in `ListItem` @layershifter ([#772](https://github.com/stardust-ui/react/pull/772))
Expand Down
69 changes: 55 additions & 14 deletions docs/src/examples/components/Chat/Types/ChatExample.shorthand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,63 @@ const items = [
<Chat.Message content="Hello" author="John Doe" timestamp="Yesterday, 10:15 PM" mine />
),
},
contentPosition: 'end',
attached: 'top',
key: 'message-id-1',
},
{
gutter: { content: <Avatar {...janeAvatar} /> },
message: {
content: <Chat.Message content="Hi" author="Jane Doe" timestamp="Yesterday, 10:15 PM" />,
content: (
<Chat.Message content="I'm back!" author="John Doe" timestamp="Yesterday, 10:15 PM" mine />
),
},
contentPosition: 'end',
attached: true,
key: 'message-id-2',
},
{
message: { content: <Chat.Message content="What's up?" /> },
message: {
content: (
<Chat.Message
content="Thanks for waiting!"
author="John Doe"
timestamp="Yesterday, 10:15 PM"
mine
/>
),
},
contentPosition: 'end',
attached: 'bottom',
key: 'message-id-3',
},
{
gutter: { content: <Avatar {...janeAvatar} /> },
message: {
content: <Chat.Message content="Hi" author="Jane Doe" timestamp="Yesterday, 10:15 PM" />,
},
attached: 'top',
key: 'message-id-4',
},
{
gutter: { content: <Avatar {...janeAvatar} /> },
message: {
content: (
<Chat.Message content="No problem!" author="Jane Doe" timestamp="Yesterday, 10:15 PM" />
),
},
attached: true,
key: 'message-id-5',
},
{
gutter: { content: <Avatar {...janeAvatar} /> },
message: {
content: (
<Chat.Message content="What's up?" author="Jane Doe" timestamp="Yesterday, 10:15 PM" />
),
},
attached: 'bottom',
key: 'message-id-6',
},
{
message: {
content: (
Expand All @@ -37,37 +81,34 @@ const items = [
/>
),
},
key: 'message-id-4',
contentPosition: 'end',
key: 'message-id-7',
},
{
gutter: { content: <Avatar {...janeAvatar} /> },
message: {
content: (
<Chat.Message
content="Sure! Let's try the new place downtown"
content="Sure! Let's try the new place downtown."
author="Jane Doe"
timestamp="Yesterday, 10:15 PM"
/>
),
},
key: 'message-id-5',
key: 'message-id-8',
},
{
children: <Divider content="Today" color="primary" important />,
key: 'message-id-6',
key: 'message-id-9',
},
{
message: {
content: (
<Chat.Message
content="Let's have a call"
author="John Doe"
timestamp="Today, 11:15 PM"
mine
/>
<Chat.Message content="Ok, let's go." author="John Doe" timestamp="Today, 11:15 PM" mine />
),
},
key: 'message-id-7',
contentPosition: 'end',
key: 'message-id-10',
},
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ const [janeAvatar, johnAvatar] = [

const items = [
{
gutterPosition: 'start',
contentPosition: 'start',
gutter: { content: <Avatar {...johnAvatar} /> },
message: {
content: <Chat.Message content="Hello" author="John Doe" timestamp="Yesterday, 10:15 PM" />,
},
key: 'message-id-1',
},
{
gutterPosition: 'end',
contentPosition: 'end',
gutter: { content: <Avatar {...janeAvatar} /> },
message: {
content: <Chat.Message content="Hi" author="Jane Doe" timestamp="Yesterday, 10:15 PM" mine />,
Expand All @@ -28,6 +28,6 @@ const items = [
},
]

const ChatExampleGutterPosition = () => <Chat items={items} />
const ChatExampleContentPosition = () => <Chat items={items} />

export default ChatExampleGutterPosition
export default ChatExampleContentPosition
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const ChatMessageExampleStyled = () => (
/>
),
},
contentPosition: 'end',
key: 'message-id-1',
},
{
Expand Down
6 changes: 3 additions & 3 deletions docs/src/examples/components/Chat/Types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ const Types = () => (
examplePath="components/Chat/Types/ChatExample"
/>
<ComponentExample
title="Gutter"
description="A Chat can have a gutter positioned at the start or at the end of a message."
examplePath="components/Chat/Types/ChatExampleGutterPosition"
title="Content position"
description="A ChatItem can position it's content at the start or at the end of the container."
examplePath="components/Chat/Types/ChatExampleContentPosition"
/>
<ComponentExample
title="Styled Chat Item"
Expand Down
2 changes: 1 addition & 1 deletion docs/src/prototypes/chatPane/chatPaneContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class ChatPaneContainer extends React.PureComponent<ChatPaneContainerProps> {
return (
<Chat.Item
key={`chat-item-${index}`}
gutterPosition={mine ? 'end' : 'start'}
contentPosition={mine ? 'end' : 'start'}
gutter={gutter && { content: <Avatar {...gutter} /> }}
message={{
content: (
Expand Down
19 changes: 12 additions & 7 deletions src/components/Chat/ChatItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ import Box from '../Box/Box'
import { ComponentSlotStylesPrepared } from '../../themes/types'

export interface ChatItemProps extends UIComponentProps, ChildrenComponentProps {
/** Attach ChatItem to other content. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would suggest to omit component class names in the description - as it should just convey general purpose of the prop. Something like: Controls item's relation to other chat items

attached?: boolean | 'top' | 'bottom'

/** Chat items can have a gutter. */
gutter?: ShorthandValue

/** Indicates whether the gutter is positioned at the start or the end. */
gutterPosition?: 'start' | 'end'
/** Indicates whether the content is positioned at the start or the end. */
contentPosition?: 'start' | 'end'

/** Chat items can have a message. */
message?: ShorthandValue
Expand All @@ -37,14 +40,16 @@ class ChatItem extends UIComponent<ReactProps<ChatItemProps>, any> {

static propTypes = {
...commonPropTypes.createCommon({ content: false }),
attached: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['top', 'bottom'])]),
gutter: customPropTypes.itemShorthand,
gutterPosition: PropTypes.oneOf(['start', 'end']),
contentPosition: PropTypes.oneOf(['start', 'end']),
message: customPropTypes.itemShorthand,
}

static defaultProps = {
as: 'li',
gutterPosition: 'start',
contentPosition: 'start',
attached: false,
}

renderComponent({
Expand All @@ -67,14 +72,14 @@ class ChatItem extends UIComponent<ReactProps<ChatItemProps>, any> {
}

private renderChatItem(styles: ComponentSlotStylesPrepared) {
const { message, gutter, gutterPosition } = this.props
const { message, gutter, contentPosition } = this.props
const gutterElement = gutter && Box.create(gutter, { defaultProps: { styles: styles.gutter } })

return (
<>
{gutterPosition === 'start' && gutterElement}
{contentPosition === 'start' && gutterElement}
{Box.create(message, { defaultProps: { styles: styles.message } })}
{gutterPosition === 'end' && gutterElement}
{contentPosition === 'end' && gutterElement}
</>
)
}
Expand Down
26 changes: 24 additions & 2 deletions src/components/Chat/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibil
import Text from '../Text/Text'
import Box from '../Box/Box'

export interface ChatMessageSlotClassNames {
author: string
timestamp: string
}

export interface ChatMessageProps
extends UIComponentProps,
ChildrenComponentProps,
Expand Down Expand Up @@ -62,6 +67,8 @@ class ChatMessage extends UIComponent<ReactProps<ChatMessageProps>, ChatMessageS

static create: Function

static slotClassNames: ChatMessageSlotClassNames

static displayName = 'ChatMessage'

static propTypes = {
Expand Down Expand Up @@ -119,9 +126,20 @@ class ChatMessage extends UIComponent<ReactProps<ChatMessageProps>, ChatMessageS
children
) : (
<>
{Text.create(author, { defaultProps: { size: 'small', styles: styles.author } })}
{Text.create(author, {
defaultProps: {
size: 'small',
styles: styles.author,
className: ChatMessage.slotClassNames.author,
},
})}
{Text.create(timestamp, {
defaultProps: { size: 'small', styles: styles.timestamp, timestamp: true },
defaultProps: {
size: 'small',
styles: styles.timestamp,
timestamp: true,
className: ChatMessage.slotClassNames.timestamp,
},
})}
{Box.create(content, { defaultProps: { styles: styles.content } })}
</>
Expand All @@ -132,5 +150,9 @@ class ChatMessage extends UIComponent<ReactProps<ChatMessageProps>, ChatMessageS
}

ChatMessage.create = createShorthandFactory(ChatMessage, 'content')
ChatMessage.slotClassNames = {
author: `${ChatMessage.className}__author`,
timestamp: `${ChatMessage.className}__timestamp`,
}

export default ChatMessage
6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ export { default as ButtonGroup, ButtonGroupProps } from './components/Button/Bu

export { default as Chat, ChatProps } from './components/Chat/Chat'
export { default as ChatItem, ChatItemProps } from './components/Chat/ChatItem'
export { default as ChatMessage, ChatMessageProps } from './components/Chat/ChatMessage'
export {
default as ChatMessage,
ChatMessageProps,
ChatMessageSlotClassNames,
} from './components/Chat/ChatMessage'

export {
default as Divider,
Expand Down
53 changes: 50 additions & 3 deletions src/themes/teams/components/Chat/chatItemStyles.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,71 @@
import { ICSSInJSStyle, ComponentSlotStylesInput } from '../../../types'
import { ChatItemVariables } from './chatItemVariables'
import { ChatItemProps } from '../../../../components/Chat/ChatItem'
import { pxToRem } from '../../../../lib'
import { default as ChatMessage } from '../../../../components/Chat/ChatMessage'
import { screenReaderContainerStyles } from '../../../../lib/accessibility/Styles/accessibilityStyles'

const chatMessageClassNameSelector = `& .${ChatMessage.className}`
const chatMessageAuthorClassNameSelector = `& .${ChatMessage.slotClassNames.author}`
const chatMessageTimestampClassNameSelector = `& .${ChatMessage.slotClassNames.timestamp}`

const getPositionStyles = (props: ChatItemProps) => ({
float: props.contentPosition === 'end' ? 'right' : 'left',
})

const getChatMessageEvaluatedStyles = (p: ChatItemProps) => ({
...(!p.attached && { [chatMessageClassNameSelector]: getPositionStyles(p) }),
...(p.attached === true && {
[chatMessageClassNameSelector]: {
[p.contentPosition === 'end' ? 'borderTopRightRadius' : 'borderTopLeftRadius']: 0,
[p.contentPosition === 'end' ? 'borderBottomRightRadius' : 'borderBottomLeftRadius']: 0,
paddingTop: pxToRem(5),
paddingBottom: pxToRem(7),
...getPositionStyles(p),
},
}),
...(p.attached === 'top' && {
[chatMessageClassNameSelector]: {
[p.contentPosition === 'end' ? 'borderBottomRightRadius' : 'borderBottomLeftRadius']: 0,
...getPositionStyles(p),
},
}),
...(p.attached === 'bottom' && {
[chatMessageClassNameSelector]: {
[p.contentPosition === 'end' ? 'borderTopRightRadius' : 'borderTopLeftRadius']: 0,
paddingTop: pxToRem(5),
paddingBottom: pxToRem(7),
...getPositionStyles(p),
},
}),
})

const chatItemStyles: ComponentSlotStylesInput<ChatItemProps, ChatItemVariables> = {
root: ({ props: p, variables: v }): ICSSInJSStyle => ({
position: 'relative',
marginTop: v.margin,
marginBottom: v.margin,
...((!p.attached || p.attached === 'top') && { marginTop: pxToRem(16) }),
...((p.attached === 'bottom' || p.attached === true) && {
marginTop: pxToRem(2),
[chatMessageAuthorClassNameSelector]: screenReaderContainerStyles,
[chatMessageTimestampClassNameSelector]: screenReaderContainerStyles,
}),
marginBottom: 0,
}),

gutter: ({ props: p, variables: v }): ICSSInJSStyle => ({
position: 'absolute',
marginTop: v.gutterMargin,
[p.gutterPosition === 'end' ? 'right' : 'left']: 0,
[p.contentPosition === 'end' ? 'right' : 'left']: 0,
...((p.attached === 'bottom' || p.attached === true) && {
display: 'none',
}),
}),

message: ({ props: p, variables: v }): ICSSInJSStyle => ({
position: 'relative',
marginLeft: v.messageMargin,
marginRight: v.messageMargin,
...getChatMessageEvaluatedStyles(p),
}),
}

Expand Down
Loading