-
Notifications
You must be signed in to change notification settings - Fork 65
docs(samples-cc-react-app): add AI documentation - AGENTS.md #594
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
a52fefc
docs(samples-cc-react-app): add AI documentation - AGENTS.md
7e12e58
docs(ai-docs): review
Shreyas281299 98bb365
Merge branch 'next' into ai-docs-samples
Shreyas281299 ef2c049
docs(samples): review comments
Shreyas281299 996f899
Merge remote-tracking branch 'origin/ai-docs-samples' into ai-docs-sa…
Shreyas281299 47ba206
Merge branch 'next' into ai-docs-samples
Shreyas281299 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
367 changes: 367 additions & 0 deletions
367
widgets-samples/cc/samples-cc-react-app/ai-docs/AGENTS.md
This file contains hidden or 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,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 | ||
| 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) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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-labelfor accessibility, could add that as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated