@handled-ai/design-system 0.17.1 → 0.18.1
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/charts/empty-chart-state.d.ts +11 -0
- package/dist/charts/empty-chart-state.js +70 -0
- package/dist/charts/empty-chart-state.js.map +1 -0
- package/dist/charts/index.d.ts +1 -0
- package/dist/charts/index.js +1 -0
- package/dist/charts/index.js.map +1 -1
- package/dist/charts/pipeline-overview.d.ts +2 -1
- package/dist/charts/pipeline-overview.js +29 -1
- package/dist/charts/pipeline-overview.js.map +1 -1
- package/dist/components/actor-byline.d.ts +3 -0
- package/dist/components/actor-byline.js +5 -0
- package/dist/components/actor-byline.js.map +1 -0
- package/dist/components/days-open-cell.d.ts +16 -0
- package/dist/components/days-open-cell.js +73 -0
- package/dist/components/days-open-cell.js.map +1 -0
- package/dist/components/detail-drawer.d.ts +16 -0
- package/dist/components/detail-drawer.js +45 -0
- package/dist/components/detail-drawer.js.map +1 -0
- package/dist/components/feedback-primitives.d.ts +66 -0
- package/dist/components/feedback-primitives.js +295 -0
- package/dist/components/feedback-primitives.js.map +1 -0
- package/dist/components/insights-filter-bar.d.ts +2 -1
- package/dist/components/insights-filter-bar.js +13 -5
- package/dist/components/insights-filter-bar.js.map +1 -1
- package/dist/components/linked-entity-cell.d.ts +14 -0
- package/dist/components/linked-entity-cell.js +96 -0
- package/dist/components/linked-entity-cell.js.map +1 -0
- package/dist/components/metric-card.d.ts +14 -1
- package/dist/components/metric-card.js +86 -0
- package/dist/components/metric-card.js.map +1 -1
- package/dist/components/performance-metrics-table.d.ts +2 -1
- package/dist/components/performance-metrics-table.js +78 -46
- package/dist/components/performance-metrics-table.js.map +1 -1
- package/dist/components/pill.d.ts +26 -0
- package/dist/components/pill.js +77 -0
- package/dist/components/pill.js.map +1 -0
- package/dist/components/quick-segment.d.ts +13 -0
- package/dist/components/quick-segment.js +96 -0
- package/dist/components/quick-segment.js.map +1 -0
- package/dist/components/score-why-chips.d.ts +8 -17
- package/dist/components/score-why-chips.js +266 -180
- package/dist/components/score-why-chips.js.map +1 -1
- package/dist/components/signal-priority-popover.d.ts +17 -0
- package/dist/components/signal-priority-popover.js +247 -0
- package/dist/components/signal-priority-popover.js.map +1 -0
- package/dist/components/user-display.d.ts +22 -0
- package/dist/components/user-display.js +138 -0
- package/dist/components/user-display.js.map +1 -0
- package/dist/components/user-pill.d.ts +3 -0
- package/dist/components/user-pill.js +5 -0
- package/dist/components/user-pill.js.map +1 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/user-display.d.ts +31 -0
- package/dist/lib/user-display.js +57 -0
- package/dist/lib/user-display.js.map +1 -0
- package/dist/prototype/index.d.ts +2 -1
- package/dist/prototype/prototype-accounts-view.d.ts +2 -1
- package/dist/prototype/prototype-admin-view.d.ts +2 -1
- package/dist/prototype/prototype-config.d.ts +15 -332
- package/dist/prototype/prototype-inbox-view.d.ts +2 -1
- package/dist/prototype/prototype-inbox-view.js +11 -12
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/dist/prototype/prototype-insights-view.d.ts +2 -1
- package/dist/prototype/prototype-shell.d.ts +2 -1
- package/dist/signal-priority-popover-DQ_VuHac.d.ts +390 -0
- package/package.json +1 -1
- package/src/charts/__tests__/insights-charts.test.tsx +62 -0
- package/src/charts/empty-chart-state.tsx +44 -0
- package/src/charts/index.ts +1 -0
- package/src/charts/pipeline-overview.tsx +38 -1
- package/src/components/__tests__/contextual-quick-action-launcher.test.tsx +99 -188
- package/src/components/__tests__/feedback-primitives.test.tsx +509 -0
- package/src/components/__tests__/insights-primitives.test.tsx +117 -0
- package/src/components/__tests__/performance-metrics-table.test.tsx +54 -0
- package/src/components/__tests__/score-why-chips.test.tsx +540 -0
- package/src/components/__tests__/signal-priority-popover.test.tsx +312 -0
- package/src/components/__tests__/user-display.test.tsx +75 -0
- package/src/components/actor-byline.tsx +1 -0
- package/src/components/days-open-cell.tsx +50 -0
- package/src/components/detail-drawer.tsx +60 -0
- package/src/components/feedback-primitives.tsx +424 -0
- package/src/components/insights-filter-bar.tsx +13 -4
- package/src/components/linked-entity-cell.tsx +74 -0
- package/src/components/metric-card.tsx +82 -0
- package/src/components/performance-metrics-table.tsx +99 -63
- package/src/components/pill.tsx +67 -0
- package/src/components/quick-segment.tsx +68 -0
- package/src/components/score-why-chips.tsx +413 -203
- package/src/components/signal-priority-popover.tsx +359 -0
- package/src/components/user-display.tsx +96 -0
- package/src/components/user-pill.tsx +1 -0
- package/src/index.ts +11 -0
- package/src/lib/__tests__/user-display.test.ts +85 -0
- package/src/lib/user-display.ts +88 -0
- package/src/prototype/__tests__/detail-view-score-why.test.tsx +33 -29
- package/src/prototype/__tests__/detail-view-title-slots.test.tsx +65 -0
- package/src/prototype/prototype-config.ts +28 -4
- package/src/prototype/prototype-inbox-view.tsx +8 -10
- package/src/prototype/__tests__/detail-view-title-subtext.test.tsx +0 -72
|
@@ -80,16 +80,12 @@ describe("DetailView corrected compact score WHY UX", () => {
|
|
|
80
80
|
|
|
81
81
|
fireEvent.click(screen.getByRole("button", { name: /urgent priority/i }));
|
|
82
82
|
|
|
83
|
+
// The new popover shows urgency explanation and score info in a different format
|
|
83
84
|
expect(screen.getByText("Customer activity spiked today.")).toBeInTheDocument();
|
|
84
|
-
expect(screen.getByText(
|
|
85
|
-
expect(screen.getByText(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
expect(within(priorityPanel).getByText("Strong signals detected.")).toBeInTheDocument();
|
|
89
|
-
expect(screen.getByText("Top factor")).toBeInTheDocument();
|
|
90
|
-
expect(screen.getAllByText("Strong signal").length).toBeGreaterThan(0);
|
|
91
|
-
expect(screen.getByText("Trigger strength")).toBeInTheDocument();
|
|
92
|
-
expect(screen.getByText("Company fit")).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByText(/82/)).toBeInTheDocument();
|
|
86
|
+
expect(screen.getByText(/\/100/)).toBeInTheDocument();
|
|
87
|
+
// Popover shows "Why this is urgent priority" heading
|
|
88
|
+
expect(screen.getByText(/why this is urgent priority/i)).toBeInTheDocument();
|
|
93
89
|
expect(screen.queryByText("How's this score?")).toBeNull();
|
|
94
90
|
});
|
|
95
91
|
|
|
@@ -116,21 +112,40 @@ describe("DetailView corrected compact score WHY UX", () => {
|
|
|
116
112
|
expect(screen.getByRole("region", { name: /treasury activity details/i })).toBeInTheDocument();
|
|
117
113
|
});
|
|
118
114
|
|
|
119
|
-
it("resets selected bucket
|
|
120
|
-
const { rerender } = render(
|
|
115
|
+
it("resets selected bucket when item id changes", () => {
|
|
116
|
+
const { rerender } = render(
|
|
117
|
+
<DetailView
|
|
118
|
+
{...baseProps({
|
|
119
|
+
getSignalScore: () =>
|
|
120
|
+
makeSignalScore({
|
|
121
|
+
explanationBuckets: [
|
|
122
|
+
{ key: "signal-a", label: "Treasury activity", kind: "signal", primarySignalId: "sig-1", signals: [{ id: "sig-1", label: "Treasury signal" }] },
|
|
123
|
+
],
|
|
124
|
+
}),
|
|
125
|
+
})}
|
|
126
|
+
/>,
|
|
127
|
+
);
|
|
121
128
|
|
|
122
|
-
|
|
123
|
-
|
|
129
|
+
// Expand a WHY bucket
|
|
130
|
+
fireEvent.click(screen.getByRole("button", { name: /treasury activity/i }));
|
|
131
|
+
expect(screen.getByRole("region", { name: /treasury activity details/i })).toBeInTheDocument();
|
|
124
132
|
|
|
125
133
|
rerender(
|
|
126
134
|
<DetailView
|
|
127
135
|
{...baseProps({
|
|
128
136
|
item: { ...baseItem, id: "case-2", title: "Second Signal" },
|
|
137
|
+
getSignalScore: () =>
|
|
138
|
+
makeSignalScore({
|
|
139
|
+
explanationBuckets: [
|
|
140
|
+
{ key: "signal-a", label: "Treasury activity", kind: "signal", primarySignalId: "sig-1", signals: [{ id: "sig-1", label: "Treasury signal" }] },
|
|
141
|
+
],
|
|
142
|
+
}),
|
|
129
143
|
})}
|
|
130
144
|
/>,
|
|
131
145
|
);
|
|
132
146
|
|
|
133
|
-
|
|
147
|
+
// Bucket should be collapsed after item change
|
|
148
|
+
expect(screen.queryByRole("region", { name: /treasury activity details/i })).toBeNull();
|
|
134
149
|
});
|
|
135
150
|
|
|
136
151
|
it("does not render factor-derived WHY chips when no signal buckets are present", () => {
|
|
@@ -167,7 +182,7 @@ describe("DetailView corrected compact score WHY UX", () => {
|
|
|
167
182
|
expect(screen.queryByRole("button", { name: /company fit/i })).toBeNull();
|
|
168
183
|
});
|
|
169
184
|
|
|
170
|
-
it("exposes aria-expanded and aria-controls for
|
|
185
|
+
it("exposes aria-expanded and aria-controls for bucket triggers", () => {
|
|
171
186
|
render(
|
|
172
187
|
<DetailView
|
|
173
188
|
{...baseProps({
|
|
@@ -181,13 +196,6 @@ describe("DetailView corrected compact score WHY UX", () => {
|
|
|
181
196
|
/>,
|
|
182
197
|
);
|
|
183
198
|
|
|
184
|
-
const priorityButton = screen.getByRole("button", { name: /urgent priority/i });
|
|
185
|
-
expect(priorityButton.getAttribute("aria-expanded")).toBe("false");
|
|
186
|
-
expect(priorityButton.getAttribute("aria-controls")).toBeTruthy();
|
|
187
|
-
fireEvent.click(priorityButton);
|
|
188
|
-
expect(priorityButton.getAttribute("aria-expanded")).toBe("true");
|
|
189
|
-
expect(document.getElementById(priorityButton.getAttribute("aria-controls")!)).toBeInTheDocument();
|
|
190
|
-
|
|
191
199
|
const bucketButton = screen.getByRole("button", { name: /signal a/i });
|
|
192
200
|
expect(bucketButton.getAttribute("aria-expanded")).toBe("false");
|
|
193
201
|
expect(bucketButton.getAttribute("aria-controls")).toBeTruthy();
|
|
@@ -213,7 +221,8 @@ describe("DetailView corrected compact score WHY UX", () => {
|
|
|
213
221
|
);
|
|
214
222
|
|
|
215
223
|
expect(screen.getAllByRole("button", { name: /treasury activity/i })).toHaveLength(1);
|
|
216
|
-
|
|
224
|
+
// Task 3 redesigned WHY pills: count badge now uses "x{N}" format
|
|
225
|
+
expect(screen.getByText(/x\s*3/)).toBeInTheDocument();
|
|
217
226
|
|
|
218
227
|
fireEvent.click(screen.getByRole("button", { name: /treasury activity/i }));
|
|
219
228
|
const matchingSignals = screen.getByRole("list", { name: /matching signals/i });
|
|
@@ -297,7 +306,7 @@ describe("DetailView corrected compact score WHY UX", () => {
|
|
|
297
306
|
});
|
|
298
307
|
});
|
|
299
308
|
|
|
300
|
-
it("
|
|
309
|
+
it("signal WHY panels do not render factor feedback UI", () => {
|
|
301
310
|
const onFactorFeedback = vi.fn();
|
|
302
311
|
render(
|
|
303
312
|
<DetailView
|
|
@@ -317,10 +326,5 @@ describe("DetailView corrected compact score WHY UX", () => {
|
|
|
317
326
|
fireEvent.click(screen.getByRole("button", { name: /signal only/i }));
|
|
318
327
|
const signalPanel = screen.getByRole("region", { name: /signal only details/i });
|
|
319
328
|
expect(within(signalPanel).queryByTitle("This factor is accurate")).toBeNull();
|
|
320
|
-
|
|
321
|
-
fireEvent.click(screen.getByRole("button", { name: /urgent priority/i }));
|
|
322
|
-
const priorityPanel = screen.getByRole("region", { name: /priority explanation/i });
|
|
323
|
-
expect(within(priorityPanel).getAllByTitle("This factor is accurate").length).toBeGreaterThan(0);
|
|
324
|
-
expect(within(priorityPanel).getByText("Needs review")).toBeInTheDocument();
|
|
325
329
|
});
|
|
326
330
|
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react"
|
|
2
|
+
import { describe, expect, it, vi } from "vitest"
|
|
3
|
+
|
|
4
|
+
import { DetailView } from "../prototype-inbox-view"
|
|
5
|
+
import type { DetailViewProps } from "../prototype-inbox-view"
|
|
6
|
+
|
|
7
|
+
const baseItem = {
|
|
8
|
+
id: "case-1",
|
|
9
|
+
title: "Churn risk case title",
|
|
10
|
+
details: "Case details",
|
|
11
|
+
statusColor: "red",
|
|
12
|
+
time: "1h ago",
|
|
13
|
+
company: "Acme Corp",
|
|
14
|
+
tag1: "Signal",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function renderDetailView(overrides: Partial<DetailViewProps> = {}) {
|
|
18
|
+
const props: DetailViewProps = {
|
|
19
|
+
item: baseItem,
|
|
20
|
+
sections: { signalBrief: false, suggestedActions: false, timeline: false },
|
|
21
|
+
getSignalScore: () => ({
|
|
22
|
+
score: 72,
|
|
23
|
+
factors: [],
|
|
24
|
+
whyNow: "Why now",
|
|
25
|
+
evidence: [],
|
|
26
|
+
confidence: 80,
|
|
27
|
+
urgencyLabel: "High",
|
|
28
|
+
urgencyExplanation: "High priority",
|
|
29
|
+
}),
|
|
30
|
+
buildSuggestedActions: () => [],
|
|
31
|
+
buildSourceItems: () => [],
|
|
32
|
+
accountContacts: [],
|
|
33
|
+
emailSignature: "",
|
|
34
|
+
iconMap: {},
|
|
35
|
+
...overrides,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return render(<DetailView {...props} />)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("DetailView title slots", () => {
|
|
42
|
+
it("renders supporting title text below the main title", () => {
|
|
43
|
+
renderDetailView({
|
|
44
|
+
renderTitleSubtext: (item) => <p data-testid="title-subtext">Full title: {item.title}</p>,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
expect(screen.getByTestId("title-subtext").textContent).toBe(
|
|
48
|
+
"Full title: Churn risk case title",
|
|
49
|
+
)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("renders title extra content beside the main title", () => {
|
|
53
|
+
const onClick = vi.fn()
|
|
54
|
+
|
|
55
|
+
renderDetailView({
|
|
56
|
+
renderTitleExtra: () => (
|
|
57
|
+
<button type="button" onClick={onClick}>
|
|
58
|
+
Quick action
|
|
59
|
+
</button>
|
|
60
|
+
),
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
expect(screen.getByRole("button", { name: "Quick action" })).toBeTruthy()
|
|
64
|
+
})
|
|
65
|
+
})
|
|
@@ -15,6 +15,8 @@ import type {
|
|
|
15
15
|
import type { TimelineEvent } from "../components/timeline-activity"
|
|
16
16
|
import type { ApprovalState } from "../components/signal-feedback-inline"
|
|
17
17
|
import type { LucideIcon } from "lucide-react"
|
|
18
|
+
import type { PriorityFactor } from "../components/signal-priority-popover"
|
|
19
|
+
import type { FeedbackChipTree, FeedbackSubmitData } from "../components/feedback-primitives"
|
|
18
20
|
|
|
19
21
|
// ---------------------------------------------------------------------------
|
|
20
22
|
// Shared
|
|
@@ -45,6 +47,16 @@ export interface SignalScoreExplanationSignal {
|
|
|
45
47
|
source?: string
|
|
46
48
|
time?: string
|
|
47
49
|
metric?: string
|
|
50
|
+
/** Signal type name (e.g., "treasury_liquidation"). Used for combined signal component identification. */
|
|
51
|
+
signalTypeName?: string
|
|
52
|
+
/** Primary display value (e.g., "-$1,724,310.11"). */
|
|
53
|
+
primaryValue?: string
|
|
54
|
+
/** Qualifier text (e.g., "100% of balance"). */
|
|
55
|
+
qualifier?: string
|
|
56
|
+
/** Counterparty / destination (e.g., "-> JPMorgan Chase --6042"). */
|
|
57
|
+
counterparty?: string
|
|
58
|
+
/** Component breakdown for combined signals. */
|
|
59
|
+
components?: Array<{ type: string; count: number }>
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
export interface SignalScoreExplanationBucket {
|
|
@@ -62,6 +74,10 @@ export interface SignalScoreExplanationBucket {
|
|
|
62
74
|
signalIds?: string[]
|
|
63
75
|
primarySignalId?: string
|
|
64
76
|
factorKeys?: string[]
|
|
77
|
+
/** Lucide icon name for the bucket type (e.g., "trending-down"). */
|
|
78
|
+
icon?: string
|
|
79
|
+
/** Tonal styling hint for the bucket. */
|
|
80
|
+
tone?: "alert" | "warn" | "info"
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
export interface SignalScoreData {
|
|
@@ -81,6 +97,14 @@ export interface SignalScoreData {
|
|
|
81
97
|
/** @deprecated The compact score UX no longer renders score-level thumbs by default. */
|
|
82
98
|
initialScoreFeedback?: { type: "up" | "down"; pills: string[]; detail: string } | null
|
|
83
99
|
initialFactorFeedback?: Record<string, { type: "up" | "down"; detail: string }>
|
|
100
|
+
/** Priority factors for the popover breakdown. */
|
|
101
|
+
priorityFactors?: PriorityFactor[]
|
|
102
|
+
/** Negative feedback chip tree for the priority popover. */
|
|
103
|
+
priorityFeedbackChips?: FeedbackChipTree[]
|
|
104
|
+
/** Callback when user submits priority-level feedback. */
|
|
105
|
+
onPriorityFeedback?: (data: FeedbackSubmitData) => void
|
|
106
|
+
/** Callback when user submits bucket-level feedback. */
|
|
107
|
+
onBucketFeedback?: (bucketKey: string, data: FeedbackSubmitData) => void
|
|
84
108
|
/** AI-generated signal brief text. When present, rendered in a dedicated section. */
|
|
85
109
|
signalBrief?: string
|
|
86
110
|
/** Compact label for time-remaining chip (e.g., "13 days left"). */
|
|
@@ -166,16 +190,16 @@ export interface InboxViewConfig {
|
|
|
166
190
|
briefStyleVariant?: BriefStyleVariant
|
|
167
191
|
/** Render extra content at the end of the detail view, below the suggested actions section. */
|
|
168
192
|
renderDetailExtra?: (item: QueueItem) => React.ReactNode
|
|
169
|
-
/** Render extra content inline with the detail title. */
|
|
170
|
-
renderTitleExtra?: (item: QueueItem) => React.ReactNode
|
|
171
|
-
/** Render supporting content below the detail title. */
|
|
172
|
-
renderTitleSubtext?: (item: QueueItem) => React.ReactNode
|
|
173
193
|
/** Render content between the signal brief text and the signal score bar (e.g. "Signals on Case" chips). */
|
|
174
194
|
renderBeforeScore?: (item: QueueItem) => React.ReactNode
|
|
175
195
|
/** Render content between the signal score section and the activity timeline (e.g. OpportunityPanel). */
|
|
176
196
|
renderAfterScore?: (item: QueueItem) => React.ReactNode
|
|
177
197
|
/** Formatted string for "Last activity X ago" in the collapsed timeline header. If omitted, falls back to the first event's time. */
|
|
178
198
|
lastActivityTime?: string
|
|
199
|
+
/** Render extra content inline with the detail title. */
|
|
200
|
+
renderTitleExtra?: (item: QueueItem) => React.ReactNode
|
|
201
|
+
/** Render supporting content below the detail title. */
|
|
202
|
+
renderTitleSubtext?: (item: QueueItem) => React.ReactNode
|
|
179
203
|
/** Sort options for the inbox. When provided, a sort dropdown is rendered in the split view toolbar. */
|
|
180
204
|
sortOptions?: InboxSortOption[]
|
|
181
205
|
/** Currently active sort option id. */
|
|
@@ -38,7 +38,8 @@ import {
|
|
|
38
38
|
} from "../components/inbox-toolbar"
|
|
39
39
|
import { GroupedListView, type GroupedListGroup } from "../components/item-list"
|
|
40
40
|
import { SignalApproval, type ApprovalState, type OpportunityPreview } from "../components/signal-feedback-inline"
|
|
41
|
-
import { ScoreWhyChips
|
|
41
|
+
import { ScoreWhyChips } from "../components/score-why-chips"
|
|
42
|
+
import { SignalPriorityPopover } from "../components/signal-priority-popover"
|
|
42
43
|
import { type SourceDef } from "../components/detail-view"
|
|
43
44
|
import {
|
|
44
45
|
SuggestedActions,
|
|
@@ -186,13 +187,10 @@ export function DetailView({
|
|
|
186
187
|
}: DetailViewProps) {
|
|
187
188
|
const [showTimeline, setShowTimeline] = React.useState(false)
|
|
188
189
|
const [extraActions, setExtraActions] = React.useState<SuggestedAction[]>([])
|
|
189
|
-
const [priorityOpen, setPriorityOpen] = React.useState(false)
|
|
190
|
-
const priorityPanelId = React.useId()
|
|
191
190
|
|
|
192
191
|
React.useEffect(() => {
|
|
193
192
|
setShowTimeline(false)
|
|
194
193
|
setExtraActions([])
|
|
195
|
-
setPriorityOpen(false)
|
|
196
194
|
}, [item.id])
|
|
197
195
|
|
|
198
196
|
const signalData = React.useMemo(
|
|
@@ -266,12 +264,14 @@ export function DetailView({
|
|
|
266
264
|
</div>
|
|
267
265
|
|
|
268
266
|
<div className="mb-6 flex flex-wrap items-center gap-2">
|
|
269
|
-
<
|
|
267
|
+
<SignalPriorityPopover
|
|
270
268
|
score={signalData.score}
|
|
271
269
|
urgencyLabel={signalData.urgencyLabel}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
270
|
+
urgencyExplanation={signalData.urgencyExplanation ?? signalData.signalBrief}
|
|
271
|
+
factors={signalData.priorityFactors ?? []}
|
|
272
|
+
metaText={undefined}
|
|
273
|
+
feedbackChips={signalData.priorityFeedbackChips}
|
|
274
|
+
onFeedbackSubmit={signalData.onPriorityFeedback}
|
|
275
275
|
/>
|
|
276
276
|
{signalData.timeChipLabel && (
|
|
277
277
|
<Badge variant="outline" title={signalData.timeChipDetail ?? undefined}>
|
|
@@ -292,8 +292,6 @@ export function DetailView({
|
|
|
292
292
|
{renderMetadataExtra?.(item)}
|
|
293
293
|
</div>
|
|
294
294
|
|
|
295
|
-
{priorityOpen && <SignalPriorityPanel id={priorityPanelId} signalData={signalData} className="mb-6" />}
|
|
296
|
-
|
|
297
295
|
{/* Signal Brief */}
|
|
298
296
|
{sections.signalBrief && (() => {
|
|
299
297
|
const briefHeading = signalBriefCopy?.heading ?? "Signal brief"
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { render, screen } from "@testing-library/react";
|
|
4
|
-
import { DetailView, type DetailViewProps } from "../prototype-inbox-view";
|
|
5
|
-
import type { QueueItem, SignalScoreData } from "../prototype-config";
|
|
6
|
-
|
|
7
|
-
const baseItem: QueueItem = {
|
|
8
|
-
id: "case-1",
|
|
9
|
-
title: "QA Placement Churn Risk Signal",
|
|
10
|
-
details: "Some details",
|
|
11
|
-
statusColor: "red",
|
|
12
|
-
time: "2h ago",
|
|
13
|
-
company: "QA Placement Account",
|
|
14
|
-
tag1: "Urgent Priority",
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
function signalScore(): SignalScoreData {
|
|
18
|
-
return {
|
|
19
|
-
score: 82,
|
|
20
|
-
factors: [],
|
|
21
|
-
whyNow: "Strong signal detected.",
|
|
22
|
-
evidence: [],
|
|
23
|
-
confidence: 75,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function baseProps(overrides: Partial<DetailViewProps> = {}): DetailViewProps {
|
|
28
|
-
return {
|
|
29
|
-
item: baseItem,
|
|
30
|
-
sections: { signalBrief: true, suggestedActions: false, timeline: false },
|
|
31
|
-
getSignalScore: () => signalScore(),
|
|
32
|
-
buildSuggestedActions: () => [],
|
|
33
|
-
buildSourceItems: () => [],
|
|
34
|
-
accountContacts: [],
|
|
35
|
-
emailSignature: "",
|
|
36
|
-
iconMap: {},
|
|
37
|
-
...overrides,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
describe("DetailView title slots", () => {
|
|
42
|
-
it("renders supporting title text below the main title", () => {
|
|
43
|
-
render(
|
|
44
|
-
<DetailView
|
|
45
|
-
{...baseProps({
|
|
46
|
-
renderTitleSubtext: (item) => (
|
|
47
|
-
<p data-testid="title-subtext">Full case: {item.title}</p>
|
|
48
|
-
),
|
|
49
|
-
})}
|
|
50
|
-
/>,
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
expect(screen.getByRole("heading", { name: "QA Placement Churn Risk Signal" })).toBeTruthy();
|
|
54
|
-
expect(screen.getByTestId("title-subtext").textContent).toBe(
|
|
55
|
-
"Full case: QA Placement Churn Risk Signal",
|
|
56
|
-
);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("renders title extra content beside the main title", () => {
|
|
60
|
-
render(
|
|
61
|
-
<DetailView
|
|
62
|
-
{...baseProps({
|
|
63
|
-
renderTitleExtra: () => (
|
|
64
|
-
<button type="button" data-testid="title-extra">Quick action</button>
|
|
65
|
-
),
|
|
66
|
-
})}
|
|
67
|
-
/>,
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
expect(screen.getByTestId("title-extra").textContent).toBe("Quick action");
|
|
71
|
-
});
|
|
72
|
-
});
|