@handled-ai/design-system 0.18.56 → 0.18.57
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/components/data-table-filter.d.ts +18 -1
- package/dist/components/data-table-filter.js +20 -6
- package/dist/components/data-table-filter.js.map +1 -1
- package/dist/components/email-preview-card.js +17 -24
- package/dist/components/email-preview-card.js.map +1 -1
- package/dist/components/signal-feedback-inline.d.ts +2 -0
- package/dist/components/signal-feedback-inline.js +16 -2
- package/dist/components/signal-feedback-inline.js.map +1 -1
- package/dist/prototype/prototype-inbox-view.js +15 -1
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/data-table-filter.test.tsx +72 -0
- package/src/components/__tests__/email-preview-card.test.tsx +7 -13
- package/src/components/data-table-filter.tsx +53 -10
- package/src/components/email-preview-card.tsx +13 -19
- package/src/components/signal-feedback-inline.tsx +14 -0
- package/src/prototype/__tests__/detail-view-opportunity-preview.test.tsx +41 -1
- package/src/prototype/__tests__/detail-view-title-slots.test.tsx +2 -23
- package/src/prototype/prototype-inbox-view.tsx +8 -0
|
@@ -43,7 +43,7 @@ export function EmailPreviewCard({
|
|
|
43
43
|
signatureHtml,
|
|
44
44
|
className,
|
|
45
45
|
}: EmailPreviewCardProps) {
|
|
46
|
-
const recipientLabel = to
|
|
46
|
+
const recipientLabel = to ? `${to}'s` : "the recipient's"
|
|
47
47
|
const bodyHtml = htmlBody ?? (textBody ? escapeHtml(textBody) : "")
|
|
48
48
|
|
|
49
49
|
return (
|
|
@@ -51,7 +51,8 @@ export function EmailPreviewCard({
|
|
|
51
51
|
<div className="flex items-start gap-2 mb-3 py-2.5 px-3 border rounded-lg bg-background text-[11.5px] text-muted-foreground">
|
|
52
52
|
<Eye className="size-4 shrink-0 mt-0.5" />
|
|
53
53
|
<span>
|
|
54
|
-
This is
|
|
54
|
+
This is how your email lands in {recipientLabel} inbox. Nothing has
|
|
55
|
+
been sent yet.
|
|
55
56
|
</span>
|
|
56
57
|
</div>
|
|
57
58
|
|
|
@@ -76,24 +77,17 @@ export function EmailPreviewCard({
|
|
|
76
77
|
<div className="text-xs text-muted-foreground shrink-0">just now</div>
|
|
77
78
|
</div>
|
|
78
79
|
|
|
79
|
-
<div
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
className="text-[13.5px] leading-relaxed whitespace-pre-wrap"
|
|
84
|
-
data-testid="email-preview-body"
|
|
85
|
-
dangerouslySetInnerHTML={{ __html: bodyHtml }}
|
|
86
|
-
/>
|
|
80
|
+
<div
|
|
81
|
+
className="px-[18px] py-2 ml-[47px] text-[13.5px] leading-relaxed whitespace-pre-wrap"
|
|
82
|
+
dangerouslySetInnerHTML={{ __html: bodyHtml }}
|
|
83
|
+
/>
|
|
87
84
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
) : null}
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
85
|
+
{signatureHtml ? (
|
|
86
|
+
<div
|
|
87
|
+
className="ml-[47px] px-[18px] pt-3 mt-3 border-t border-border/50 pb-4 text-xs leading-relaxed text-muted-foreground"
|
|
88
|
+
dangerouslySetInnerHTML={{ __html: signatureHtml }}
|
|
89
|
+
/>
|
|
90
|
+
) : null}
|
|
97
91
|
</div>
|
|
98
92
|
</div>
|
|
99
93
|
)
|
|
@@ -90,6 +90,7 @@ interface OpportunityPreview {
|
|
|
90
90
|
description?: string | null
|
|
91
91
|
churnType?: string | null
|
|
92
92
|
churnTypeOptions?: Array<string | OpportunityPreviewOption>
|
|
93
|
+
nextStep?: string | null
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
interface OpportunityDraft {
|
|
@@ -97,6 +98,7 @@ interface OpportunityDraft {
|
|
|
97
98
|
amount: string
|
|
98
99
|
description: string
|
|
99
100
|
churnType: string
|
|
101
|
+
nextStep: string
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
interface SignalApprovalLabels {
|
|
@@ -466,6 +468,7 @@ function buildOpportunityDraft(preview?: OpportunityPreview): OpportunityDraft {
|
|
|
466
468
|
: formatAmountDraftValue(preview.amountValue),
|
|
467
469
|
description: preview?.description ?? "",
|
|
468
470
|
churnType: preview?.churnType ?? "",
|
|
471
|
+
nextStep: preview?.nextStep ?? "",
|
|
469
472
|
}
|
|
470
473
|
}
|
|
471
474
|
|
|
@@ -898,6 +901,17 @@ function Actions() {
|
|
|
898
901
|
)}
|
|
899
902
|
</label>
|
|
900
903
|
|
|
904
|
+
<label className="space-y-1 text-xs">
|
|
905
|
+
<span className="font-medium text-muted-foreground">Next Step</span>
|
|
906
|
+
<textarea
|
|
907
|
+
value={opportunityDraft.nextStep}
|
|
908
|
+
onChange={(event) => setOpportunityDraft((draft) => ({ ...draft, nextStep: event.target.value }))}
|
|
909
|
+
rows={2}
|
|
910
|
+
placeholder="No next step set"
|
|
911
|
+
className="w-full resize-none rounded-md border border-border bg-background px-2 py-1.5 text-xs text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring"
|
|
912
|
+
/>
|
|
913
|
+
</label>
|
|
914
|
+
|
|
901
915
|
<label className="space-y-1 text-xs">
|
|
902
916
|
<span className="font-medium text-muted-foreground">Description</span>
|
|
903
917
|
<textarea
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react"
|
|
2
|
-
import { describe, expect, it } from "vitest"
|
|
2
|
+
import { describe, expect, it, vi } from "vitest"
|
|
3
3
|
import { fireEvent, render, screen } from "@testing-library/react"
|
|
4
4
|
|
|
5
5
|
import { DetailView, type DetailViewProps } from "../prototype-inbox-view"
|
|
@@ -66,6 +66,7 @@ describe("DetailView opportunity approval preview", () => {
|
|
|
66
66
|
description: "Initial description",
|
|
67
67
|
churnType: "Churn Risk",
|
|
68
68
|
churnTypeOptions: ["Churn Risk", "Win Back"],
|
|
69
|
+
nextStep: "Initial next step",
|
|
69
70
|
})
|
|
70
71
|
},
|
|
71
72
|
})}
|
|
@@ -84,7 +85,46 @@ describe("DetailView opportunity approval preview", () => {
|
|
|
84
85
|
expect((await screen.findByLabelText("Close Date") as HTMLInputElement).value).toBe("2026-06-30")
|
|
85
86
|
expect((screen.getByLabelText("Amount") as HTMLInputElement).value).toBe("$75,000")
|
|
86
87
|
expect((screen.getByLabelText("Churn Type") as HTMLSelectElement).value).toBe("Churn Risk")
|
|
88
|
+
expect((screen.getByLabelText("Next Step") as HTMLTextAreaElement).value).toBe("Initial next step")
|
|
87
89
|
expect((screen.getByLabelText("Description") as HTMLTextAreaElement).value).toBe("Initial description")
|
|
88
90
|
expect((screen.getByRole("button", { name: /confirm/i }) as HTMLButtonElement).disabled).toBe(false)
|
|
89
91
|
})
|
|
92
|
+
|
|
93
|
+
it("passes edited next step with the opportunity draft on confirm", async () => {
|
|
94
|
+
const onSignalApprove = vi.fn()
|
|
95
|
+
|
|
96
|
+
render(
|
|
97
|
+
<DetailView
|
|
98
|
+
{...baseProps({
|
|
99
|
+
getSignalApprovalState: () => "confirming",
|
|
100
|
+
opportunityPreview: {
|
|
101
|
+
name: "Churn Risk - WIT-825 Fixture Account",
|
|
102
|
+
accountName: "WIT-825 Fixture Account",
|
|
103
|
+
stage: "Prospecting",
|
|
104
|
+
closeDate: "Jun 30, 2026",
|
|
105
|
+
closeDateValue: "2026-06-30",
|
|
106
|
+
amount: "$75,000",
|
|
107
|
+
amountValue: 75000,
|
|
108
|
+
description: "Initial description",
|
|
109
|
+
churnType: "Churn Risk",
|
|
110
|
+
churnTypeOptions: ["Churn Risk", "Win Back"],
|
|
111
|
+
nextStep: "Initial next step",
|
|
112
|
+
},
|
|
113
|
+
onSignalApprove,
|
|
114
|
+
})}
|
|
115
|
+
/>,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
const nextStepInput = screen.getByLabelText("Next Step") as HTMLTextAreaElement
|
|
119
|
+
fireEvent.change(nextStepInput, { target: { value: "Schedule validation call" } })
|
|
120
|
+
fireEvent.click(screen.getByRole("button", { name: /confirm/i }))
|
|
121
|
+
|
|
122
|
+
expect(onSignalApprove).toHaveBeenCalledWith(baseItem, {
|
|
123
|
+
closeDate: "2026-06-30",
|
|
124
|
+
amount: "$75,000",
|
|
125
|
+
churnType: "Churn Risk",
|
|
126
|
+
description: "Initial description",
|
|
127
|
+
nextStep: "Schedule validation call",
|
|
128
|
+
})
|
|
129
|
+
})
|
|
90
130
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { render, screen } from "@testing-library/react"
|
|
2
|
+
import { describe, expect, it, vi } from "vitest"
|
|
3
3
|
|
|
4
4
|
import { DetailView } from "../prototype-inbox-view"
|
|
5
5
|
import type { DetailViewProps } from "../prototype-inbox-view"
|
|
@@ -14,10 +14,6 @@ const baseItem = {
|
|
|
14
14
|
tag1: "Signal",
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
cleanup()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
17
|
function renderDetailView(overrides: Partial<DetailViewProps> = {}) {
|
|
22
18
|
const props: DetailViewProps = {
|
|
23
19
|
item: baseItem,
|
|
@@ -43,23 +39,6 @@ function renderDetailView(overrides: Partial<DetailViewProps> = {}) {
|
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
describe("DetailView title slots", () => {
|
|
46
|
-
it("does not render the inert detail Back button while preserving title and metadata slots", () => {
|
|
47
|
-
renderDetailView({
|
|
48
|
-
renderTitleActionRow: () => (
|
|
49
|
-
<button type="button">Full-width quick action</button>
|
|
50
|
-
),
|
|
51
|
-
renderMetadataExtra: () => <span data-testid="metadata-extra">Owner: Lee</span>,
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
expect(screen.queryByRole("button", { name: "Back" })).toBeNull()
|
|
55
|
-
expect(screen.getByRole("heading", { name: "Churn risk case title" })).toBeTruthy()
|
|
56
|
-
expect(screen.getAllByText("Acme Corp").length).toBeGreaterThan(0)
|
|
57
|
-
expect(screen.getByRole("button", { name: "Full-width quick action" })).toBeTruthy()
|
|
58
|
-
expect(screen.getByTestId("metadata-extra").textContent).toBe("Owner: Lee")
|
|
59
|
-
expect(screen.getByTestId("priority-popover-trigger")).toBeTruthy()
|
|
60
|
-
expect(screen.getByRole("button", { name: "View account details for Acme Corp" })).toBeTruthy()
|
|
61
|
-
})
|
|
62
|
-
|
|
63
42
|
it("renders supporting title text below the main title", () => {
|
|
64
43
|
renderDetailView({
|
|
65
44
|
renderTitleSubtext: (item) => <p data-testid="title-subtext">Full title: {item.title}</p>,
|
|
@@ -488,6 +488,14 @@ export function DetailView({
|
|
|
488
488
|
<div className="pb-8">
|
|
489
489
|
{/* Header */}
|
|
490
490
|
<div className="mb-4 flex items-center gap-2">
|
|
491
|
+
<button
|
|
492
|
+
type="button"
|
|
493
|
+
className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground"
|
|
494
|
+
>
|
|
495
|
+
<ArrowLeft className="h-3.5 w-3.5" />
|
|
496
|
+
Back
|
|
497
|
+
</button>
|
|
498
|
+
<span className="text-muted-foreground/40">·</span>
|
|
491
499
|
<span className="text-xs text-muted-foreground">{item.company}</span>
|
|
492
500
|
</div>
|
|
493
501
|
|