chore: add tracking info of in site message (#33394)

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Joel
2026-03-13 15:29:24 +08:00
committed by GitHub
parent fe561ef3d0
commit 97776eabff
4 changed files with 42 additions and 11 deletions

View File

@ -1,7 +1,13 @@
import type { ComponentProps } from 'react'
import type { InSiteMessageActionItem } from './index'
import { fireEvent, render, screen } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import InSiteMessage from './index'
vi.mock('@/app/components/base/amplitude', () => ({
trackEvent: vi.fn(),
}))
describe('InSiteMessage', () => {
const originalLocation = window.location
@ -18,9 +24,10 @@ describe('InSiteMessage', () => {
vi.unstubAllGlobals()
})
const renderComponent = (actions: InSiteMessageActionItem[], props?: Partial<React.ComponentProps<typeof InSiteMessage>>) => {
const renderComponent = (actions: InSiteMessageActionItem[], props?: Partial<ComponentProps<typeof InSiteMessage>>) => {
return render(
<InSiteMessage
notificationId="test-notification-id"
title="Title\\nLine"
subtitle="Subtitle\\nLine"
main="Main content"
@ -34,8 +41,8 @@ describe('InSiteMessage', () => {
describe('Rendering', () => {
it('should render title, subtitle, markdown content, and action buttons', () => {
const actions: InSiteMessageActionItem[] = [
{ action: 'close', text: 'Close', type: 'default' },
{ action: 'link', text: 'Learn more', type: 'primary', data: 'https://example.com' },
{ action: 'close', action_name: 'dismiss', text: 'Close', type: 'default' },
{ action: 'link', action_name: 'learn_more', text: 'Learn more', type: 'primary', data: 'https://example.com' },
]
renderComponent(actions, { className: 'custom-message' })
@ -56,7 +63,7 @@ describe('InSiteMessage', () => {
})
it('should fallback to default header background when headerBgUrl is empty string', () => {
const actions: InSiteMessageActionItem[] = [{ action: 'close', text: 'Close', type: 'default' }]
const actions: InSiteMessageActionItem[] = [{ action: 'close', action_name: 'dismiss', text: 'Close', type: 'default' }]
const { container } = renderComponent(actions, { headerBgUrl: '' })
const header = container.querySelector('div[style]')
@ -68,7 +75,7 @@ describe('InSiteMessage', () => {
describe('Actions', () => {
it('should call onAction and hide component when close action is clicked', () => {
const onAction = vi.fn()
const closeAction: InSiteMessageActionItem = { action: 'close', text: 'Close', type: 'default' }
const closeAction: InSiteMessageActionItem = { action: 'close', action_name: 'dismiss', text: 'Close', type: 'default' }
renderComponent([closeAction], { onAction })
fireEvent.click(screen.getByRole('button', { name: 'Close' }))
@ -80,6 +87,7 @@ describe('InSiteMessage', () => {
it('should open a new tab when link action data is a string', () => {
const linkAction: InSiteMessageActionItem = {
action: 'link',
action_name: 'confirm',
text: 'Open link',
type: 'primary',
data: 'https://example.com',
@ -103,6 +111,7 @@ describe('InSiteMessage', () => {
const linkAction: InSiteMessageActionItem = {
action: 'link',
action_name: 'confirm',
text: 'Open self',
type: 'primary',
data: { href: 'https://example.com/self', target: '_self' },
@ -118,6 +127,7 @@ describe('InSiteMessage', () => {
it('should not trigger navigation when link data is invalid', () => {
const linkAction: InSiteMessageActionItem = {
action: 'link',
action_name: 'confirm',
text: 'Broken link',
type: 'primary',
data: { rel: 'noopener' },

View File

@ -1,6 +1,7 @@
'use client'
import { useMemo, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { trackEvent } from '@/app/components/base/amplitude'
import Button from '@/app/components/base/button'
import { MarkdownWithDirective } from '@/app/components/base/markdown-with-directive'
import { cn } from '@/utils/classnames'
@ -10,12 +11,14 @@ type InSiteMessageButtonType = 'primary' | 'default'
export type InSiteMessageActionItem = {
action: InSiteMessageAction
action_name: string // for tracing and analytics
data?: unknown
text: string
type: InSiteMessageButtonType
}
type InSiteMessageProps = {
notificationId: string
actions: InSiteMessageActionItem[]
className?: string
headerBgUrl?: string
@ -52,6 +55,7 @@ function normalizeLinkData(data: unknown): { href: string, rel?: string, target?
const DEFAULT_HEADER_BG_URL = '/in-site-message/header-bg.svg'
function InSiteMessage({
notificationId,
actions,
className,
headerBgUrl = DEFAULT_HEADER_BG_URL,
@ -70,7 +74,17 @@ function InSiteMessage({
}
}, [headerBgUrl])
useEffect(() => {
trackEvent('in_site_message_show', {
notification_id: notificationId,
})
}, [notificationId])
const handleAction = (item: InSiteMessageActionItem) => {
trackEvent('in_site_message_action', {
notification_id: notificationId,
action: item.action_name,
})
onAction?.(item)
if (item.action === 'close') {

View File

@ -15,11 +15,16 @@ const {
mockNotificationDismiss: vi.fn(),
}))
vi.mock('@/config', () => ({
get IS_CLOUD_EDITION() {
return mockConfig.isCloudEdition
},
}))
vi.mock(import('@/config'), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
get IS_CLOUD_EDITION() {
return mockConfig.isCloudEdition
},
}
})
vi.mock('@/service/client', () => ({
consoleQuery: {

View File

@ -75,6 +75,7 @@ function InSiteMessageNotification() {
const fallbackActions: InSiteMessageActionItem[] = [
{
type: 'default',
action_name: 'dismiss',
text: t('operation.close', { ns: 'common' }),
action: 'close',
},
@ -96,6 +97,7 @@ function InSiteMessageNotification() {
return (
<InSiteMessage
key={notification.notification_id}
notificationId={notification.notification_id}
title={notification.title}
subtitle={notification.subtitle}
headerBgUrl={notification.title_pic_url}