This workspace may contain up to 6 related repositories. Not all teams have all repos. Always be aware of which repository you're in when making changes.
| Repository | Type | Purpose | Typical Location |
|---|---|---|---|
| CRM.Omnichannel | Monorepo (Backend) | 20+ microservices for Omnichannel platform | <workspace-root>/CRM.Omnichannel/ |
| ConversationControl | Frontend (Agent UI) | Agent experience and conversation management UI | <workspace-root>/CRM.OmniChannel.ConversationControl/ |
| LiveChatWidget | Frontend (Customer) | Customer-facing chat widget | <workspace-root>/CRM.OmniChannel.LiveChatWidget/ |
| omnichannel-chat-sdk | Public SDK | TypeScript SDK for chat integration | <workspace-root>/omnichannel-chat-sdk/ |
| omnichannel-chat-widget | Public Components | React component library | <workspace-root>/omnichannel-chat-widget/ |
| omnichannel-amsclient | Shared Library | File upload/download client | <workspace-root>/omnichannel-amsclient/ |
- Purpose: Shared React component library for building chat widgets
- Type: TypeScript/React Library (npm package)
- Tech Stack: TypeScript, React 17+, Rollup, Jest, Storybook
- Distribution: npm registry (@microsoft/omnichannel-chat-widget)
- Consumers: CRM.OmniChannel.LiveChatWidget, external customers
What is omnichannel-chat-widget?
This is a React component library providing pre-built, customizable UI components for chat experiences. It includes message bubbles, input boxes, file attachments, typing indicators, and more. Components are theme-able and accessible (ARIA support).
Key Features:
- Pre-built React chat components
- Theme customization support
- Accessibility (ARIA attributes, keyboard navigation)
- TypeScript type definitions
- Storybook for component preview
Monorepo Structure:
This repo contains 2 npm packages:
- chat-components - Base components (buttons, icons, primitives)
- chat-widget - Higher-level chat-specific components
Integration:
- Peer dependency:
omnichannel-chat-sdk(for chat operations) - Peer dependency:
reactandreact-dom(17+ recommended) - Consumed by: LiveChatWidget, external customers building custom widgets
- Node.js (version in package.json engines)
- npm package manager
cd omnichannel-chat-widget
# Install dependencies (both packages)
npm installBuild:
- Build all packages:
npm run build- Build both chat-components and chat-widget - Build specific package:
npm run build:chat-componentsnpm run build:chat-widget
- Watch mode:
npm run watch- Incremental development
Test:
- Unit tests:
npm test- Jest tests for all packages - Test specific package:
npm run test:chat-componentsnpm run test:chat-widget
- Coverage:
npm run coverage- Test coverage report - Lint:
npm run lint- ESLint validation
Storybook:
- Start Storybook:
npm run storybook- Component preview at localhost:6006 - Build Storybook:
npm run build-storybook- Static site for deployment
Release:
- Publish:
npm run publish:packages- Publish both packages to npm - Version bump: Use lerna or npm workspaces for versioning
- Avoid
anytype - Use proper React prop types - Explicit prop types - Use interfaces for component props
- React Hooks - Prefer functional components with hooks
- Accessibility - Always include ARIA attributes
Example Component:
// ✅ CORRECT - Explicit prop types, accessibility
import React from 'react';
export interface MessageBubbleProps {
message: string;
sender: 'agent' | 'customer';
timestamp: Date;
onRetry?: () => void;
className?: string;
}
export const MessageBubble: React.FC<MessageBubbleProps> = ({
message,
sender,
timestamp,
onRetry,
className
}) => {
return (
<div
className={`message-bubble ${sender} ${className || ''}`}
role="article"
aria-label={`Message from ${sender} at ${timestamp.toLocaleTimeString()}`}
>
<div className="message-content">{message}</div>
<div className="message-timestamp" aria-hidden="true">
{timestamp.toLocaleTimeString()}
</div>
{onRetry && (
<button
onClick={onRetry}
aria-label="Retry sending message"
>
Retry
</button>
)}
</div>
);
};File organization:
chat-widget/src/
├── components/
│ ├── MessageBubble/
│ │ ├── MessageBubble.tsx # Component implementation
│ │ ├── MessageBubble.test.tsx # Jest tests
│ │ ├── MessageBubble.stories.tsx # Storybook stories
│ │ └── index.ts # Exports
│ └── ...
├── hooks/ # Custom React hooks
├── utils/ # Utility functions
└── index.ts # Package exports
Naming conventions:
- Components: PascalCase (e.g.,
MessageBubble,InputBox) - Props interfaces:
ComponentNameProps(e.g.,MessageBubbleProps) - Files: Match component name (e.g.,
MessageBubble.tsx) - Hooks: camelCase with
useprefix (e.g.,useTypingIndicator)
Unit Tests (Jest + React Testing Library):
- Location:
<ComponentName>.test.tsxfiles - Run:
npm test - Coverage target: >80% for components
Test Best Practices:
import { render, screen, fireEvent } from '@testing-library/react';
import { MessageBubble } from './MessageBubble';
describe('MessageBubble', () => {
it('should render message content', () => {
render(
<MessageBubble
message="Hello world"
sender="customer"
timestamp={new Date('2024-01-01T12:00:00')}
/>
);
expect(screen.getByText('Hello world')).toBeInTheDocument();
});
it('should call onRetry when retry button clicked', () => {
const onRetry = jest.fn();
render(
<MessageBubble
message="Failed message"
sender="customer"
timestamp={new Date()}
onRetry={onRetry}
/>
);
fireEvent.click(screen.getByLabelText('Retry sending message'));
expect(onRetry).toHaveBeenCalledTimes(1);
});
it('should have correct ARIA attributes', () => {
render(
<MessageBubble
message="Test"
sender="agent"
timestamp={new Date()}
/>
);
const bubble = screen.getByRole('article');
expect(bubble).toHaveAttribute('aria-label');
});
});Storybook Stories:
- Location:
<ComponentName>.stories.tsxfiles - Purpose: Visual testing, component documentation, design review
import type { Meta, StoryObj } from '@storybook/react';
import { MessageBubble } from './MessageBubble';
const meta: Meta<typeof MessageBubble> = {
title: 'Components/MessageBubble',
component: MessageBubble,
tags: ['autodocs'],
};
export default meta;
type Story = StoryObj<typeof MessageBubble>;
export const CustomerMessage: Story = {
args: {
message: 'Hello, I need help with my order',
sender: 'customer',
timestamp: new Date(),
},
};
export const AgentMessage: Story = {
args: {
message: 'Sure, I can help you with that!',
sender: 'agent',
timestamp: new Date(),
},
};
export const WithRetry: Story = {
args: {
message: 'Failed to send',
sender: 'customer',
timestamp: new Date(),
onRetry: () => console.log('Retry clicked'),
},
};Components should support theme customization:
// Theme interface
export interface ChatWidgetTheme {
primaryColor: string;
secondaryColor: string;
textColor: string;
backgroundColor: string;
fontFamily: string;
borderRadius: string;
}
// Theme context
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext<ChatWidgetTheme | undefined>(undefined);
export const useTheme = () => {
const theme = useContext(ThemeContext);
if (!theme) {
throw new Error('useTheme must be used within ThemeProvider');
}
return theme;
};
// Component using theme
export const ThemedButton: React.FC<ButtonProps> = ({ children, ...props }) => {
const theme = useTheme();
return (
<button
style={{
backgroundColor: theme.primaryColor,
color: theme.textColor,
borderRadius: theme.borderRadius,
fontFamily: theme.fontFamily,
}}
{...props}
>
{children}
</button>
);
};All components MUST be accessible:
- Semantic HTML: Use appropriate elements (
<button>,<input>, etc.) - ARIA attributes: Add
role,aria-label,aria-describedbywhere needed - Keyboard navigation: All interactive elements keyboard accessible
- Focus management: Logical tab order, visible focus indicators
- Screen reader support: Test with NVDA/JAWS/VoiceOver
Accessibility checklist:
- Component uses semantic HTML
- All interactive elements keyboard accessible (Tab, Enter, Space)
- ARIA roles and labels present
- Color contrast meets WCAG AA standards (4.5:1 for text)
- Focus indicators visible
- Tested with screen reader
This library integrates with:
- omnichannel-chat-sdk (peer dependency) - For chat operations (send message, etc.)
- React (peer dependency) - UI framework
Consumed by:
- CRM.OmniChannel.LiveChatWidget (npm dependency) - Customer chat widget
- External customers (public npm) - Custom widget implementations
When changing component APIs:
- This is a public contract - breaking changes affect external customers
- Use semantic versioning: major version for breaking changes
- Coordinate with LiveChatWidget team
- Update Storybook stories to reflect API changes
- Provide migration guide in CHANGELOG.md
- Code standards: Follow TypeScript best practices, React conventions
- Commit messages: Conventional commit format (feat:, fix:, chore:, etc.)
- Testing: All tests must pass, add tests for new components
- Storybook: Add stories for new components
- Accessibility: Verify all accessibility requirements met
- Documentation: Update README.md if component APIs change
- CHANGELOG: Update CHANGELOG.md under [Unreleased] section
Build Issues:
- Clear node_modules:
rm -rf node_modules && npm install - Check Node version:
node --version - Ensure both packages build:
npm run build
Storybook Issues:
- Clear Storybook cache:
npm run storybook -- --no-manager-cache - Check for circular dependencies
- Verify all stories have valid meta exports
Peer Dependency Warnings:
- Ensure React version compatibility (17+ recommended)
- Check chat-sdk peer dependency version in package.json
Accessibility Issues:
- Use axe-core for automated testing:
npm run test:a11y(if configured) - Manually test with keyboard navigation (Tab, Enter, Space)
- Test with screen reader (NVDA, JAWS, VoiceOver)
- README.md - Component library usage, examples
- CHANGE_LOG.md - Release history
- docs/ - Additional documentation
- Storybook: Run
npm run storybookfor interactive component docs
Before making breaking changes to component APIs:
-
Identify impact:
- LiveChatWidget dependency (check package.json version)
- External customers (check npm download stats)
-
Coordination:
- Notify LiveChatWidget team
- Create tracking work item
- Plan migration timeline (minimum 2 release cycles)
-
Implementation:
- Add new prop/API (backwards-compatible)
- Mark old prop as deprecated in JSDoc:
@deprecated Use newProp instead - Update Storybook stories with new API examples
- After 2 releases, remove old prop (major version bump)
-
Documentation:
- Update README.md with new component examples
- Add migration guide to CHANGE_LOG.md
- Update TypeScript type definitions
Summary: This is a public React component library with shared components for chat UIs. Focus on accessibility, theme customization, and Storybook documentation. Coordinate breaking changes with consumers (LiveChatWidget, external customers).