quick-bug-reporter-react 1.4.0 → 1.5.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/dist/index.d.cts CHANGED
@@ -92,6 +92,10 @@ type BugReporterSubmitOptions = {
92
92
  consoleLogs?: ConsoleLogEntry[];
93
93
  jsErrors?: CapturedJsError[];
94
94
  onProgress?: SubmitProgressCallback;
95
+ stepsToReproduce?: string;
96
+ expectedResult?: string;
97
+ actualResult?: string;
98
+ additionalContext?: string;
95
99
  };
96
100
  declare class BugReporter {
97
101
  private integration;
@@ -153,7 +157,12 @@ type BugReporterContextValue = {
153
157
  screenshotHighlightCount: number;
154
158
  updateScreenshotAnnotation: (annotation: ScreenshotAnnotationState) => void;
155
159
  clearDraft: () => void;
156
- submitReport: (title: string, description: string) => Promise<BugSubmitResult | null>;
160
+ submitReport: (title: string, structuredFields: {
161
+ stepsToReproduce: string;
162
+ expectedResult: string;
163
+ actualResult: string;
164
+ additionalContext: string;
165
+ }) => Promise<BugSubmitResult | null>;
157
166
  resetMessages: () => void;
158
167
  };
159
168
  declare function BugReporterProvider({ children, integrations, defaultProvider, maxDurationMs, }: BugReporterProviderProps): react_jsx_runtime.JSX.Element;
package/dist/index.d.ts CHANGED
@@ -92,6 +92,10 @@ type BugReporterSubmitOptions = {
92
92
  consoleLogs?: ConsoleLogEntry[];
93
93
  jsErrors?: CapturedJsError[];
94
94
  onProgress?: SubmitProgressCallback;
95
+ stepsToReproduce?: string;
96
+ expectedResult?: string;
97
+ actualResult?: string;
98
+ additionalContext?: string;
95
99
  };
96
100
  declare class BugReporter {
97
101
  private integration;
@@ -153,7 +157,12 @@ type BugReporterContextValue = {
153
157
  screenshotHighlightCount: number;
154
158
  updateScreenshotAnnotation: (annotation: ScreenshotAnnotationState) => void;
155
159
  clearDraft: () => void;
156
- submitReport: (title: string, description: string) => Promise<BugSubmitResult | null>;
160
+ submitReport: (title: string, structuredFields: {
161
+ stepsToReproduce: string;
162
+ expectedResult: string;
163
+ actualResult: string;
164
+ additionalContext: string;
165
+ }) => Promise<BugSubmitResult | null>;
157
166
  resetMessages: () => void;
158
167
  };
159
168
  declare function BugReporterProvider({ children, integrations, defaultProvider, maxDurationMs, }: BugReporterProviderProps): react_jsx_runtime.JSX.Element;
package/dist/index.js CHANGED
@@ -892,6 +892,10 @@ var CloudIntegration = class {
892
892
  fd.set("project_key", this.projectKey);
893
893
  fd.set("title", payload.title);
894
894
  fd.set("description", payload.description || "");
895
+ if (payload.stepsToReproduce) fd.set("steps_to_reproduce", payload.stepsToReproduce);
896
+ if (payload.expectedResult) fd.set("expected_result", payload.expectedResult);
897
+ if (payload.actualResult) fd.set("actual_result", payload.actualResult);
898
+ if (payload.additionalContext) fd.set("additional_context", payload.additionalContext);
895
899
  fd.set("provider", "cloud");
896
900
  fd.set("capture_mode", payload.captureMode);
897
901
  fd.set("has_screenshot", String(Boolean(payload.screenshotBlob)));
@@ -1827,6 +1831,10 @@ var BugReporter = class {
1827
1831
  const payload = {
1828
1832
  title: normalizedTitle,
1829
1833
  description: normalizedDescription,
1834
+ stepsToReproduce: options.stepsToReproduce,
1835
+ expectedResult: options.expectedResult,
1836
+ actualResult: options.actualResult,
1837
+ additionalContext: options.additionalContext,
1830
1838
  videoBlob: artifacts.videoBlob,
1831
1839
  screenshotBlob: options.screenshotBlob ?? artifacts.screenshotBlob,
1832
1840
  networkLogs: this.session.finalizeNetworkLogsForSubmit(artifacts.captureMode),
@@ -2359,7 +2367,7 @@ function BugReporterProvider({
2359
2367
  setScreenshotAnnotation(annotation);
2360
2368
  }, []);
2361
2369
  const submitReport = useCallback(
2362
- async (title, description) => {
2370
+ async (title, structuredFields) => {
2363
2371
  const reporter = getOrCreateReporter();
2364
2372
  if (!reporter) {
2365
2373
  setError("No bug tracker integration is configured.");
@@ -2379,6 +2387,25 @@ function BugReporterProvider({
2379
2387
  setSubmissionProgress("Preparing submission\u2026");
2380
2388
  setError(null);
2381
2389
  setSuccess(null);
2390
+ const { stepsToReproduce, expectedResult, actualResult, additionalContext } = structuredFields;
2391
+ const sections = [];
2392
+ if (stepsToReproduce.trim()) {
2393
+ sections.push(`## Steps to Reproduce
2394
+ ${stepsToReproduce.trim()}`);
2395
+ }
2396
+ if (expectedResult.trim()) {
2397
+ sections.push(`## Expected Result
2398
+ ${expectedResult.trim()}`);
2399
+ }
2400
+ if (actualResult.trim()) {
2401
+ sections.push(`## Actual Result
2402
+ ${actualResult.trim()}`);
2403
+ }
2404
+ if (additionalContext.trim()) {
2405
+ sections.push(`## Additional Context
2406
+ ${additionalContext.trim()}`);
2407
+ }
2408
+ const description = sections.length > 0 ? sections.join("\n\n") : "No description provided";
2382
2409
  const screenshotBlobForSubmit = draftMode === "screenshot" ? screenshotAnnotation.annotatedBlob ?? screenshotBlob : null;
2383
2410
  const metadata = {
2384
2411
  annotation: draftMode === "screenshot" && screenshotAnnotation.highlights.length > 0 ? {
@@ -2393,6 +2420,10 @@ function BugReporterProvider({
2393
2420
  };
2394
2421
  try {
2395
2422
  const result = await reporter.submit(title, description, {
2423
+ stepsToReproduce,
2424
+ expectedResult,
2425
+ actualResult,
2426
+ additionalContext,
2396
2427
  screenshotBlob: screenshotBlobForSubmit,
2397
2428
  metadata,
2398
2429
  consoleLogs,
@@ -3190,6 +3221,7 @@ function providerLabel(provider) {
3190
3221
  if (provider === "cloud") return "QuickBugs Cloud";
3191
3222
  return provider;
3192
3223
  }
3224
+ var CHAR_LIMIT = 4e3;
3193
3225
  function BugReporterModal() {
3194
3226
  const {
3195
3227
  autoStopNotice,
@@ -3219,9 +3251,39 @@ function BugReporterModal() {
3219
3251
  videoPreviewUrl
3220
3252
  } = useBugReporter();
3221
3253
  const [title, setTitle] = useState("");
3222
- const [description, setDescription] = useState("");
3254
+ const [stepsToReproduce, setStepsToReproduce] = useState("");
3255
+ const [expectedResult, setExpectedResult] = useState("");
3256
+ const [actualResult, setActualResult] = useState("");
3257
+ const [additionalContext, setAdditionalContext] = useState("");
3223
3258
  const [step, setStep] = useState("review");
3259
+ const [activeTab, setActiveTab] = useState("steps");
3260
+ const totalChars = stepsToReproduce.length + expectedResult.length + actualResult.length + additionalContext.length;
3261
+ const isOverLimit = totalChars > CHAR_LIMIT;
3224
3262
  const elapsedLabel = useMemo(() => formatElapsed2(elapsedMs), [elapsedMs]);
3263
+ const handleStepsKeyDown = (event) => {
3264
+ if (event.key === "Enter" && !event.shiftKey) {
3265
+ event.preventDefault();
3266
+ const textarea = event.currentTarget;
3267
+ const cursorPos = textarea.selectionStart;
3268
+ const textBeforeCursor = stepsToReproduce.substring(0, cursorPos);
3269
+ const textAfterCursor = stepsToReproduce.substring(cursorPos);
3270
+ const lines = textBeforeCursor.split("\n");
3271
+ const currentLine = lines[lines.length - 1];
3272
+ const numberMatch = currentLine.match(/^(\d+)\.\s/);
3273
+ const nextNumber = numberMatch ? parseInt(numberMatch[1]) + 1 : lines.length > 0 && stepsToReproduce.trim() === "" ? 1 : lines.length + 1;
3274
+ const newText = textBeforeCursor + "\n" + nextNumber + ". " + textAfterCursor;
3275
+ setStepsToReproduce(newText);
3276
+ setTimeout(() => {
3277
+ const newCursorPos = cursorPos + ("\n" + nextNumber + ". ").length;
3278
+ textarea.setSelectionRange(newCursorPos, newCursorPos);
3279
+ }, 0);
3280
+ }
3281
+ };
3282
+ const handleStepsFocus = () => {
3283
+ if (stepsToReproduce.trim() === "") {
3284
+ setStepsToReproduce("1. ");
3285
+ }
3286
+ };
3225
3287
  const handleDialogOpenChange = (open) => {
3226
3288
  if (open) {
3227
3289
  openModal();
@@ -3232,15 +3294,24 @@ function BugReporterModal() {
3232
3294
  };
3233
3295
  const handleSubmit = async (event) => {
3234
3296
  event.preventDefault();
3235
- const result = await submitReport(title, description);
3297
+ const result = await submitReport(title, {
3298
+ stepsToReproduce,
3299
+ expectedResult,
3300
+ actualResult,
3301
+ additionalContext
3302
+ });
3236
3303
  if (result) {
3237
3304
  setTitle("");
3238
- setDescription("");
3305
+ setStepsToReproduce("");
3306
+ setExpectedResult("");
3307
+ setActualResult("");
3308
+ setAdditionalContext("");
3239
3309
  setStep("review");
3310
+ setActiveTab("steps");
3240
3311
  }
3241
3312
  };
3242
3313
  const hasIntegrations = availableProviders.length > 0;
3243
- const canSubmit = !isSubmitting && !isCapturingScreenshot && hasIntegrations && !!selectedProvider && hasDraft && title.trim().length > 0;
3314
+ const canSubmit = !isSubmitting && !isCapturingScreenshot && hasIntegrations && !!selectedProvider && hasDraft && title.trim().length > 0 && !isOverLimit;
3244
3315
  return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: handleDialogOpenChange, children: /* @__PURE__ */ jsx(
3245
3316
  DialogContent,
3246
3317
  {
@@ -3406,18 +3477,91 @@ function BugReporterModal() {
3406
3477
  }
3407
3478
  )
3408
3479
  ] }),
3409
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 sm:col-span-2", children: [
3410
- /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: "bug-description", children: "Quick note" }),
3411
- /* @__PURE__ */ jsx(
3480
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 sm:col-span-2", children: [
3481
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Bug Details" }),
3482
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1 border-b border-gray-200", children: [
3483
+ /* @__PURE__ */ jsx(
3484
+ "button",
3485
+ {
3486
+ type: "button",
3487
+ className: `px-3 py-2 text-sm font-medium transition-colors ${activeTab === "steps" ? "border-b-2 border-indigo-600 text-indigo-600" : "text-gray-500 hover:text-gray-700"}`,
3488
+ onClick: () => setActiveTab("steps"),
3489
+ children: "Steps"
3490
+ }
3491
+ ),
3492
+ /* @__PURE__ */ jsx(
3493
+ "button",
3494
+ {
3495
+ type: "button",
3496
+ className: `px-3 py-2 text-sm font-medium transition-colors ${activeTab === "expected" ? "border-b-2 border-indigo-600 text-indigo-600" : "text-gray-500 hover:text-gray-700"}`,
3497
+ onClick: () => setActiveTab("expected"),
3498
+ children: "Expected"
3499
+ }
3500
+ ),
3501
+ /* @__PURE__ */ jsx(
3502
+ "button",
3503
+ {
3504
+ type: "button",
3505
+ className: `px-3 py-2 text-sm font-medium transition-colors ${activeTab === "actual" ? "border-b-2 border-indigo-600 text-indigo-600" : "text-gray-500 hover:text-gray-700"}`,
3506
+ onClick: () => setActiveTab("actual"),
3507
+ children: "Actual"
3508
+ }
3509
+ ),
3510
+ /* @__PURE__ */ jsx(
3511
+ "button",
3512
+ {
3513
+ type: "button",
3514
+ className: `px-3 py-2 text-sm font-medium transition-colors ${activeTab === "context" ? "border-b-2 border-indigo-600 text-indigo-600" : "text-gray-500 hover:text-gray-700"}`,
3515
+ onClick: () => setActiveTab("context"),
3516
+ children: "Context"
3517
+ }
3518
+ )
3519
+ ] }),
3520
+ activeTab === "steps" && /* @__PURE__ */ jsx(
3412
3521
  Textarea,
3413
3522
  {
3414
- id: "bug-description",
3415
- maxLength: 4e3,
3416
- placeholder: "What did you expect, what happened, and any quick repro steps.",
3417
- value: description,
3418
- onChange: (event) => setDescription(event.target.value)
3523
+ id: "bug-steps",
3524
+ placeholder: "Press Enter to start numbering steps...",
3525
+ value: stepsToReproduce,
3526
+ onChange: (event) => setStepsToReproduce(event.target.value),
3527
+ onKeyDown: handleStepsKeyDown,
3528
+ onFocus: handleStepsFocus
3419
3529
  }
3420
- )
3530
+ ),
3531
+ activeTab === "expected" && /* @__PURE__ */ jsx(
3532
+ Textarea,
3533
+ {
3534
+ id: "bug-expected",
3535
+ placeholder: "Describe what should happen...",
3536
+ value: expectedResult,
3537
+ onChange: (event) => setExpectedResult(event.target.value)
3538
+ }
3539
+ ),
3540
+ activeTab === "actual" && /* @__PURE__ */ jsx(
3541
+ Textarea,
3542
+ {
3543
+ id: "bug-actual",
3544
+ placeholder: "Describe what actually happened...",
3545
+ value: actualResult,
3546
+ onChange: (event) => setActualResult(event.target.value)
3547
+ }
3548
+ ),
3549
+ activeTab === "context" && /* @__PURE__ */ jsx(
3550
+ Textarea,
3551
+ {
3552
+ id: "bug-context",
3553
+ placeholder: "Any additional information, workarounds, or notes...",
3554
+ value: additionalContext,
3555
+ onChange: (event) => setAdditionalContext(event.target.value)
3556
+ }
3557
+ ),
3558
+ /* @__PURE__ */ jsxs("p", { className: `text-xs ${isOverLimit ? "text-red-600 font-medium" : "text-gray-500"}`, children: [
3559
+ totalChars,
3560
+ "/",
3561
+ CHAR_LIMIT,
3562
+ " characters ",
3563
+ isOverLimit && "(over limit)"
3564
+ ] })
3421
3565
  ] }),
3422
3566
  /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
3423
3567
  /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: "bug-provider", children: "Submit to" }),