Skip to content
Merged
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
367 changes: 367 additions & 0 deletions widgets-samples/cc/samples-cc-react-app/ai-docs/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,367 @@
# React Sample App - Widget Integration Guide

## Purpose

Demonstrates how to integrate Contact Center widgets into a React application. Use this as a reference when adding new widgets.

## Design System

- **Framework:** React 18.3.1 + TypeScript
- **UI Library:** Momentum Design System (`@momentum-design/components`)
- **State:** MobX store (`@webex/cc-store`)
- **Theme:** Dark/Light mode toggle (persisted in localStorage)
- **Layout:** CSS Grid with `.box`, `.section-box`, `.fieldset` structure

## Critical Integration Pattern

**Follow this exact pattern for ALL new widgets:**

### Step 1: Import the Widget

```tsx
import {NewWidget} from '@webex/cc-widgets';
```

### Step 2: Add to defaultWidgets

```tsx
const defaultWidgets = {
stationLogin: true,
userState: true,
// ... existing widgets
newWidget: false, // ← Add here (false by default for user opt-in)
};
```

### Step 3: Add Checkbox for Widget Selection

```tsx
<Checkbox
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The checkboxes in the sample app currently have aria-label for accessibility, could add that as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated

onChange={handleCheckboxChange}
name="newWidget"
checked={selectedWidgets.newWidget}
htmlId="newWidget-checkbox"
aria-label="new widget checkbox"
>
<Text>New Widget</Text>
</Checkbox>
```

### Step 4: Conditional Rendering with Standard Layout

```tsx
{
selectedWidgets.newWidget && (
<div className="box">
<section className="section-box">
<fieldset className="fieldset">
<legend className="legend-box">New Widget</legend>
<NewWidget onEvent={(data) => handleNewWidgetEvent(data)} onError={(error) => onError('NewWidget', error)} />
</fieldset>
</section>
</div>
);
}
```

### Step 5: Add callbacks (if needed)

```tsx
const handleNewWidgetCallback = (data) => {
console.log('New widget callback:', data);
// Handle callback logic
};
```

## Layout Structure Rules

### Container Hierarchy (ALWAYS use this)

```tsx
<div className="box">
{' '}
{/* Outer container with background */}
<section className="section-box">
{' '}
{/* Inner section with padding */}
<fieldset className="fieldset">
{' '}
{/* Fieldset for grouping */}
<legend className="legend-box">Title</legend> {/* Title/legend */}
<WidgetComponent /> {/* Actual widget */}
</fieldset>
</section>
</div>
```

### Why this structure?

- `box` - Consistent spacing and background
- `section-box` - Momentum Design padding
- `fieldset` - Semantic grouping
- `legend-box` - Styled title
- **Result:** Visual consistency across all widgets

## Styling Rules

### CSS Variables (MUST USE)

```scss
// Colors
var(--mds-color-theme-text-primary-normal)
var(--mds-color-theme-background-solid-primary-normal)
var(--mds-color-theme-background-primary-normal)

// Spacing
var(--mds-spacing-1) // 0.25rem (4px)
var(--mds-spacing-2) // 0.5rem (8px)
var(--mds-spacing-3) // 1rem (16px)
var(--mds-spacing-4) // 1.5rem (24px)

// Typography
var(--mds-font-size-body-small)
var(--mds-font-size-body-medium)
```

### ❌ NEVER Do This

```tsx
<div style={{color: '#FF0000'}}> // Hardcoded color
<div style={{padding: '10px'}}> // Hardcoded spacing
```

### ✅ ALWAYS Do This

```tsx
<div style={{
color: 'var(--mds-color-theme-text-primary-normal)',
padding: 'var(--mds-spacing-3)'
}}>
```

## Event Handling Pattern

### Standard onError Handler

```tsx
const onError = (widgetName: string, error: Error) => {
console.error(`${widgetName} error:`, error);
// Optional: Show toast notification
setToast({type: 'error'});
};
```

**EVERY widget MUST have onError callback.**

### Widget-Specific Events

```tsx
// IncomingTask
const onIncomingTaskCB = ({task}) => {
console.log('Incoming task:', task);
setIncomingTasks((prev) => [...prev, task]);
playNotificationSound(); // Custom logic
};

// UserState
const onAgentStateChangedCB = (newState: AgentState, oldState: AgentState) => {
console.log('State changed from', oldState, 'to', newState);
setSelectedState(newState);
};

// CallControl
const onRecordingToggleCB = ({isRecording, task}) => {
console.log('Recording:', isRecording, 'for task:', task.data.interactionId);
};
```

## Theme Integration

Theme is controlled by `@momentum-ui`'s `ThemeProvider`:

```tsx
import {ThemeProvider} from '@momentum-design/components/dist/react';

<ThemeProvider
themeclass={store.currentTheme === 'LIGHT' ? 'mds-theme-stable-lightWebex' : 'mds-theme-stable-darkWebex'}
>
{/* Your widgets */}
</ThemeProvider>;
```

- **Theme provider**: `@momentum-ui` library manages the theme through `ThemeProvider`
- **Theme storage**: `store.currentTheme` stores the theme as a string ('LIGHT' or 'DARK')
- **Theme classes**: Theme class names (`mds-theme-stable-lightWebex` / `mds-theme-stable-darkWebex`) are passed to ThemeProvider
- **Automatic updates**: All widgets automatically respond to theme changes through the ThemeProvider context

**User can toggle theme via UI checkbox** - widgets update automatically through the provider.

## State Management

### When to Use store Directly

```tsx
// Access store for global state
import {store} from '@webex/cc-widgets';

// Examples:
store.currentTask; // Current active task
store.taskList; // All tasks
store.incomingTask; // Incoming task
store.agentState; // Current agent state
```

### When to Use Local State

```tsx
// UI-only state (no widget dependency)
const [showPopup, setShowPopup] = useState(false);
const [selectedOption, setSelectedOption] = useState('');
```

## Complete Example: Adding a New Widget

```tsx
// 1. Import
import {NewAwesomeWidget} from '@webex/cc-widgets';

// 2. Add to defaultWidgets
const defaultWidgets = {
// ... existing
newAwesomeWidget: false,
};

// 3. Checkbox in widget selector
<Checkbox
onChange={handleCheckboxChange}
name="newAwesomeWidget"
checked={selectedWidgets.newAwesomeWidget}
htmlId="newAwesomeWidget-checkbox"
>
<Text>New Awesome Widget</Text>
</Checkbox>;

// 4. Setup callback handler (if needed)
const handleCallback = (data) => {
console.log('Callback:', data);
};

// 5. Render with standard layout
{
selectedWidgets.newAwesomeWidget && (
<div className="box">
<section className="section-box">
<fieldset className="fieldset">
<legend className="legend-box">New Awesome Widget</legend>
<NewAwesomeWidget
handleCallback={handleCallback}
onError={(error) => onError('NewAwesomeWidget', error)}
customProp={someValue}
/>
</fieldset>
</section>
</div>
);
}
```

## Common Mistakes to AVOID

### ❌ Breaking CSS class structure

```tsx
// WRONG
<div>
<NewWidget />
</div>
```

### ✅ Correct

```tsx
<div className="box">
<section className="section-box">
<fieldset className="fieldset">
<legend className="legend-box">Widget Name</legend>
<NewWidget />
</fieldset>
</section>
</div>
```

### ❌ Forgetting defaultWidgets entry

```tsx
// WRONG - Widget renders immediately, user can't disable
{
selectedWidgets.newWidget && <NewWidget />;
}
// But newWidget not in defaultWidgets!
```

### ✅ Correct

```tsx
// In defaultWidgets
const defaultWidgets = {
newWidget: false, // ← MUST ADD HERE
};

// Then render
{
selectedWidgets.newWidget && <NewWidget />;
}
```

### ❌ Missing error handler

```tsx
// WRONG
<NewWidget onEvent={handleEvent} />
```

### ✅ Correct

```tsx
<NewWidget onEvent={handleEvent} onError={(error) => onError('NewWidget', error)} />
```

### ❌ Hardcoding colors

```tsx
// WRONG
<div style={{backgroundColor: '#1a1a1a'}}>
```

### ✅ Correct

```tsx
<div style={{backgroundColor: 'var(--mds-color-theme-background-primary-normal)'}}>
```

## Testing Checklist

After adding a new widget:

- [ ] Widget imports without errors
- [ ] Appears in widget selector checkbox list
- [ ] Can be enabled/disabled via checkbox
- [ ] Selection persists in localStorage
- [ ] Renders with correct layout (box > section-box > fieldset)
- [ ] Has legend/title
- [ ] Uses Momentum CSS variables (no hardcoded colors)
- [ ] Callbacks are handled correctly
- [ ] onError handler present and logs errors
- [ ] Works in both light and dark themes
- [ ] No console errors when enabled/disabled
- [ ] No visual/layout breaking when rendered alongside other widgets

## File Locations

- **Main App:** `src/App.tsx`
- **Styles:** `src/App.scss`

## Additional Resources

- [MobX Store Package](../../packages/contact-center/store/ai-docs/agent.md)
- [cc-widgets Package](../../packages/contact-center/cc-widgets/ai-docs/agent.md)
Loading