Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

feat(ChatMessage): add badge and badgePosition props #823

Merged
merged 12 commits into from
Feb 4, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Features
- Accessibility for menu divider @jurokapsiar ([#822](https://github.com/stardust-ui/react/pull/822))
- Added slot class names in `ChatMessage`, `ChatItem`, `Dropdown`, `ItemLayout`, `Layout`, `MenuItem` @mnajdova ([#827](https://github.com/stardust-ui/react/pull/827))
- Add `badge` and `badgePosition` properties on the `ChatMessage` @mnajdova ([#823](https://github.com/stardust-ui/react/pull/823))

### Fixes
- Fix `Dropdown` component styles regression @Bugaa92 ([#824](https://github.com/stardust-ui/react/pull/824))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as React from 'react'
import { Avatar, Chat } from '@stardust-ui/react'

const janeAvatar = {
image: 'public/images/avatar/small/ade.jpg',
status: { color: 'green', icon: 'check' },
}

const items = [
{
message: {
content: (
<Chat.Message
content="Hi, can we talk? It's important!"
author="John Doe"
timestamp="Yesterday, 10:15 PM"
mine
badge={{
icon: 'exclamation',
color: 'red',
}}
badgePosition="start"
/>
),
},
contentPosition: 'end',
key: 'message-id-1',
},
{
gutter: { content: <Avatar {...janeAvatar} /> },
message: {
content: (
<Chat.Message
content="Sure @John. Let's schedule a meeting."
author="Jane Doe"
timestamp="Yesterday, 10:15 PM"
badge={{
icon: 'at',
color: 'red',
}}
/>
),
},
attached: 'top',
key: 'message-id-4',
},
]

const ChatExample = () => <Chat items={items} />

export default ChatExample
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ const content = (
</div>
)

const slotLabelStyles: any = (label, beforeStyles) => ({
const slotLabelStyles: any = (label, beforeStyles?, slotStyles?) => ({
position: 'relative',
border: '1px solid #000',
padding: '12px',
...slotStyles,
':before': {
content: `'${label}'`,
position: 'absolute',
Expand Down Expand Up @@ -56,6 +57,14 @@ const ChatMessageExampleStyled = () => (
}),
content: { ...slotLabelStyles('content'), backgroundColor: '#F08080' },
timestamp: { ...slotLabelStyles('timestamp'), backgroundColor: '#FFFFE0' },
badge: {
...slotLabelStyles(
'badge',
{ textAlign: 'center', left: '0px' },
{ position: 'absolute', overflow: 'visible' },
),
backgroundColor: '#FFFF00',
},
},
},
componentVariables: {
Expand All @@ -77,6 +86,8 @@ const ChatMessageExampleStyled = () => (
author="John Doe"
timestamp="Yesterday, 10:15 PM"
mine
badge={{ icon: 'at' }}
badgePosition="start"
/>
),
},
Expand All @@ -92,6 +103,7 @@ const ChatMessageExampleStyled = () => (
content={{ content }}
author="Jane Doe"
timestamp="Yesterday, 10:15 PM"
badge={{ icon: 'exclamation' }}
/>
),
},
Expand Down
5 changes: 5 additions & 0 deletions docs/src/examples/components/Chat/Types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ const Types = () => (
description="A Chat item with custom styles for every slot."
examplePath="components/Chat/Types/ChatMessageExampleStyled"
/>
<ComponentExample
title="Badge"
description="A Chat message may contained badge positioned at the start or end of the message."
examplePath="components/Chat/Types/ChatMessageExampleBadge"
/>
</ExampleSection>
)

Expand Down
23 changes: 22 additions & 1 deletion packages/react/src/components/Chat/ChatMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibil

import Text from '../Text/Text'
import Box from '../Box/Box'
import Label from '../Label/Label'

export interface ChatMessageSlotClassNames {
author: string
timestamp: string
badge: string
content: string
}

Expand All @@ -48,6 +50,12 @@ export interface ChatMessageProps
/** Timestamp of the message. */
timestamp?: ShorthandValue

/** Badge attached to the message. */
badge?: ShorthandValue

/** A message can format the badge to appear at the start or the end of the message. */
badgePosition?: 'start' | 'end'

/**
* Called after user's focus.
* @param {SyntheticEvent} event - React's original SyntheticEvent.
Expand Down Expand Up @@ -76,6 +84,8 @@ class ChatMessage extends UIComponent<ReactProps<ChatMessageProps>, ChatMessageS
...commonPropTypes.createCommon({ content: 'shorthand' }),
accessibility: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
author: customPropTypes.itemShorthand,
badge: customPropTypes.itemShorthand,
badgePosition: PropTypes.oneOf(['start', 'end']),
mine: PropTypes.bool,
timestamp: customPropTypes.itemShorthand,
onFocus: PropTypes.func,
Expand All @@ -84,6 +94,7 @@ class ChatMessage extends UIComponent<ReactProps<ChatMessageProps>, ChatMessageS
static defaultProps = {
accessibility: chatMessageBehavior,
as: 'div',
badgePosition: 'end',
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure about this default - lot of examples where badge appears at start

}

public state = {
Expand All @@ -110,9 +121,15 @@ class ChatMessage extends UIComponent<ReactProps<ChatMessageProps>, ChatMessageS
unhandledProps,
styles,
}: RenderResultConfig<ChatMessageProps>) {
const { author, children, content, timestamp } = this.props
const { author, children, content, timestamp, badge, badgePosition } = this.props
const childrenPropExists = childrenExist(children)
const className = childrenPropExists ? cx(classes.root, classes.content) : classes.root
const badgeElement = Label.create(badge, {
defaultProps: {
className: ChatMessage.slotClassNames.badge,
styles: styles.badge,
},
})

return (
<ElementType
Expand All @@ -127,6 +144,7 @@ class ChatMessage extends UIComponent<ReactProps<ChatMessageProps>, ChatMessageS
children
) : (
<>
{badgePosition === 'start' && badgeElement}
{Text.create(author, {
defaultProps: {
size: 'small',
Expand All @@ -142,12 +160,14 @@ class ChatMessage extends UIComponent<ReactProps<ChatMessageProps>, ChatMessageS
className: ChatMessage.slotClassNames.timestamp,
},
})}

{Box.create(content, {
defaultProps: {
className: ChatMessage.slotClassNames.content,
styles: styles.content,
},
})}
{badgePosition === 'end' && badgeElement}
</>
)}
</ElementType>
Expand All @@ -159,6 +179,7 @@ ChatMessage.create = createShorthandFactory(ChatMessage, 'content')
ChatMessage.slotClassNames = {
author: `${ChatMessage.className}__author`,
timestamp: `${ChatMessage.className}__timestamp`,
badge: `${ChatMessage.className}__badge`,
content: `${ChatMessage.className}__content`,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { pxToRem } from '../../../../lib'

const chatMessageStyles: ComponentSlotStylesInput<ChatMessageProps, ChatMessageVariables> = {
root: ({ props: p, variables: v }): ICSSInJSStyle => ({
position: 'relative',
display: 'inline-block',
paddingLeft: v.padding,
paddingRight: v.padding,
Expand Down Expand Up @@ -44,6 +45,21 @@ const chatMessageStyles: ComponentSlotStylesInput<ChatMessageProps, ChatMessageV
textDecoration: 'underline',
},
}),
badge: ({ props: p, variables: v }) => {
const sidePosition = p.badgePosition === 'start' ? 'left' : 'right'
return {
boxShadow: v.badgeShadow,
position: 'absolute',
padding: pxToRem(4),
height: 'auto',
width: 'auto',
borderRadius: '50%',
top: pxToRem(4),
zIndex: '1',
[sidePosition]: 0,
transform: p.badgePosition === 'start' ? 'translateX(-50%)' : 'translateX(50%)',
}
},
}

export default chatMessageStyles
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface ChatMessageVariables {
headerMarginBottom: string
contentFocusOutlineColor: string
border: string
badgeShadow: string
}

export default (siteVars): ChatMessageVariables => ({
Expand All @@ -24,4 +25,5 @@ export default (siteVars): ChatMessageVariables => ({
headerMarginBottom: pxToRem(2),
contentFocusOutlineColor: siteVars.brand,
border: 'none',
badgeShadow: siteVars.shadowLevel1Darker,
})
1 change: 1 addition & 0 deletions packages/react/src/themes/teams/siteVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const green04 = '#237b4b'
// SHADOW LEVELS
//
export const shadowLevel1 = '0 .2rem .4rem -.075rem rgba(0, 0, 0, 0.1)'
export const shadowLevel1Darker = '0 .2rem .4rem -.075rem rgba(0, 0, 0, 0.5)'

//
// FONT SIZES
Expand Down