Skip to content

Commit 1a8860e

Browse files
committed
Fix dismissed flicker
1 parent dced5a4 commit 1a8860e

File tree

2 files changed

+145
-13
lines changed

2 files changed

+145
-13
lines changed

webview-ui/src/components/common/DismissibleUpsell.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const DismissibleUpsell = memo(
4444
dismissOnClick = false,
4545
}: DismissibleUpsellProps) => {
4646
const { t } = useAppTranslation()
47-
const [isVisible, setIsVisible] = useState(true)
47+
const [isVisible, setIsVisible] = useState(false)
4848
const isMountedRef = useRef(true)
4949

5050
useEffect(() => {
@@ -63,8 +63,8 @@ const DismissibleUpsell = memo(
6363
// Add null/undefined check for message
6464
if (message && message.type === "dismissedUpsells" && Array.isArray(message.list)) {
6565
// Check if this upsell has been dismissed
66-
if (message.list.includes(upsellId)) {
67-
setIsVisible(false)
66+
if (!message.list.includes(upsellId)) {
67+
setIsVisible(true)
6868
}
6969
}
7070
}

webview-ui/src/components/common/__tests__/DismissibleUpsell.spec.tsx

Lines changed: 142 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,31 @@ describe("DismissibleUpsell", () => {
3333
vi.clearAllTimers()
3434
})
3535

36-
it("renders children content", () => {
36+
// Helper function to make the component visible
37+
const makeUpsellVisible = () => {
38+
const messageEvent = new MessageEvent("message", {
39+
data: {
40+
type: "dismissedUpsells",
41+
list: [], // Empty list means no upsells are dismissed
42+
},
43+
})
44+
window.dispatchEvent(messageEvent)
45+
}
46+
47+
it("renders children content", async () => {
3748
render(
3849
<DismissibleUpsell upsellId="test-upsell">
3950
<div>Test content</div>
4051
</DismissibleUpsell>,
4152
)
4253

43-
expect(screen.getByText("Test content")).toBeInTheDocument()
54+
// Component starts hidden, make it visible
55+
makeUpsellVisible()
56+
57+
// Wait for component to become visible
58+
await waitFor(() => {
59+
expect(screen.getByText("Test content")).toBeInTheDocument()
60+
})
4461
})
4562

4663
it("requests dismissed upsells list on mount", () => {
@@ -63,6 +80,14 @@ describe("DismissibleUpsell", () => {
6380
</DismissibleUpsell>,
6481
)
6582

83+
// Make component visible first
84+
makeUpsellVisible()
85+
86+
// Wait for component to be visible
87+
await waitFor(() => {
88+
expect(screen.getByText("Test content")).toBeInTheDocument()
89+
})
90+
6691
// Find and click the dismiss button
6792
const dismissButton = screen.getByRole("button", { name: /dismiss/i })
6893
fireEvent.click(dismissButton)
@@ -89,6 +114,9 @@ describe("DismissibleUpsell", () => {
89114
</DismissibleUpsell>,
90115
)
91116

117+
// Component starts hidden by default
118+
expect(container.firstChild).toBeNull()
119+
92120
// Simulate receiving a message that this upsell is dismissed
93121
const messageEvent = new MessageEvent("message", {
94122
data: {
@@ -98,7 +126,7 @@ describe("DismissibleUpsell", () => {
98126
})
99127
window.dispatchEvent(messageEvent)
100128

101-
// Check that the component is no longer visible
129+
// Check that the component remains hidden
102130
await waitFor(() => {
103131
expect(container.firstChild).toBeNull()
104132
})
@@ -126,23 +154,39 @@ describe("DismissibleUpsell", () => {
126154
})
127155
})
128156

129-
it("applies the className prop to the container", () => {
157+
it("applies the className prop to the container", async () => {
130158
const { container } = render(
131159
<DismissibleUpsell upsellId="test-upsell" className="custom-class">
132160
<div>Test content</div>
133161
</DismissibleUpsell>,
134162
)
135163

164+
// Make component visible
165+
makeUpsellVisible()
166+
167+
// Wait for component to be visible
168+
await waitFor(() => {
169+
expect(container.firstChild).not.toBeNull()
170+
})
171+
136172
expect(container.firstChild).toHaveClass("custom-class")
137173
})
138174

139-
it("dismiss button has proper accessibility attributes", () => {
175+
it("dismiss button has proper accessibility attributes", async () => {
140176
render(
141177
<DismissibleUpsell upsellId="test-upsell">
142178
<div>Test content</div>
143179
</DismissibleUpsell>,
144180
)
145181

182+
// Make component visible
183+
makeUpsellVisible()
184+
185+
// Wait for component to be visible
186+
await waitFor(() => {
187+
expect(screen.getByText("Test content")).toBeInTheDocument()
188+
})
189+
146190
const dismissButton = screen.getByRole("button", { name: /dismiss/i })
147191
expect(dismissButton).toHaveAttribute("aria-label", "Dismiss")
148192
expect(dismissButton).toHaveAttribute("title", "Dismiss and don't show again")
@@ -157,6 +201,14 @@ describe("DismissibleUpsell", () => {
157201
</DismissibleUpsell>,
158202
)
159203

204+
// Make component visible
205+
makeUpsellVisible()
206+
207+
// Wait for component to be visible
208+
await waitFor(() => {
209+
expect(screen.getByText("Test content")).toBeInTheDocument()
210+
})
211+
160212
const dismissButton = screen.getByRole("button", { name: /dismiss/i })
161213

162214
// Click multiple times rapidly
@@ -202,13 +254,21 @@ describe("DismissibleUpsell", () => {
202254
expect(true).toBe(true)
203255
})
204256

205-
it("handles invalid/malformed messages gracefully", () => {
257+
it("handles invalid/malformed messages gracefully", async () => {
206258
render(
207259
<DismissibleUpsell upsellId="test-upsell">
208260
<div>Test content</div>
209261
</DismissibleUpsell>,
210262
)
211263

264+
// First make it visible
265+
makeUpsellVisible()
266+
267+
// Wait for component to be visible
268+
await waitFor(() => {
269+
expect(screen.getByText("Test content")).toBeInTheDocument()
270+
})
271+
212272
// Send various malformed messages
213273
const malformedMessages = [
214274
{ type: "dismissedUpsells", list: null },
@@ -236,6 +296,14 @@ describe("DismissibleUpsell", () => {
236296
</DismissibleUpsell>,
237297
)
238298

299+
// Make component visible
300+
makeUpsellVisible()
301+
302+
// Wait for component to be visible
303+
await waitFor(() => {
304+
expect(screen.getByText("Test content")).toBeInTheDocument()
305+
})
306+
239307
const dismissButton = screen.getByRole("button", { name: /dismiss/i })
240308
fireEvent.click(dismissButton)
241309

@@ -255,13 +323,21 @@ describe("DismissibleUpsell", () => {
255323
})
256324
})
257325

258-
it("uses separate id and className props correctly", () => {
326+
it("uses separate id and className props correctly", async () => {
259327
const { container } = render(
260328
<DismissibleUpsell upsellId="unique-id" className="styling-class">
261329
<div>Test content</div>
262330
</DismissibleUpsell>,
263331
)
264332

333+
// Make component visible
334+
makeUpsellVisible()
335+
336+
// Wait for component to be visible
337+
await waitFor(() => {
338+
expect(container.firstChild).not.toBeNull()
339+
})
340+
265341
// className should be applied to the container
266342
expect(container.firstChild).toHaveClass("styling-class")
267343

@@ -275,22 +351,30 @@ describe("DismissibleUpsell", () => {
275351
})
276352
})
277353

278-
it("calls onClick when the container is clicked", () => {
354+
it("calls onClick when the container is clicked", async () => {
279355
const onClick = vi.fn()
280356
render(
281357
<DismissibleUpsell upsellId="test-upsell" onClick={onClick}>
282358
<div>Test content</div>
283359
</DismissibleUpsell>,
284360
)
285361

362+
// Make component visible
363+
makeUpsellVisible()
364+
365+
// Wait for component to be visible
366+
await waitFor(() => {
367+
expect(screen.getByText("Test content")).toBeInTheDocument()
368+
})
369+
286370
// Click on the container (not the dismiss button)
287371
const container = screen.getByText("Test content").parentElement as HTMLElement
288372
fireEvent.click(container)
289373

290374
expect(onClick).toHaveBeenCalledTimes(1)
291375
})
292376

293-
it("does not call onClick when dismiss button is clicked", () => {
377+
it("does not call onClick when dismiss button is clicked", async () => {
294378
const onClick = vi.fn()
295379
const onDismiss = vi.fn()
296380
render(
@@ -299,6 +383,14 @@ describe("DismissibleUpsell", () => {
299383
</DismissibleUpsell>,
300384
)
301385

386+
// Make component visible
387+
makeUpsellVisible()
388+
389+
// Wait for component to be visible
390+
await waitFor(() => {
391+
expect(screen.getByText("Test content")).toBeInTheDocument()
392+
})
393+
302394
// Click the dismiss button
303395
const dismissButton = screen.getByRole("button", { name: /dismiss/i })
304396
fireEvent.click(dismissButton)
@@ -308,13 +400,21 @@ describe("DismissibleUpsell", () => {
308400
expect(onDismiss).toHaveBeenCalledTimes(1)
309401
})
310402

311-
it("adds cursor-pointer class when onClick is provided", () => {
403+
it("adds cursor-pointer class when onClick is provided", async () => {
312404
const { container, rerender } = render(
313405
<DismissibleUpsell upsellId="test-upsell" onClick={() => {}}>
314406
<div>Test content</div>
315407
</DismissibleUpsell>,
316408
)
317409

410+
// Make component visible
411+
makeUpsellVisible()
412+
413+
// Wait for component to be visible
414+
await waitFor(() => {
415+
expect(container.firstChild).not.toBeNull()
416+
})
417+
318418
// Should have cursor-pointer when onClick is provided
319419
expect(container.firstChild).toHaveClass("cursor-pointer")
320420

@@ -338,6 +438,14 @@ describe("DismissibleUpsell", () => {
338438
</DismissibleUpsell>,
339439
)
340440

441+
// Make component visible
442+
makeUpsellVisible()
443+
444+
// Wait for component to be visible
445+
await waitFor(() => {
446+
expect(screen.getByText("Test content")).toBeInTheDocument()
447+
})
448+
341449
// Click on the container
342450
const containerDiv = screen.getByText("Test content").parentElement as HTMLElement
343451
fireEvent.click(containerDiv)
@@ -371,6 +479,14 @@ describe("DismissibleUpsell", () => {
371479
</DismissibleUpsell>,
372480
)
373481

482+
// Make component visible
483+
makeUpsellVisible()
484+
485+
// Wait for component to be visible
486+
await waitFor(() => {
487+
expect(screen.getByText("Test content")).toBeInTheDocument()
488+
})
489+
374490
const containerDiv = screen.getByText("Test content").parentElement as HTMLElement
375491
fireEvent.click(containerDiv)
376492

@@ -396,6 +512,14 @@ describe("DismissibleUpsell", () => {
396512
</DismissibleUpsell>,
397513
)
398514

515+
// Make component visible
516+
makeUpsellVisible()
517+
518+
// Wait for component to be visible
519+
await waitFor(() => {
520+
expect(screen.getByText("Test content")).toBeInTheDocument()
521+
})
522+
399523
const containerDiv = screen.getByText("Test content").parentElement as HTMLElement
400524
fireEvent.click(containerDiv)
401525

@@ -415,6 +539,14 @@ describe("DismissibleUpsell", () => {
415539
</DismissibleUpsell>,
416540
)
417541

542+
// Make component visible
543+
makeUpsellVisible()
544+
545+
// Wait for component to be visible
546+
await waitFor(() => {
547+
expect(screen.getByText("Test content")).toBeInTheDocument()
548+
})
549+
418550
const containerDiv = screen.getByText("Test content").parentElement as HTMLElement
419551
fireEvent.click(containerDiv)
420552

0 commit comments

Comments
 (0)