fix(web): stabilize learn dify tour target

This commit is contained in:
Jingyi-Dify
2026-07-07 18:16:06 -07:00
parent 7419010a55
commit 7973b02edc
4 changed files with 68 additions and 0 deletions

View File

@ -888,6 +888,7 @@ describe('AppList', () => {
mockStepByStepTour.setUiState({
activeTaskId: 'home',
activeGuideIndex: 0,
activeGuideIndexes: [0, 1],
minimized: true,
})
@ -931,6 +932,7 @@ describe('AppList', () => {
expect(state?.activeTaskId).toBeUndefined()
expect(state?.activeGuideIndex).toBeUndefined()
expect(state?.activeGuideGroup).toBeUndefined()
expect(state?.activeGuideIndexes).toBeUndefined()
expect(state?.completedTaskIds).toEqual(['home'])
expect(state?.minimized).toBe(false)
})
@ -945,6 +947,7 @@ describe('AppList', () => {
mockStepByStepTour.setUiState({
activeTaskId: 'home',
activeGuideIndex: 0,
activeGuideIndexes: [0, 1],
minimized: true,
});
(fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml-content', mode: AppModeEnum.CHAT })
@ -962,6 +965,7 @@ describe('AppList', () => {
const state = mockStepByStepTour.observedState
expect(state?.activeTaskId).toBeUndefined()
expect(state?.activeGuideIndex).toBeUndefined()
expect(state?.activeGuideIndexes).toBeUndefined()
expect(state?.completedTaskIds).toEqual(['home'])
})
expect(mockHandleImportDSL).toHaveBeenCalledWith(
@ -1016,6 +1020,7 @@ describe('AppList', () => {
mockStepByStepTour.setUiState({
activeTaskId: 'home',
activeGuideIndex: 0,
activeGuideIndexes: [0, 1],
minimized: true,
})
@ -1038,6 +1043,7 @@ describe('AppList', () => {
const state = mockStepByStepTour.observedState
expect(state?.activeTaskId).toBeUndefined()
expect(state?.activeGuideIndex).toBeUndefined()
expect(state?.activeGuideIndexes).toBeUndefined()
expect(state?.completedTaskIds).toEqual([])
})
expect(screen.queryByTestId('try-app-panel')).not.toBeInTheDocument()

View File

@ -267,6 +267,7 @@ const Apps = ({ onSuccess }: { onSuccess?: () => void }) => {
activeTaskId: undefined,
activeGuideIndex: undefined,
activeGuideGroup: undefined,
activeGuideIndexes: undefined,
minimized: true,
})
}, [setStepByStepTourAccountState, stepByStepTourAccountState])
@ -280,6 +281,7 @@ const Apps = ({ onSuccess }: { onSuccess?: () => void }) => {
activeTaskId: undefined,
activeGuideIndex: undefined,
activeGuideGroup: undefined,
activeGuideIndexes: undefined,
minimized: false,
})
stepByStepTourActions.completeTask(HOME_STEP_BY_STEP_TOUR_TASK_ID, {
@ -304,6 +306,7 @@ const Apps = ({ onSuccess }: { onSuccess?: () => void }) => {
activeTaskId: undefined,
activeGuideIndex: undefined,
activeGuideGroup: undefined,
activeGuideIndexes: undefined,
minimized: false,
})
stepByStepTourActions.completeTask(HOME_STEP_BY_STEP_TOUR_TASK_ID, {

View File

@ -1190,6 +1190,63 @@ describe('StepByStepTourMount', () => {
}
})
it('renders the coachmark when the target attribute is added to an existing element', async () => {
const animationFrameCallbacks: FrameRequestCallback[] = []
const requestAnimationFrameSpy = vi.spyOn(window, 'requestAnimationFrame').mockImplementation((callback) => {
animationFrameCallbacks.push(callback)
return animationFrameCallbacks.length
})
const cancelAnimationFrameSpy = vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {})
setStepByStepTourTestState({
activeTaskId: 'home',
activeGuideIndex: 1,
manuallyEnabledWorkspaceIds: ['workspace-1'],
manuallyDisabledWorkspaceIds: [],
minimized: true,
completedTaskIds: [],
skipped: false,
})
const target = document.createElement('button')
target.type = 'button'
target.textContent = 'Create from sample app'
target.getBoundingClientRect = () => ({
bottom: 280,
height: 40,
left: 980,
right: 1300,
top: 240,
width: 320,
x: 980,
y: 240,
toJSON: () => ({}),
})
document.body.appendChild(target)
const flushAnimationFrames = () => {
act(() => {
animationFrameCallbacks.splice(0).forEach(callback => callback(0))
})
}
try {
renderStepByStepTourMount()
flushAnimationFrames()
expect(screen.queryByText('Click here to make it yours')).not.toBeInTheDocument()
target.dataset.stepByStepTourTarget = STEP_BY_STEP_TOUR_TARGETS.homeTryAppCreate
await act(async () => {
await Promise.resolve()
})
flushAnimationFrames()
expect(await screen.findByText('Click here to make it yours')).toBeInTheDocument()
}
finally {
requestAnimationFrameSpy.mockRestore()
cancelAnimationFrameSpy.mockRestore()
target.remove()
}
})
it('measures the coachmark size without observing the coachmark element', async () => {
const resizeObservers: Array<{
observedElements: Element[]

View File

@ -45,6 +45,8 @@ export const useStepByStepTourTarget = (target?: string) => {
const observer = new MutationObserver(scheduleSyncTarget)
observer.observe(document.body, {
attributeFilter: ['data-step-by-step-tour-target'],
attributes: true,
childList: true,
subtree: true,
})