-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2473 from anuradha9712/feat-chat-bubble-component
feat(chatBubble): add new chat bubble component
- Loading branch information
Showing
26 changed files
with
1,792 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import * as React from 'react'; | ||
import { IncomingBubble, IncomingOptionProps } from './IncomingBubble'; | ||
import { OutgoingBubble, OutgoingOptionProps } from './OutgoingBubble'; | ||
import { BaseProps } from '@/utils/types'; | ||
|
||
export type ChatBubbleType = 'incoming' | 'outgoing'; | ||
|
||
export interface ChatBubbleProps extends BaseProps { | ||
/** | ||
* Type of ChatBubble | ||
*/ | ||
type: ChatBubbleType; | ||
/** | ||
* Props for Incoming Chat Bubble Type | ||
* | ||
* **`incomingOptions` are only applicable when `type` is `incoming` | ||
* | ||
* <pre style="font-family: monospace; font-size: 13px; background: #f8f8f8"> | ||
* IncomingOptionProps: { | ||
* children?: React.ReactNode; | ||
* time?: string | React.ReactText; | ||
* metaData?: string; | ||
* actionBar?: () => JSX.Element; | ||
* urgentMessage?: () => JSX.Element; | ||
* avatarData?: ChatAvatarProps; | ||
* showAvatar?: boolean; | ||
* } | ||
* | ||
* ChatAvatarProps: { | ||
* appearance?: AvatarAppearance; | ||
* firstName?: string; | ||
* lastName?: string; | ||
* role?: string; | ||
* tabIndex?: number; | ||
* icon?: React.ReactNode; | ||
* image?: React.ReactNode; | ||
* } | ||
* </pre> | ||
*/ | ||
incomingOptions?: IncomingOptionProps; | ||
/** | ||
* Props for Outgoing Chat Bubble Type | ||
* **`outgoingOptions` are only applicable when `type` is `outgoing` | ||
* <pre style="font-family: monospace; font-size: 13px; background: #f8f8f8"> | ||
* OutgoingOptionProps: { | ||
* metaData?: string; | ||
* status?: boolean; | ||
* failed?: boolean; | ||
* children?: React.ReactNode; | ||
* time?: string | React.ReactText; | ||
* actionBar?: () => JSX.Element; | ||
* urgentMessage?: () => JSX.Element; | ||
* failedMessage?: () => JSX.Element; | ||
* } | ||
* </pre> | ||
*/ | ||
outgoingOptions?: OutgoingOptionProps; | ||
/** | ||
* Elements to be rendered inside ChatBubble | ||
*/ | ||
children?: React.ReactNode; | ||
} | ||
|
||
export const ChatBubble = (props: ChatBubbleProps) => { | ||
const { type, incomingOptions, outgoingOptions, children, ...rest } = props; | ||
|
||
if (type === 'incoming') { | ||
return ( | ||
<IncomingBubble {...incomingOptions} {...rest}> | ||
{children} | ||
</IncomingBubble> | ||
); | ||
} | ||
|
||
return ( | ||
<OutgoingBubble {...outgoingOptions} {...rest}> | ||
{children} | ||
</OutgoingBubble> | ||
); | ||
}; | ||
|
||
ChatBubble.displayName = 'ChatBubble'; | ||
export default ChatBubble; |
144 changes: 144 additions & 0 deletions
144
core/components/molecules/chat/chatBubble/IncomingBubble.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import * as React from 'react'; | ||
import { AccentAppearance } from '@/common.type'; | ||
import styles from '@css/components/chatBubble.module.css'; | ||
import { Text, Avatar, Row, Column } from '@/index'; | ||
import classNames from 'classnames'; | ||
import { BaseProps } from '@/utils/types'; | ||
|
||
export interface ChatAvatarProps { | ||
appearance?: AccentAppearance; | ||
firstName?: string; | ||
lastName?: string; | ||
role?: string; | ||
tabIndex?: number; | ||
icon?: React.ReactNode; | ||
image?: React.ReactNode; | ||
} | ||
|
||
export interface IncomingOptionProps extends BaseProps { | ||
children?: React.ReactNode; | ||
time?: string | React.ReactText; | ||
metaData?: string; | ||
actionBar?: () => JSX.Element; | ||
urgentMessage?: () => JSX.Element; | ||
avatarData?: ChatAvatarProps; | ||
showAvatar?: boolean; | ||
} | ||
|
||
const MetaSeparator = () => ( | ||
<span className={styles['ChatBubble-separator']} data-test="DesignSystem-IncomingChatBubble-Separator" /> | ||
); | ||
|
||
export const IncomingBubble = (props: IncomingOptionProps) => { | ||
const { time, metaData, urgentMessage, avatarData = {}, showAvatar, children, actionBar, ...rest } = props; | ||
const { image, icon, firstName, lastName } = avatarData; | ||
const fullName = `${firstName ? firstName : ''} ${lastName ? lastName : ''}`; | ||
|
||
const [showActionBar, setShowActionBar] = React.useState(false); | ||
|
||
const showMetaRow = firstName || lastName || time || metaData || urgentMessage; | ||
|
||
const metaDataClass = classNames({ | ||
['d-flex align-items-center mb-3']: true, | ||
[styles['ChatBubble-metaData--incoming']]: showAvatar, | ||
}); | ||
|
||
return ( | ||
<div | ||
data-test="DesignSystem-ChatBubble-IncomingWrapper" | ||
{...rest} | ||
role="group" | ||
aria-labelledby="chat-bubble-header" | ||
> | ||
{showMetaRow && ( | ||
<Row className={metaDataClass} data-test="DesignSystem-IncomingChatBubble-MetaDataWrapper"> | ||
{[ | ||
fullName && (firstName || lastName) && ( | ||
<Text key="fullName" weight="medium" size="small" data-test="DesignSystem-IncomingChatBubble-Name"> | ||
{fullName} | ||
</Text> | ||
), | ||
time && ( | ||
<Text | ||
key="time" | ||
appearance="subtle" | ||
weight="medium" | ||
size="small" | ||
data-test="DesignSystem-IncomingChatBubble-Time" | ||
aria-label={`Time: ${time}`} | ||
> | ||
{time} | ||
</Text> | ||
), | ||
metaData && ( | ||
<Text | ||
key="metaData" | ||
appearance="subtle" | ||
weight="medium" | ||
size="small" | ||
data-test="DesignSystem-IncomingChatBubble-MetaData" | ||
aria-label={metaData} | ||
> | ||
{metaData} | ||
</Text> | ||
), | ||
urgentMessage && ( | ||
<div | ||
key="urgentMessage" | ||
data-test="DesignSystem-IncomingChatBubble-UrgentMessage" | ||
role="alert" | ||
aria-live="assertive" | ||
> | ||
{urgentMessage()} | ||
</div> | ||
), | ||
] | ||
.filter(Boolean) | ||
.map((element, index, array) => ( | ||
<React.Fragment key={index}> | ||
{element} | ||
{index < array.length - 1 && <MetaSeparator key={`separator-${index}`} />} | ||
</React.Fragment> | ||
))} | ||
</Row> | ||
)} | ||
<Row | ||
onMouseEnter={() => setShowActionBar(true)} | ||
onMouseLeave={() => setShowActionBar(false)} | ||
data-test="DesignSystem-IncomingChatBubble-Wrapper" | ||
> | ||
<Column className={styles['ChatBubble-boxWrapper']} data-test="DesignSystem-IncomingChatBubble-ChatBoxWrapper"> | ||
{showAvatar && ( | ||
<Avatar | ||
data-test="DesignSystem-IncomingChatBubble-Avatar" | ||
className="mr-4" | ||
{...avatarData} | ||
withTooltip={false} | ||
aria-label={`Avatar of ${fullName}`} | ||
> | ||
{image || icon} | ||
</Avatar> | ||
)} | ||
<div className={styles['ChatBubble-box--incoming']} data-test="DesignSystem-IncomingChatBubble-ChatBox"> | ||
{children} | ||
</div> | ||
</Column> | ||
<Column className={styles['ChatBubble-actionBarWrapper']}> | ||
{actionBar && showActionBar && ( | ||
<div | ||
data-test="DesignSystem-IncomingChatBubble-ActionBar" | ||
className={styles['ChatBubble-actionBar--incoming']} | ||
role="toolbar" | ||
aria-label="Action bar" | ||
> | ||
{actionBar()} | ||
</div> | ||
)} | ||
</Column> | ||
</Row> | ||
</div> | ||
); | ||
}; | ||
|
||
IncomingBubble.displayName = 'IncomingBubble'; | ||
export default IncomingBubble; |
Oops, something went wrong.