@handled-ai/design-system 0.20.1 → 0.20.3

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.
@@ -120,8 +120,6 @@ describe("DetailView case-panel-v2 section layout", () => {
120
120
  screen.getByText("Signal brief copy: account activity needs review."),
121
121
  screen.getByText("Metadata marker"),
122
122
  screen.getByText("Before-score status marker"),
123
- screen.getByText("The why"),
124
- screen.getByText("The why copy: customer replied after the escalation window opened."),
125
123
  screen.getByText("Cash movement"),
126
124
  screen.getByText("Approve action"),
127
125
  screen.getByText("Opportunity marker"),
@@ -154,14 +152,14 @@ describe("DetailView case-panel-v2 section layout", () => {
154
152
  )
155
153
  })
156
154
 
157
- it("renders signal brief and The why separately when both copies exist", () => {
155
+ it("renders signal brief and chip-backed Why section without a separate The why block", () => {
158
156
  renderDetailView({ sectionLayout: "case-panel-v2" })
159
157
 
160
- expectInDocumentOrder(
161
- screen.getByText("Signal brief copy: account activity needs review."),
162
- screen.getByText("The why"),
163
- screen.getByText("The why copy: customer replied after the escalation window opened."),
164
- )
158
+ expect(screen.getByText("Signal brief copy: account activity needs review.")).toBeTruthy()
159
+ expect(screen.queryByText("The why")).toBeNull()
160
+ expect(screen.queryByText("The why copy: customer replied after the escalation window opened.")).toBeNull()
161
+ expect(screen.getByText("Why")).toBeTruthy()
162
+ expect(screen.getByText("Cash movement")).toBeTruthy()
165
163
  })
166
164
 
167
165
  it("falls back to whyNow in the brief and suppresses the separate The why block when signalBrief is missing", () => {
@@ -1,3 +1,4 @@
1
+ import "@testing-library/jest-dom/vitest"
1
2
  import { describe, it, expect, vi, beforeEach } from "vitest"
2
3
  import React from "react"
3
4
  import { render, fireEvent } from "@testing-library/react"
@@ -181,14 +182,19 @@ describe("DetailView timeline system-events toggle", () => {
181
182
  '[data-testid="system-events-toggle"]',
182
183
  ) as HTMLElement
183
184
  fireEvent.click(toggle)
185
+ expect(toggle).toHaveAttribute("aria-pressed", "true")
184
186
  expect(container.textContent).toContain("Score updated +3")
185
187
 
186
188
  // Collapse the timeline
187
189
  expandTimeline(container)
190
+ expect(toggle).toHaveAttribute("aria-pressed", "true")
191
+ expect(container.textContent).not.toContain("Score updated +3")
192
+
188
193
  // Re-expand
189
194
  expandTimeline(container)
190
195
 
191
196
  // System events should still be visible (toggle didn't change)
197
+ expect(toggle).toHaveAttribute("aria-pressed", "true")
192
198
  expect(container.textContent).toContain("Score updated +3")
193
199
  })
194
200
 
@@ -203,15 +209,20 @@ describe("DetailView timeline system-events toggle", () => {
203
209
  const badge = container.querySelector('[data-testid="hidden-count-badge"]')
204
210
  expect(badge).not.toBeNull()
205
211
  expect(badge?.textContent).toBe("2")
212
+ expect(badge).toHaveClass("min-w-[18px]")
206
213
  })
207
214
 
208
- it("calls localStorage.setItem when toggle changes", () => {
215
+ it("calls localStorage.setItem when toggle changes and shows a stronger pressed style", () => {
209
216
  const { container } = render(<DetailView {...baseProps()} />)
210
217
  expandTimeline(container)
211
218
  const toggle = container.querySelector(
212
219
  '[data-testid="system-events-toggle"]',
213
220
  ) as HTMLElement
221
+ expect(toggle).toHaveAttribute("aria-pressed", "false")
214
222
  fireEvent.click(toggle)
223
+ expect(toggle).toHaveAttribute("aria-pressed", "true")
224
+ expect(toggle.className).toContain("border-primary/40")
225
+ expect(toggle.className).toContain("bg-primary/10")
215
226
  expect(localStorageMock.setItem).toHaveBeenCalledWith(
216
227
  "test-show-score-changes",
217
228
  "true",
@@ -301,12 +312,15 @@ describe("DetailView timeline system-events toggle", () => {
301
312
  expect(toggle).toBeNull()
302
313
  })
303
314
 
304
- it("shows footer hint when timeline is expanded and system events are hidden", () => {
315
+ it("shows footer hint below the case-panel timeline when timeline is expanded and system events are hidden", () => {
305
316
  const { container } = render(<DetailView {...baseProps()} />)
306
317
  expandTimeline(container)
318
+ const timeline = container.querySelector('[data-variant="case-panel"]')
307
319
  const hint = container.querySelector('[data-testid="timeline-footer-hint"]')
320
+ expect(timeline).not.toBeNull()
308
321
  expect(hint).not.toBeNull()
309
322
  expect(hint?.textContent).toBe("Score changes are hidden.")
323
+ expect(timeline?.compareDocumentPosition(hint as Node)).toBe(Node.DOCUMENT_POSITION_FOLLOWING)
310
324
  })
311
325
 
312
326
  it("shows visible footer hint with count when system events are shown", () => {
@@ -48,6 +48,7 @@ import {
48
48
  type SuggestedContact,
49
49
  } from "../components/suggested-actions"
50
50
  import { TimelineActivity, type TimelineEvent } from "../components/timeline-activity"
51
+ import { cn } from "../lib/utils"
51
52
  import type {
52
53
  QueueItem,
53
54
  InboxViewConfig,
@@ -296,13 +297,23 @@ function TimelineSection({
296
297
  <button
297
298
  type="button"
298
299
  onClick={() => setShowSystemEvents((prev) => !prev)}
299
- className="flex shrink-0 items-center gap-1.5 rounded-full border border-border bg-background px-2.5 py-1 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground cursor-pointer"
300
+ className={cn(
301
+ "flex shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] font-medium transition-colors hover:text-foreground",
302
+ showSystemEvents
303
+ ? "border-primary/40 bg-primary/10 text-primary shadow-sm hover:bg-primary/15"
304
+ : "border-border bg-background text-muted-foreground hover:bg-muted/40"
305
+ )}
300
306
  aria-pressed={showSystemEvents}
301
307
  data-testid="system-events-toggle"
302
308
  >
303
309
  {toggleLabel}
304
310
  <span
305
- className="inline-flex items-center justify-center rounded-full bg-muted px-1.5 text-[10px] font-semibold min-w-[18px] tabular-nums"
311
+ className={cn(
312
+ "inline-flex min-w-[18px] items-center justify-center rounded-full px-1.5 text-[10px] font-semibold tabular-nums",
313
+ showSystemEvents
314
+ ? "bg-primary/15 text-primary ring-1 ring-primary/30"
315
+ : "bg-muted text-muted-foreground ring-1 ring-border/70"
316
+ )}
306
317
  data-testid="hidden-count-badge"
307
318
  >
308
319
  {hiddenCount}
@@ -314,7 +325,7 @@ function TimelineSection({
314
325
  {/* Timeline body */}
315
326
  {showTimeline && visibleEvents.length > 0 && (
316
327
  <div className="mt-3">
317
- <TimelineActivity events={visibleEvents} />
328
+ <TimelineActivity events={visibleEvents} variant="case-panel" />
318
329
  </div>
319
330
  )}
320
331
 
@@ -491,9 +502,6 @@ export function DetailView({
491
502
  : "hover:bg-muted/50"
492
503
 
493
504
  const isCasePanelV2 = sectionLayout === "case-panel-v2"
494
- const v2WhyText = signalData.signalBrief
495
- ? signalData.whyNow || signalData.urgencyExplanation
496
- : undefined
497
505
 
498
506
  // The metadata chips row (priority · deadline · account · renderMetadataExtra). Rendered above
499
507
  // the brief by default, or beneath it when `metadataLayout === "below-brief"` (case-panel redesign).
@@ -713,15 +721,6 @@ export function DetailView({
713
721
  {/* Before-score content slot (e.g. status/attention pills) */}
714
722
  {renderBeforeScore?.(item)}
715
723
 
716
- {v2WhyText ? (
717
- <div className="mb-8">
718
- <h3 className="text-xs font-bold text-muted-foreground uppercase tracking-wider mb-3">The why</h3>
719
- <p className="text-sm text-foreground/90 leading-relaxed mb-4">
720
- {v2WhyText}
721
- </p>
722
- </div>
723
- ) : null}
724
-
725
724
  <div className="mb-8">
726
725
  <ScoreWhyChips
727
726
  item={item}