@kelet-ai/feedback-ui 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +444 -475
- package/dist/feedback-ui.es.js +660 -643
- package/dist/feedback-ui.umd.js +16 -16
- package/dist/hooks/feedback-state/index.d.ts +1 -0
- package/dist/hooks/feedback-state/types.d.ts +10 -2
- package/dist/hooks/feedback-state/use-feedback-reducer.d.ts +34 -0
- package/dist/hooks/feedback-state/use-state-change-tracking.d.ts +13 -0
- package/package.json +6 -3
- package/dist/README.stories.d.ts +0 -5
- package/dist/components/ui/button.d.ts +0 -10
- package/dist/components/ui/popover.d.ts +0 -7
- package/dist/components/ui/textarea.d.ts +0 -3
- package/dist/components/vote-feedback.stories.d.ts +0 -10
- package/dist/feedback-ui.es.d.ts +0 -56
- package/dist/hooks/feedback-state/use-feedback-state.stories.d.ts +0 -20
- package/dist/hooks/feedback-state/use-feedback-state.test.d.ts +0 -1
- package/dist/ui/shadcn/index.d.ts +0 -2
- package/dist/ui/shadcn/vote-feedback.d.ts +0 -13
- package/dist/ui/shadcn/vote-feedback.stories.d.ts +0 -11
- /package/dist/{feedback-ui.umd.d.ts → contexts/kelet.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
# Feedback UI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<!-- METADATA
|
|
4
|
+
**Project Type**: React Component and Hooks Library
|
|
5
|
+
**Primary Use Case**: User Feedback Collection. Especially great for learning from interactions with AI systems.
|
|
6
|
+
**Framework**: React 19+, TypeScript, Headless UI
|
|
7
|
+
**Installation**: `npm install @kelet-ai/feedback-ui` or with your preferred package manager. If using shadcn/ui, use `npx shadcn add https://feedback-ui.kelet.ai/r/vote-feedback.json` or with your preferred package manager.
|
|
8
|
+
-->
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
> **A headless React component to collect feedback for product and AI features.**
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
Perfect for capturing user reactions to AI-generated content, new features, documentation, and UI components with both
|
|
13
|
+
explicit voting interfaces and implicit behavior tracking.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 🚀 Quick Start
|
|
18
|
+
|
|
19
|
+
Get beautiful feedback components in 30 seconds with shadcn/ui integration:
|
|
8
20
|
|
|
9
21
|
```bash
|
|
10
22
|
npx shadcn add https://feedback-ui.kelet.ai/r/vote-feedback.json
|
|
@@ -24,590 +36,388 @@ function App() {
|
|
|
24
36
|
}
|
|
25
37
|
```
|
|
26
38
|
|
|
27
|
-
|
|
39
|
+
**Result**: Fully styled thumbs up/down buttons with popover feedback form.
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
---
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
- **AI responses** - Improve AI models with user feedback
|
|
33
|
-
- **Documentation** - Learn what content helps users
|
|
34
|
-
- **UI components** - Validate design decisions
|
|
43
|
+
## ✨ Why Feedback UI?
|
|
35
44
|
|
|
36
|
-
###
|
|
45
|
+
### **Perfect For**
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
- **🤖 AI Response Feedback** - Improve models with user reactions
|
|
48
|
+
- **🆕 Product Feature Validation** - Get user sentiment on new functionality
|
|
49
|
+
- **📚 Documentation Effectiveness** - Learn what content helps users
|
|
50
|
+
- **🎨 UI/UX Component Testing** - Validate design decisions
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
import { useFeedbackState } from '@kelet-ai/feedback-ui';
|
|
52
|
+
### **Key Benefits**
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
- **🎯 Headless Architecture** - Complete design control
|
|
55
|
+
- **♿ Accessibility First** - ARIA support, keyboard navigation, focus management
|
|
56
|
+
- **📊 Implicit Tracking** - Capture user behavior without explicit feedback prompts
|
|
57
|
+
- **🔒 TypeScript Native** - Full type safety included
|
|
58
|
+
- **⚡ Zero Dependencies** - Just React, no other deps
|
|
59
|
+
- **🎨 Flexible Styling** - Works with any CSS framework
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
<textarea
|
|
52
|
-
value={content}
|
|
53
|
-
onChange={e => setContent(e.target.value)}
|
|
54
|
-
placeholder="Edit this content..."
|
|
55
|
-
/>
|
|
56
|
-
);
|
|
57
|
-
// 🎯 Automatically sends feedback when user stops editing!
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Three Ways to Use
|
|
61
|
+
---
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
## 📋 Installation Methods
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
### **Option 1: shadcn/ui (Recommended)**
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
import { ShadcnVoteFeedback } from '@/components/ui/vote-feedback';
|
|
67
|
+
First [install shadcn/ui](https://ui.shadcn.com/docs/installation) and then:
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
onFeedback={handleFeedback}
|
|
73
|
-
variant="outline"
|
|
74
|
-
/>;
|
|
69
|
+
```bash
|
|
70
|
+
npx shadcn add https://feedback-ui.kelet.ai/r/vote-feedback.json
|
|
75
71
|
```
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
When you need complete design control, use the headless components.
|
|
73
|
+
✅ **Best for**: Quick setup with beautiful pre-styled components
|
|
80
74
|
|
|
81
|
-
|
|
75
|
+
### **Option 2: NPM Package**
|
|
82
76
|
|
|
83
|
-
```
|
|
77
|
+
```bash
|
|
84
78
|
npm install @kelet-ai/feedback-ui
|
|
85
79
|
```
|
|
86
80
|
|
|
87
|
-
|
|
81
|
+
✅ **Best for**: Full control over styling and behavior
|
|
88
82
|
|
|
89
|
-
|
|
90
|
-
import { VoteFeedback } from '@kelet-ai/feedback-ui';
|
|
91
|
-
|
|
92
|
-
<VoteFeedback.Root onFeedback={handleFeedback}>
|
|
93
|
-
<VoteFeedback.UpvoteButton className="your-styles">
|
|
94
|
-
👍 Like
|
|
95
|
-
</VoteFeedback.UpvoteButton>
|
|
96
|
-
<VoteFeedback.DownvoteButton className="your-styles">
|
|
97
|
-
👎 Dislike
|
|
98
|
-
</VoteFeedback.DownvoteButton>
|
|
99
|
-
<VoteFeedback.Popover className="your-popover">
|
|
100
|
-
<VoteFeedback.Textarea placeholder="What could be better?" />
|
|
101
|
-
<VoteFeedback.SubmitButton>Send</VoteFeedback.SubmitButton>
|
|
102
|
-
</VoteFeedback.Popover>
|
|
103
|
-
</VoteFeedback.Root>;
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## Features
|
|
107
|
-
|
|
108
|
-
- **Accessible**: Full keyboard navigation and ARIA support
|
|
109
|
-
- **TypeScript**: Complete type safety included
|
|
110
|
-
- **Flexible**: Works with any styling solution
|
|
111
|
-
- **Zero Dependencies**: Just React - no other dependencies
|
|
112
|
-
- **Automatic Feedback**: `useFeedbackState` hook captures implicit user behavior
|
|
113
|
-
|
|
114
|
-
## API Reference
|
|
83
|
+
---
|
|
115
84
|
|
|
116
|
-
|
|
85
|
+
## 🎯 Usage Patterns
|
|
117
86
|
|
|
118
|
-
|
|
119
|
-
| ---------------- | ---------- | -------- | ----------------------------------------------- |
|
|
120
|
-
| `identifier` | `string` | ✓ | Unique identifier for tracking |
|
|
121
|
-
| `onFeedback` | `function` | ✓ | Callback when feedback is submitted |
|
|
122
|
-
| `extra_metadata` | `object` | | Additional metadata to include |
|
|
123
|
-
| `trigger_name` | `string` | | Optional trigger name for categorizing feedback |
|
|
87
|
+
### **Pattern 1: Explicit Feedback (Most Common)**
|
|
124
88
|
|
|
125
|
-
|
|
89
|
+
Users explicitly vote and provide comments:
|
|
126
90
|
|
|
127
91
|
```tsx
|
|
128
|
-
{
|
|
129
|
-
identifier: string;
|
|
130
|
-
vote: 'upvote' | 'downvote';
|
|
131
|
-
explanation ? : string;
|
|
132
|
-
extra_metadata ? : object;
|
|
133
|
-
source ? : 'IMPLICIT' | 'EXPLICIT';
|
|
134
|
-
correction ? : string;
|
|
135
|
-
selection ? : string;
|
|
136
|
-
trigger_name ? : string; // Optional trigger name for categorizing feedback
|
|
137
|
-
}
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## useFeedbackState Hook
|
|
141
|
-
|
|
142
|
-
The `useFeedbackState` hook is a **drop-in replacement for React's useState** that automatically tracks state changes
|
|
143
|
-
and sends implicit feedback through the Kelet system. Perfect for capturing user behavior without explicit feedback
|
|
144
|
-
prompts.
|
|
92
|
+
import { VoteFeedback } from '@kelet-ai/feedback-ui';
|
|
145
93
|
|
|
146
|
-
|
|
94
|
+
<VoteFeedback.Root onFeedback={handleFeedback} identifier="ai-response">
|
|
95
|
+
<VoteFeedback.UpvoteButton>👍 Helpful</VoteFeedback.UpvoteButton>
|
|
96
|
+
<VoteFeedback.DownvoteButton>👎 Not helpful</VoteFeedback.DownvoteButton>
|
|
97
|
+
<VoteFeedback.Popover>
|
|
98
|
+
<VoteFeedback.Textarea placeholder="How can we improve?" />
|
|
99
|
+
<VoteFeedback.SubmitButton>Send feedback</VoteFeedback.SubmitButton>
|
|
100
|
+
</VoteFeedback.Popover>
|
|
101
|
+
</VoteFeedback.Root>;
|
|
102
|
+
```
|
|
147
103
|
|
|
148
|
-
|
|
149
|
-
- 🎯 **Automatic diff detection** - Only triggers on actual changes
|
|
150
|
-
- ⏱️ **Smart debouncing** - Prevents feedback spam (default: 1500ms)
|
|
151
|
-
- 📊 **Multiple diff formats** - Git, object, or JSON diff formats
|
|
152
|
-
- 🎭 **Dynamic identifiers** - Can derive identifier from state
|
|
153
|
-
- 🎚️ **Intelligent vote determination** - Automatic upvote/downvote based on change magnitude
|
|
154
|
-
- 🔍 **Custom comparison** - Support for custom equality functions
|
|
104
|
+
### **Pattern 2: Implicit State Tracking**
|
|
155
105
|
|
|
156
|
-
|
|
106
|
+
Capture user behavior automatically by using a drop-in replacement for useState:
|
|
157
107
|
|
|
158
108
|
```tsx
|
|
159
109
|
import { useFeedbackState } from '@kelet-ai/feedback-ui';
|
|
160
110
|
|
|
161
|
-
function
|
|
162
|
-
// Drop-in replacement
|
|
163
|
-
const [
|
|
111
|
+
function ContentEditor() {
|
|
112
|
+
// Drop-in useState replacement that tracks changes
|
|
113
|
+
const [content, setContent] = useFeedbackState(
|
|
114
|
+
'Start writing...',
|
|
115
|
+
'content-editor'
|
|
116
|
+
);
|
|
164
117
|
|
|
165
118
|
return (
|
|
166
|
-
<
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
119
|
+
<textarea
|
|
120
|
+
value={content}
|
|
121
|
+
onChange={e => setContent(e.target.value)}
|
|
122
|
+
placeholder="Edit this content..."
|
|
123
|
+
/>
|
|
170
124
|
);
|
|
125
|
+
// 🎯 Automatically sends feedback when user stops editing!
|
|
171
126
|
}
|
|
172
127
|
```
|
|
173
128
|
|
|
174
|
-
###
|
|
129
|
+
### **Pattern 3: Complex State with Reducer**
|
|
130
|
+
|
|
131
|
+
For advanced state management with automatic trigger tracking, by using a drop-in replacement for useReducer:
|
|
175
132
|
|
|
176
133
|
```tsx
|
|
177
|
-
import {
|
|
134
|
+
import { useFeedbackReducer } from '@kelet-ai/feedback-ui';
|
|
178
135
|
|
|
179
|
-
function
|
|
180
|
-
const [
|
|
181
|
-
{ name: '', email: '', preferences: {} },
|
|
182
|
-
state => `profile-${state.email}`, // Dynamic identifier
|
|
183
|
-
{
|
|
184
|
-
debounceMs: 2000, // Wait 2s before sending feedback
|
|
185
|
-
diffType: 'object', // Use object diff format
|
|
186
|
-
metadata: {
|
|
187
|
-
component: 'UserProfile',
|
|
188
|
-
version: '1.2.0',
|
|
189
|
-
},
|
|
190
|
-
compareWith: (a, b) => {
|
|
191
|
-
// Custom comparison function
|
|
192
|
-
return a.name === b.name && a.email === b.email;
|
|
193
|
-
},
|
|
194
|
-
}
|
|
195
|
-
);
|
|
136
|
+
function TodoApp() {
|
|
137
|
+
const [todos, dispatch] = useFeedbackReducer(todoReducer, [], 'todo-app');
|
|
196
138
|
|
|
197
139
|
return (
|
|
198
|
-
<
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
placeholder="Name"
|
|
203
|
-
/>
|
|
204
|
-
<input
|
|
205
|
-
value={profile.email}
|
|
206
|
-
onChange={e => setProfile({ ...profile, email: e.target.value })}
|
|
207
|
-
placeholder="Email"
|
|
208
|
-
/>
|
|
209
|
-
</form>
|
|
140
|
+
<button onClick={() => dispatch({ type: 'ADD_TODO', text: 'New task' })}>
|
|
141
|
+
Add Todo
|
|
142
|
+
</button>
|
|
143
|
+
// 🎯 Automatically sends feedback with trigger_name: 'ADD_TODO'
|
|
210
144
|
);
|
|
211
145
|
}
|
|
212
146
|
```
|
|
213
147
|
|
|
214
|
-
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 💡 Core Concepts
|
|
215
151
|
|
|
216
|
-
|
|
217
|
-
powerful feature allows you to understand the context and cause of state changes in your feedback data.
|
|
152
|
+
Understanding these fundamental concepts will help you implement feedback collection effectively:
|
|
218
153
|
|
|
219
|
-
|
|
154
|
+
### **🔑 Identifiers**
|
|
155
|
+
|
|
156
|
+
**What**: Unique tracking ID that connects feedback to its context
|
|
157
|
+
**Purpose**: Links feedback to specific sessions, users, or content pieces
|
|
158
|
+
**Best Practice**: Use traceable IDs from your logging system (session ID, trace ID, request ID)
|
|
220
159
|
|
|
221
160
|
```tsx
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
'content-editor',
|
|
225
|
-
{
|
|
226
|
-
default_trigger_name: 'manual_edit', // Default for all changes
|
|
227
|
-
}
|
|
228
|
-
);
|
|
161
|
+
// ✅ Good: Traceable identifier
|
|
162
|
+
<VoteFeedback.Root identifier="session-abc123-ai-response-456"/>
|
|
229
163
|
|
|
230
|
-
//
|
|
231
|
-
|
|
164
|
+
// ✅ Good: Content-based identifier
|
|
165
|
+
<VoteFeedback.Root identifier={`article-${articleId}-section-${sectionId}`}/>
|
|
232
166
|
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
setContent('Spell checker fixed this', 'spell_check');
|
|
167
|
+
// ❌ Poor: Generic identifier
|
|
168
|
+
<VoteFeedback.Root identifier="feedback"/>
|
|
236
169
|
```
|
|
237
170
|
|
|
238
|
-
|
|
171
|
+
### **📊 Feedback Sources**
|
|
239
172
|
|
|
240
|
-
|
|
173
|
+
Controls how feedback is collected and what data is captured:
|
|
241
174
|
|
|
242
|
-
|
|
243
|
-
const [draft, setDraft] = useFeedbackState('', 'email-draft');
|
|
175
|
+
#### **Explicit Feedback**
|
|
244
176
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
```
|
|
177
|
+
- **What**: Users actively vote and provide comments
|
|
178
|
+
- **When to use**: Direct user opinions, satisfaction surveys, feature ratings
|
|
179
|
+
- **Data captured**: Vote (upvote/downvote), comments, metadata
|
|
180
|
+
- **User experience**: Interactive buttons and forms
|
|
250
181
|
|
|
251
|
-
|
|
182
|
+
```tsx
|
|
183
|
+
// Explicit feedback - user clicks buttons
|
|
184
|
+
<VoteFeedback.Root onFeedback={handleExplicitFeedback}>
|
|
185
|
+
<VoteFeedback.UpvoteButton>👍 Helpful</VoteFeedback.UpvoteButton>
|
|
186
|
+
<VoteFeedback.DownvoteButton>👎 Not helpful</VoteFeedback.DownvoteButton>
|
|
187
|
+
</VoteFeedback.Root>
|
|
188
|
+
```
|
|
252
189
|
|
|
253
|
-
|
|
254
|
-
2. **ai_suggestion**: `'Hello world' → 'Hello world!'`
|
|
190
|
+
#### **Implicit Feedback**
|
|
255
191
|
|
|
256
|
-
|
|
192
|
+
- **What**: Automatic behavior tracking without user prompts
|
|
193
|
+
- **When to use**: Content editing, user interactions, workflow analysis
|
|
194
|
+
- **Data captured**: State diffs, interaction patterns, timing data
|
|
195
|
+
- **User experience**: Seamless, no interruption
|
|
257
196
|
|
|
258
197
|
```tsx
|
|
259
|
-
//
|
|
260
|
-
const [
|
|
261
|
-
|
|
262
|
-
'
|
|
263
|
-
{
|
|
264
|
-
default_trigger_name: 'user_input',
|
|
265
|
-
debounceMs: 2000,
|
|
266
|
-
}
|
|
198
|
+
// Implicit feedback - tracks changes automatically
|
|
199
|
+
const [content, setContent] = useFeedbackState(
|
|
200
|
+
'Initial content',
|
|
201
|
+
'content-editor'
|
|
267
202
|
);
|
|
203
|
+
// Sends feedback when user stops editing
|
|
204
|
+
```
|
|
268
205
|
|
|
269
|
-
|
|
270
|
-
setArticle({ ...article, title: 'My Article' }, 'user_input');
|
|
206
|
+
### **🏷️ Trigger Names**
|
|
271
207
|
|
|
272
|
-
|
|
273
|
-
setArticle(
|
|
274
|
-
{
|
|
275
|
-
...article,
|
|
276
|
-
content: 'AI generated introduction...',
|
|
277
|
-
},
|
|
278
|
-
'ai_generation'
|
|
279
|
-
);
|
|
208
|
+
Categorization system for grouping and analyzing feedback:
|
|
280
209
|
|
|
281
|
-
|
|
282
|
-
setArticle(
|
|
283
|
-
{
|
|
284
|
-
...article,
|
|
285
|
-
content: 'AI-generated introduction...',
|
|
286
|
-
},
|
|
287
|
-
'grammar_fix'
|
|
288
|
-
);
|
|
210
|
+
#### **Purpose**
|
|
289
211
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
content: 'AI-generated introduction with my changes...',
|
|
295
|
-
},
|
|
296
|
-
'user_refinement'
|
|
297
|
-
);
|
|
298
|
-
```
|
|
212
|
+
- **Group related feedback** for analysis
|
|
213
|
+
- **Track interaction types** (manual vs AI-assisted)
|
|
214
|
+
- **Measure feature adoption** and usage patterns
|
|
215
|
+
- **Debug user experience** issues
|
|
299
216
|
|
|
300
|
-
#### Common
|
|
217
|
+
#### **Common Patterns**
|
|
301
218
|
|
|
302
219
|
```tsx
|
|
303
|
-
// Content creation
|
|
304
|
-
|
|
305
|
-
default_trigger_name: 'manual_edit';
|
|
306
|
-
}
|
|
307
|
-
'user_typing' | 'copy_paste' | 'drag_drop';
|
|
308
|
-
|
|
309
|
-
// AI interactions
|
|
310
|
-
'ai_generation' | 'ai_completion' | 'ai_correction';
|
|
311
|
-
'prompt_result' | 'ai_suggestion_accepted';
|
|
220
|
+
// Content creation triggers
|
|
221
|
+
'user_typing' | 'ai_generation' | 'spell_check' | 'auto_format';
|
|
312
222
|
|
|
313
|
-
//
|
|
314
|
-
'
|
|
315
|
-
'validation_fix' | 'import_data';
|
|
223
|
+
// User interaction triggers
|
|
224
|
+
'manual_edit' | 'voice_input' | 'copy_paste' | 'drag_drop';
|
|
316
225
|
|
|
317
|
-
//
|
|
318
|
-
'voice_input' | 'gesture_input' | 'shortcut_key';
|
|
319
|
-
'context_menu' | 'toolbar_action';
|
|
320
|
-
|
|
321
|
-
// Workflow steps
|
|
226
|
+
// Workflow triggers
|
|
322
227
|
'draft' | 'review' | 'approval' | 'publication';
|
|
323
|
-
|
|
228
|
+
|
|
229
|
+
// AI interaction triggers
|
|
230
|
+
'ai_completion' | 'ai_correction' | 'prompt_result';
|
|
324
231
|
```
|
|
325
232
|
|
|
326
|
-
####
|
|
233
|
+
#### **Dynamic Trigger Names**
|
|
327
234
|
|
|
328
235
|
```tsx
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
);
|
|
236
|
+
const [document, setDocument] = useFeedbackState(
|
|
237
|
+
initialDoc,
|
|
238
|
+
'document-editor',
|
|
239
|
+
{ default_trigger_name: 'user_edit' }
|
|
240
|
+
);
|
|
335
241
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
const handleAIComplete = completion => {
|
|
341
|
-
setDocument(
|
|
342
|
-
{
|
|
343
|
-
...document,
|
|
344
|
-
content: document.content + completion,
|
|
345
|
-
},
|
|
346
|
-
'ai_completion'
|
|
347
|
-
);
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
const handleSpellCheck = correctedText => {
|
|
351
|
-
setDocument(
|
|
352
|
-
{
|
|
353
|
-
...document,
|
|
354
|
-
content: correctedText,
|
|
355
|
-
},
|
|
356
|
-
'spell_check'
|
|
357
|
-
);
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
const handleImport = importedData => {
|
|
361
|
-
setDocument(importedData, 'data_import');
|
|
362
|
-
};
|
|
363
|
-
}
|
|
242
|
+
// Different triggers for different actions
|
|
243
|
+
setDocument(aiGeneratedContent, 'ai_generation');
|
|
244
|
+
setDocument(userEditedContent, 'manual_refinement');
|
|
245
|
+
setDocument(spellCheckedContent, 'spell_check');
|
|
364
246
|
```
|
|
365
247
|
|
|
366
|
-
|
|
248
|
+
### **🔄 State Change Tracking**
|
|
249
|
+
|
|
250
|
+
For implicit feedback, understanding how state changes are processed:
|
|
251
|
+
|
|
252
|
+
#### **Debouncing Logic**
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
// User types: "Hello" → "Hello World" → "Hello World!"
|
|
256
|
+
// Only sends ONE feedback after user stops typing
|
|
257
|
+
const [text, setText] = useFeedbackState('', 'editor', {
|
|
258
|
+
debounceMs: 1500, // Wait 1.5s after last change
|
|
259
|
+
});
|
|
260
|
+
```
|
|
367
261
|
|
|
368
|
-
|
|
369
|
-
2. **Track User Behavior** - Understand how users interact with features
|
|
370
|
-
3. **Measure AI Impact** - See how often AI suggestions are used vs manual input
|
|
371
|
-
4. **Debug Issues** - Identify problematic interaction patterns
|
|
372
|
-
5. **Optimize UX** - Focus improvements on frequently used triggers
|
|
262
|
+
#### **Diff Calculation**
|
|
373
263
|
|
|
374
|
-
|
|
264
|
+
Three formats available for different use cases:
|
|
375
265
|
|
|
376
|
-
|
|
266
|
+
| Format | Best For | Output |
|
|
267
|
+
| -------- | ------------------- | ------------------- |
|
|
268
|
+
| `git` | Code/text changes | Unified diff format |
|
|
269
|
+
| `object` | Structured data | Deep object diff |
|
|
270
|
+
| `json` | Simple before/after | JSON comparison |
|
|
377
271
|
|
|
378
|
-
|
|
379
|
-
- **Correction patterns**: Are users frequently fixing AI suggestions?
|
|
380
|
-
- **Workflow efficiency**: Which triggers lead to the most refinements?
|
|
381
|
-
- **Feature adoption**: Are new features being used as intended?
|
|
272
|
+
#### **Vote Determination**
|
|
382
273
|
|
|
383
|
-
|
|
274
|
+
Automatic classification of changes:
|
|
384
275
|
|
|
385
276
|
```tsx
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
277
|
+
// Smart vote logic based on change magnitude
|
|
278
|
+
const [data, setData] = useFeedbackState(initial, 'tracker', {
|
|
279
|
+
vote: (before, after, diffPercentage) => {
|
|
280
|
+
// Small changes = refinement (positive)
|
|
281
|
+
if (diffPercentage <= 0.5) return 'upvote';
|
|
282
|
+
// Large changes = major revision (might indicate issues)
|
|
283
|
+
return 'downvote';
|
|
284
|
+
},
|
|
285
|
+
});
|
|
391
286
|
```
|
|
392
287
|
|
|
393
|
-
|
|
288
|
+
### **🎯 Best Practices Summary**
|
|
394
289
|
|
|
395
|
-
|
|
396
|
-
| -------------- | -------------------------------- | -------- | ------------------------------ |
|
|
397
|
-
| `initialState` | `T` | ✓ | Initial state value (any type) |
|
|
398
|
-
| `identifier` | `string \| (state: T) => string` | ✓ | Unique identifier for tracking |
|
|
399
|
-
| `options` | `DiffOptions<T>` | | Configuration options |
|
|
290
|
+
#### **Identifiers**
|
|
400
291
|
|
|
401
|
-
|
|
292
|
+
✅ Use traceable session/request IDs
|
|
293
|
+
✅ Include context in identifier structure
|
|
294
|
+
✅ Keep identifiers consistent across related actions
|
|
402
295
|
|
|
403
|
-
|
|
404
|
-
| ---------------------- | --------------------------------------- | --------------------- | ------------------------------------------------- |
|
|
405
|
-
| `debounceMs` | `number` | `1500` | Debounce time in milliseconds |
|
|
406
|
-
| `diffType` | `'git' \| 'object' \| 'json'` | `'git'` | Format for diff output |
|
|
407
|
-
| `compareWith` | `(a: T, b: T) => boolean` | | Custom equality comparison function |
|
|
408
|
-
| `metadata` | `Record<string, any>` | | Additional metadata to include |
|
|
409
|
-
| `onFeedback` | `(data: FeedbackData) => Promise<void>` | | Custom feedback handler (for testing) |
|
|
410
|
-
| `vote` | `'upvote' \| 'downvote' \| function` | | Static vote or custom function for determination |
|
|
411
|
-
| `default_trigger_name` | `string` | `'auto_state_change'` | Default trigger name when no trigger is specified |
|
|
296
|
+
#### **Feedback Sources**
|
|
412
297
|
|
|
413
|
-
|
|
298
|
+
✅ Use explicit feedback for user opinions
|
|
299
|
+
✅ Use implicit feedback for behavior analysis
|
|
300
|
+
✅ Combine both for comprehensive insights
|
|
414
301
|
|
|
415
|
-
|
|
416
|
-
2. **Debouncing**: Multiple rapid changes extend the timer (user is still editing)
|
|
417
|
-
3. **Diff Calculation**: Once changes stop, calculates the difference from start to final state
|
|
418
|
-
4. **Vote Determination**:
|
|
419
|
-
- **≤50% change** = `upvote` (minor refinement)
|
|
420
|
-
- **>50% change** = `downvote` (major revision)
|
|
421
|
-
5. **Automatic Feedback**: Sends implicit feedback with diff data in the `correction` field
|
|
302
|
+
#### **Trigger Names**
|
|
422
303
|
|
|
423
|
-
|
|
304
|
+
✅ Use consistent naming conventions
|
|
305
|
+
✅ Group related triggers with prefixes
|
|
306
|
+
✅ Document trigger meanings for your team
|
|
424
307
|
|
|
425
|
-
####
|
|
308
|
+
#### **Integration**
|
|
309
|
+
|
|
310
|
+
✅ Handle feedback data asynchronously
|
|
311
|
+
✅ Include relevant metadata for context
|
|
312
|
+
✅ Test both explicit and implicit flows
|
|
426
313
|
|
|
427
|
-
```
|
|
428
314
|
---
|
|
429
|
-
+++
|
|
430
|
-
@@ -1,1 +1,1 @@
|
|
431
|
-
-{"name": "John"}
|
|
432
|
-
+{"name": "John Doe"}
|
|
433
|
-
```
|
|
434
315
|
|
|
435
|
-
|
|
316
|
+
## 🔧 Core Components
|
|
436
317
|
|
|
437
|
-
|
|
438
|
-
[
|
|
439
|
-
{
|
|
440
|
-
"kind": "E",
|
|
441
|
-
"path": ["name"],
|
|
442
|
-
"lhs": "John",
|
|
443
|
-
"rhs": "John Doe"
|
|
444
|
-
}
|
|
445
|
-
]
|
|
446
|
-
```
|
|
318
|
+
### **VoteFeedback.Root**
|
|
447
319
|
|
|
448
|
-
|
|
320
|
+
Main container component that manages feedback state.
|
|
449
321
|
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
"
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
|
|
322
|
+
```tsx
|
|
323
|
+
<VoteFeedback.Root
|
|
324
|
+
identifier="unique-id" // Required: Unique tracking ID
|
|
325
|
+
onFeedback={handleFeedback} // Required: Callback function
|
|
326
|
+
trigger_name="user_feedback" // Optional: Categorization
|
|
327
|
+
extra_metadata={{ page: 'home' }} // Optional: Additional data
|
|
328
|
+
>
|
|
329
|
+
{/* Child components */}
|
|
330
|
+
</VoteFeedback.Root>
|
|
459
331
|
```
|
|
460
332
|
|
|
461
|
-
###
|
|
333
|
+
### **VoteFeedback.UpvoteButton / DownvoteButton**
|
|
462
334
|
|
|
463
|
-
|
|
464
|
-
- **Content creation** - Understand how users refine their writing
|
|
465
|
-
- **Configuration changes** - Monitor settings adjustments
|
|
466
|
-
- **Data visualization** - Capture how users explore and filter data
|
|
467
|
-
- **AI interactions** - Learn from user modifications to AI-generated content
|
|
335
|
+
Interactive voting buttons with built-in state management.
|
|
468
336
|
|
|
469
|
-
|
|
337
|
+
```tsx
|
|
338
|
+
<VoteFeedback.UpvoteButton className="your-styles">
|
|
339
|
+
👍 Like
|
|
340
|
+
</VoteFeedback.UpvoteButton>
|
|
341
|
+
```
|
|
470
342
|
|
|
471
|
-
|
|
343
|
+
### **VoteFeedback.Popover**
|
|
472
344
|
|
|
473
|
-
|
|
345
|
+
Context-aware feedback form that appears after voting.
|
|
474
346
|
|
|
475
347
|
```tsx
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
// Always downvote - useful for error tracking
|
|
482
|
-
const [errors, setErrors] = useFeedbackState([], 'error-log', {
|
|
483
|
-
vote: 'downvote',
|
|
484
|
-
});
|
|
348
|
+
<VoteFeedback.Popover className="your-popover-styles">
|
|
349
|
+
<VoteFeedback.Textarea placeholder="Tell us more..." />
|
|
350
|
+
<VoteFeedback.SubmitButton>Send</VoteFeedback.SubmitButton>
|
|
351
|
+
</VoteFeedback.Popover>
|
|
485
352
|
```
|
|
486
353
|
|
|
487
|
-
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## 📊 Automatic Feedback Hooks
|
|
357
|
+
|
|
358
|
+
### **useFeedbackState Hook**
|
|
359
|
+
|
|
360
|
+
A **drop-in replacement for React's useState** that automatically tracks state changes.
|
|
361
|
+
|
|
362
|
+
#### **Basic Usage**
|
|
488
363
|
|
|
489
364
|
```tsx
|
|
490
|
-
|
|
491
|
-
const [issue, setIssue] = useFeedbackState(
|
|
492
|
-
{ priority: 1, severity: 'low' },
|
|
493
|
-
'issue-tracker',
|
|
494
|
-
{
|
|
495
|
-
vote: (before, after, diffPercentage) => {
|
|
496
|
-
// Priority increase = positive change
|
|
497
|
-
if (after.priority > before.priority) return 'upvote';
|
|
498
|
-
if (after.priority < before.priority) return 'downvote';
|
|
499
|
-
|
|
500
|
-
// Severity increase = negative change
|
|
501
|
-
const severityOrder = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
502
|
-
const beforeSev = severityOrder[before.severity] || 1;
|
|
503
|
-
const afterSev = severityOrder[after.severity] || 1;
|
|
504
|
-
|
|
505
|
-
// Fall back to diff percentage for other cases
|
|
506
|
-
return afterSev > beforeSev
|
|
507
|
-
? 'downvote'
|
|
508
|
-
: diffPercentage > 0.7
|
|
509
|
-
? 'downvote'
|
|
510
|
-
: 'upvote';
|
|
511
|
-
},
|
|
512
|
-
}
|
|
513
|
-
);
|
|
365
|
+
const [count, setCount] = useFeedbackState(0, 'counter-widget');
|
|
514
366
|
```
|
|
515
367
|
|
|
516
|
-
#### Advanced
|
|
368
|
+
#### **Advanced Configuration**
|
|
517
369
|
|
|
518
370
|
```tsx
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
'article-editor',
|
|
523
|
-
{
|
|
524
|
-
vote: (before, after, diffPercentage) => {
|
|
525
|
-
// More tags = improvement
|
|
526
|
-
if (after.tags.length > before.tags.length) return 'upvote';
|
|
527
|
-
|
|
528
|
-
// Significant content expansion = improvement
|
|
529
|
-
const contentGrowth =
|
|
530
|
-
after.content.length / Math.max(before.content.length, 1);
|
|
531
|
-
if (contentGrowth > 1.2) return 'upvote';
|
|
532
|
-
|
|
533
|
-
// Major changes might indicate problems
|
|
534
|
-
return diffPercentage > 0.6 ? 'downvote' : 'upvote';
|
|
535
|
-
},
|
|
536
|
-
}
|
|
537
|
-
);
|
|
538
|
-
|
|
539
|
-
// User experience tracking
|
|
540
|
-
const [settings, setSettings] = useFeedbackState(
|
|
541
|
-
{ theme: 'light', notifications: true, autoSave: false },
|
|
542
|
-
'user-preferences',
|
|
371
|
+
const [profile, setProfile] = useFeedbackState(
|
|
372
|
+
{ name: '', email: '' },
|
|
373
|
+
state => `profile-${state.email}`, // Dynamic identifier
|
|
543
374
|
{
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
// Disabling features might indicate UX issues
|
|
550
|
-
if (!after.autoSave && before.autoSave) return 'downvote';
|
|
551
|
-
|
|
552
|
-
// Small tweaks are usually positive
|
|
553
|
-
return diffPercentage < 0.3 ? 'upvote' : 'downvote';
|
|
554
|
-
},
|
|
375
|
+
debounceMs: 2000, // Wait time before sending feedback
|
|
376
|
+
diffType: 'object', // Format: 'git' | 'object' | 'json'
|
|
377
|
+
metadata: { component: 'UserProfile' },
|
|
378
|
+
vote: 'upvote', // Static vote or custom function
|
|
555
379
|
}
|
|
556
380
|
);
|
|
557
381
|
```
|
|
558
382
|
|
|
559
|
-
|
|
383
|
+
#### **Trigger Names for Categorization**
|
|
560
384
|
|
|
561
385
|
```tsx
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
'
|
|
386
|
+
const [content, setContent] = useFeedbackState(
|
|
387
|
+
'Initial content',
|
|
388
|
+
'content-editor',
|
|
389
|
+
{ default_trigger_name: 'manual_edit' }
|
|
566
390
|
);
|
|
567
391
|
|
|
568
|
-
//
|
|
569
|
-
|
|
570
|
-
[],
|
|
571
|
-
items => `shopping-cart-${items.length}-items`
|
|
572
|
-
);
|
|
392
|
+
// Uses default trigger
|
|
393
|
+
setContent('User typed this');
|
|
573
394
|
|
|
574
|
-
//
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
{ debounceMs: 800 } // Shorter for search
|
|
579
|
-
);
|
|
395
|
+
// Override with specific trigger
|
|
396
|
+
setContent('AI generated this', 'ai_assistance');
|
|
397
|
+
setContent('Spell checker fixed this', 'spell_check');
|
|
398
|
+
```
|
|
580
399
|
|
|
581
|
-
|
|
582
|
-
const [formData, setFormData] = useFeedbackState(initialForm, 'contact-form', {
|
|
583
|
-
vote: (before, after, diffPercentage) => {
|
|
584
|
-
// Form completion = positive signal
|
|
585
|
-
const beforeFields = Object.values(before).filter(Boolean).length;
|
|
586
|
-
const afterFields = Object.values(after).filter(Boolean).length;
|
|
587
|
-
return afterFields > beforeFields ? 'upvote' : 'downvote';
|
|
588
|
-
},
|
|
589
|
-
});
|
|
400
|
+
### **useFeedbackReducer Hook**
|
|
590
401
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
402
|
+
A **drop-in replacement for React's useReducer** with automatic trigger name extraction from action types.
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
const [state, dispatch] = useFeedbackReducer(
|
|
406
|
+
counterReducer,
|
|
407
|
+
{ count: 0 },
|
|
408
|
+
'counter-widget'
|
|
596
409
|
);
|
|
597
410
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
// Don't make it too complicated - keep it readable!
|
|
602
|
-
// Complex business logic should be in separate functions
|
|
603
|
-
return determineVoteFromBusinessLogic(before, after, diffPercentage);
|
|
604
|
-
},
|
|
605
|
-
});
|
|
411
|
+
dispatch({ type: 'increment' }); // trigger_name: 'increment'
|
|
412
|
+
dispatch({ type: 'reset' }); // trigger_name: 'reset'
|
|
413
|
+
dispatch({ type: 'custom' }, 'override'); // Custom trigger name
|
|
606
414
|
```
|
|
607
415
|
|
|
608
|
-
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## 🎨 Examples
|
|
609
419
|
|
|
610
|
-
### Basic
|
|
420
|
+
### **Basic Voting Interface**
|
|
611
421
|
|
|
612
422
|
```tsx
|
|
613
423
|
<VoteFeedback.Root onFeedback={feedback => console.log(feedback)}>
|
|
@@ -616,7 +426,7 @@ const [data, setData] = useFeedbackState({}, 'complex-data', {
|
|
|
616
426
|
</VoteFeedback.Root>
|
|
617
427
|
```
|
|
618
428
|
|
|
619
|
-
###
|
|
429
|
+
### **Styled with Custom CSS**
|
|
620
430
|
|
|
621
431
|
```tsx
|
|
622
432
|
<VoteFeedback.Root onFeedback={handleFeedback}>
|
|
@@ -635,51 +445,210 @@ const [data, setData] = useFeedbackState({}, 'complex-data', {
|
|
|
635
445
|
</VoteFeedback.Root>
|
|
636
446
|
```
|
|
637
447
|
|
|
638
|
-
###
|
|
448
|
+
### **Polymorphic Components with asChild**
|
|
449
|
+
|
|
450
|
+
```tsx
|
|
451
|
+
<VoteFeedback.UpvoteButton asChild>
|
|
452
|
+
<button className="custom-button">
|
|
453
|
+
<Icon name="thumbs-up" />
|
|
454
|
+
Like
|
|
455
|
+
</button>
|
|
456
|
+
</VoteFeedback.UpvoteButton>
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### **AI Response Feedback**
|
|
639
460
|
|
|
640
461
|
```tsx
|
|
641
462
|
<VoteFeedback.Root
|
|
463
|
+
identifier="ai-response-123"
|
|
642
464
|
onFeedback={handleFeedback}
|
|
643
|
-
|
|
644
|
-
|
|
465
|
+
trigger_name="ai_evaluation"
|
|
466
|
+
extra_metadata={{
|
|
467
|
+
model: 'gpt-4',
|
|
468
|
+
prompt_length: 150,
|
|
469
|
+
response_time: 1200,
|
|
470
|
+
}}
|
|
645
471
|
>
|
|
646
472
|
<VoteFeedback.UpvoteButton>👍 Helpful</VoteFeedback.UpvoteButton>
|
|
647
473
|
<VoteFeedback.DownvoteButton>👎 Not helpful</VoteFeedback.DownvoteButton>
|
|
648
474
|
<VoteFeedback.Popover>
|
|
649
|
-
<VoteFeedback.Textarea placeholder="How can we improve?" />
|
|
475
|
+
<VoteFeedback.Textarea placeholder="How can we improve this response?" />
|
|
650
476
|
<VoteFeedback.SubmitButton>Send feedback</VoteFeedback.SubmitButton>
|
|
651
477
|
</VoteFeedback.Popover>
|
|
652
478
|
</VoteFeedback.Root>
|
|
653
479
|
```
|
|
654
480
|
|
|
655
|
-
|
|
481
|
+
---
|
|
482
|
+
|
|
483
|
+
## 📖 API Reference
|
|
484
|
+
|
|
485
|
+
### **Feedback Data Object**
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
interface FeedbackData {
|
|
489
|
+
identifier: string; // Unique tracking ID
|
|
490
|
+
vote: 'upvote' | 'downvote'; // User's vote
|
|
491
|
+
explanation?: string; // Optional user comment
|
|
492
|
+
extra_metadata?: Record<string, any>; // Additional context data
|
|
493
|
+
source?: 'IMPLICIT' | 'EXPLICIT'; // How feedback was collected
|
|
494
|
+
correction?: string; // For implicit feedback diffs
|
|
495
|
+
selection?: string; // Selected text context
|
|
496
|
+
trigger_name?: string; // Categorization tag
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### **VoteFeedback.Root Props**
|
|
501
|
+
|
|
502
|
+
| Prop | Type | Required | Description |
|
|
503
|
+
| ---------------- | ------------------------------ | -------- | ----------------------------------- |
|
|
504
|
+
| `identifier` | `string` | ✅ | Unique identifier for tracking |
|
|
505
|
+
| `onFeedback` | `(data: FeedbackData) => void` | ✅ | Callback when feedback is submitted |
|
|
506
|
+
| `trigger_name` | `string` | ❌ | Optional categorization tag |
|
|
507
|
+
| `extra_metadata` | `object` | ❌ | Additional context data |
|
|
508
|
+
|
|
509
|
+
### **Hook Options**
|
|
510
|
+
|
|
511
|
+
#### **useFeedbackState Options**
|
|
512
|
+
|
|
513
|
+
| Option | Type | Default | Description |
|
|
514
|
+
| ---------------------- | ------------------------------------ | --------------------- | ----------------------------- |
|
|
515
|
+
| `debounceMs` | `number` | `1500` | Debounce time in milliseconds |
|
|
516
|
+
| `diffType` | `'git' \| 'object' \| 'json'` | `'git'` | Diff output format |
|
|
517
|
+
| `compareWith` | `(a: T, b: T) => boolean` | `undefined` | Custom equality function |
|
|
518
|
+
| `metadata` | `Record<string, any>` | `{}` | Additional metadata |
|
|
519
|
+
| `vote` | `'upvote' \| 'downvote' \| function` | `auto` | Vote determination logic |
|
|
520
|
+
| `default_trigger_name` | `string` | `'auto_state_change'` | Default trigger name |
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## ♿ Accessibility
|
|
525
|
+
|
|
526
|
+
✅ **Full keyboard navigation support**
|
|
527
|
+
✅ **ARIA labels and roles**
|
|
528
|
+
✅ **Focus management**
|
|
529
|
+
✅ **Screen reader compatible**
|
|
530
|
+
✅ **High contrast support**
|
|
531
|
+
✅ **Reduced motion respect**
|
|
532
|
+
|
|
533
|
+
### **Keyboard Shortcuts**
|
|
534
|
+
|
|
535
|
+
- `Tab` / `Shift+Tab` - Navigate between elements
|
|
536
|
+
- `Enter` / `Space` - Activate buttons
|
|
537
|
+
- `Escape` - Close popover
|
|
538
|
+
- `Arrow Keys` - Navigate within popover
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## 🛠 Development
|
|
543
|
+
|
|
544
|
+
### **Setup**
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
bun install # Install dependencies
|
|
548
|
+
bun dev # Start Storybook development server
|
|
549
|
+
bun run checks # Run all quality checks
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### **Testing**
|
|
553
|
+
|
|
554
|
+
```bash
|
|
555
|
+
bun run test:unit # Run unit tests
|
|
556
|
+
bun run test:storybook # Run Storybook interaction tests
|
|
557
|
+
bun run test:storybook:coverage # Run with coverage
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### **Building**
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
bun build # Build library for production
|
|
564
|
+
bun run typecheck # TypeScript type checking
|
|
565
|
+
bun run lint # ESLint code quality checks
|
|
566
|
+
bun run prettier # Code formatting
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### **Quality Checks**
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
bun run checks # Run all quality checks (lint, format, typecheck, tests)
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
---
|
|
576
|
+
|
|
577
|
+
## ❓ Troubleshooting
|
|
578
|
+
|
|
579
|
+
### **Common Issues**
|
|
580
|
+
|
|
581
|
+
#### **Q: Feedback not triggering**
|
|
582
|
+
|
|
583
|
+
```tsx
|
|
584
|
+
// ❌ Missing required props
|
|
585
|
+
<VoteFeedback.Root>
|
|
586
|
+
<VoteFeedback.UpvoteButton>👍</VoteFeedback.UpvoteButton>
|
|
587
|
+
</VoteFeedback.Root>
|
|
588
|
+
|
|
589
|
+
// ✅ Include required identifier and onFeedback
|
|
590
|
+
<VoteFeedback.Root
|
|
591
|
+
identifier="my-feature"
|
|
592
|
+
onFeedback={handleFeedback}
|
|
593
|
+
>
|
|
594
|
+
<VoteFeedback.UpvoteButton>👍</VoteFeedback.UpvoteButton>
|
|
595
|
+
</VoteFeedback.Root>
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
#### **Q: TypeScript errors with custom components**
|
|
656
599
|
|
|
657
600
|
```tsx
|
|
601
|
+
// ✅ Use asChild for custom components
|
|
658
602
|
<VoteFeedback.UpvoteButton asChild>
|
|
659
|
-
<
|
|
660
|
-
<Icon name="thumbs-up" />
|
|
661
|
-
Like
|
|
662
|
-
</button>
|
|
603
|
+
<MyCustomButton>Like</MyCustomButton>
|
|
663
604
|
</VoteFeedback.UpvoteButton>
|
|
664
605
|
```
|
|
665
606
|
|
|
666
|
-
|
|
607
|
+
#### **Q: useFeedbackState not sending feedback**
|
|
667
608
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
609
|
+
```tsx
|
|
610
|
+
// ✅ Ensure debounce time has passed(the time, not the configuration! :P) and state actually changed
|
|
611
|
+
const [value, setValue] = useFeedbackState('initial', 'test', {
|
|
612
|
+
debounceMs: 1000, // Wait 1 second after last change
|
|
613
|
+
});
|
|
614
|
+
```
|
|
672
615
|
|
|
673
|
-
|
|
616
|
+
#### **Q: Styling not working**
|
|
674
617
|
|
|
675
|
-
```
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
bun build # Build library
|
|
618
|
+
```tsx
|
|
619
|
+
// ✅ Components are unstyled by default - add your own CSS or use the Shadcn UI library
|
|
620
|
+
<VoteFeedback.UpvoteButton className="bg-green-500 text-white p-2">
|
|
621
|
+
👍 Like
|
|
622
|
+
</VoteFeedback.UpvoteButton>
|
|
681
623
|
```
|
|
682
624
|
|
|
625
|
+
### **Best Practices**
|
|
626
|
+
|
|
627
|
+
✅ **Use unique identifiers** for each feedback instance - the identifier should be traceable back to the session's log
|
|
628
|
+
and allow us to understand the context of the feedback.
|
|
629
|
+
✅ **Handle feedback data asynchronously** in your callback
|
|
630
|
+
✅ **Test keyboard navigation** in your implementation
|
|
631
|
+
✅ **Provide meaningful trigger names** for categorization
|
|
632
|
+
✅ **Include relevant metadata** for context
|
|
633
|
+
|
|
634
|
+
❌ **Don't use the same identifier** for multiple components. An identifier should be traced back to the session's log -
|
|
635
|
+
allows us to understand the context of the feedback.
|
|
636
|
+
|
|
637
|
+
---
|
|
638
|
+
|
|
639
|
+
## 📚 Additional Resources
|
|
640
|
+
|
|
641
|
+
**📖 Full Documentation**: [https://feedback-ui.kelet.ai/](https://feedback-ui.kelet.ai/)
|
|
642
|
+
**🎮 Interactive Examples**: [Storybook Documentation](https://feedback-ui.kelet.ai/storybook/)
|
|
643
|
+
**🐛 Report Issues**: [GitHub Issues](https://github.com/kelet-ai/feedback-ui/issues)
|
|
644
|
+
**💬 Discussions**: [GitHub Discussions](https://github.com/kelet-ai/feedback-ui/discussions)
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
683
648
|
## License
|
|
684
649
|
|
|
685
|
-
MIT
|
|
650
|
+
**MIT License** - See [LICENSE](LICENSE) file for details.
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
_Built with ❤️ by the [Kelet AI](https://kelet.ai) team_
|