refactor(web): continue replacing PortalToFollowElem with Popover components (#35431)

Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Coding On Star
2026-04-21 10:44:52 +08:00
committed by GitHub
parent da00de6688
commit d583b1b835
78 changed files with 2441 additions and 1893 deletions

View File

@@ -1,32 +1,32 @@
import { fireEvent, render, waitFor } from '@testing-library/react'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { NoteTheme } from '../../../types'
import ColorPicker, { COLOR_LIST } from '../color-picker'
import ColorPicker from '../color-picker'
vi.mock('@langgenius/dify-ui/popover', () => import('@/__mocks__/base-ui-popover'))
describe('NoteEditor ColorPicker', () => {
it('should open the palette and apply the selected theme', async () => {
const onThemeChange = vi.fn()
const { container } = render(
render(
<ColorPicker theme={NoteTheme.blue} onThemeChange={onThemeChange} />,
)
const trigger = container.querySelector('[data-state="closed"]') as HTMLElement
fireEvent.click(screen.getByTestId('popover-trigger'))
fireEvent.click(trigger)
const popup = document.body.querySelector('[role="tooltip"]')
const popup = screen.getByTestId('popover-content')
expect(popup).toBeInTheDocument()
const options = popup?.querySelectorAll('.group.relative')
const options = popup.querySelectorAll('.group.relative')
expect(options).toHaveLength(COLOR_LIST.length)
expect(options).toHaveLength(6)
fireEvent.click(options?.[COLOR_LIST.length - 1] as Element)
fireEvent.click(options[5] as Element)
expect(onThemeChange).toHaveBeenCalledWith(NoteTheme.violet)
await waitFor(() => {
expect(document.body.querySelector('[role="tooltip"]')).not.toBeInTheDocument()
expect(screen.queryByTestId('popover-content')).not.toBeInTheDocument()
})
})
})

View File

@@ -1,6 +1,8 @@
import { fireEvent, render, screen } from '@testing-library/react'
import FontSizeSelector from '../font-size-selector'
vi.mock('@langgenius/dify-ui/popover', () => import('@/__mocks__/base-ui-popover'))
const {
mockHandleFontSize,
mockHandleOpenFontSizeSelector,
@@ -52,4 +54,12 @@ describe('NoteEditor FontSizeSelector', () => {
expect(mockHandleFontSize).toHaveBeenCalledWith('16px')
expect(mockHandleOpenFontSizeSelector).toHaveBeenCalledWith(false)
})
it('should fall back to the small label when current font size is unknown', () => {
mockFontSize = '18px'
render(<FontSizeSelector />)
expect(screen.getByText('workflow.nodes.note.editor.small')).toBeInTheDocument()
})
})

View File

@@ -76,11 +76,14 @@ describe('NoteEditor Toolbar', () => {
expect(screen.getByText('workflow.nodes.note.editor.medium')).toBeInTheDocument()
const triggers = container.querySelectorAll('[data-state="closed"]')
const buttons = container.querySelectorAll('button[type="button"]')
fireEvent.click(buttons[0] as HTMLElement)
fireEvent.click(triggers[0] as HTMLElement)
await waitFor(() => {
expect(document.body.querySelectorAll('.group.relative').length).toBeGreaterThan(0)
})
const colorOptions = document.body.querySelectorAll('[role="tooltip"] .group.relative')
const colorOptions = document.body.querySelectorAll('.group.relative')
fireEvent.click(colorOptions[colorOptions.length - 1] as Element)

View File

@@ -1,17 +1,17 @@
import { cn } from '@langgenius/dify-ui/cn'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import {
memo,
useState,
} from 'react'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { THEME_MAP } from '../../constants'
import { NoteTheme } from '../../types'
export const COLOR_LIST = [
const COLOR_LIST = [
{
key: NoteTheme.blue,
inner: THEME_MAP[NoteTheme.blue]!.title,
@@ -55,28 +55,35 @@ const ColorPicker = ({
const [open, setOpen] = useState(false)
return (
<PortalToFollowElem
<Popover
open={open}
onOpenChange={setOpen}
placement="top"
offset={4}
>
<PortalToFollowElemTrigger onClick={() => setOpen(!open)}>
<div className={cn(
'flex h-8 w-8 cursor-pointer items-center justify-center rounded-md hover:bg-black/5',
open && 'bg-black/5',
)}
>
<div
<PopoverTrigger
nativeButton
render={(
<button
type="button"
className={cn(
'h-4 w-4 rounded-full border border-black/5',
THEME_MAP[theme]!.title,
'flex h-8 w-8 cursor-pointer items-center justify-center rounded-md hover:bg-black/5',
open && 'bg-black/5',
)}
>
</div>
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent>
<div
className={cn(
'h-4 w-4 rounded-full border border-black/5',
THEME_MAP[theme]!.title,
)}
>
</div>
</button>
)}
/>
<PopoverContent
placement="top"
sideOffset={4}
popupClassName="border-none bg-transparent shadow-none"
>
<div className="grid grid-cols-3 grid-rows-2 gap-0.5 rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-lg">
{
COLOR_LIST.map(color => (
@@ -107,8 +114,8 @@ const ColorPicker = ({
))
}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
</PopoverContent>
</Popover>
)
}

View File

@@ -1,13 +1,13 @@
import { cn } from '@langgenius/dify-ui/cn'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import { RiFontSize } from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import { Check } from '@/app/components/base/icons/src/vender/line/general'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { useFontSize } from './hooks'
const FontSizeSelector = () => {
@@ -34,23 +34,30 @@ const FontSizeSelector = () => {
} = useFontSize()
return (
<PortalToFollowElem
<Popover
open={fontSizeSelectorShow}
onOpenChange={handleOpenFontSizeSelector}
placement="bottom-start"
offset={2}
>
<PortalToFollowElemTrigger onClick={() => handleOpenFontSizeSelector(!fontSizeSelectorShow)}>
<div className={cn(
'flex h-8 cursor-pointer items-center rounded-md pr-1.5 pl-2 text-[13px] font-medium text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
fontSizeSelectorShow && 'bg-state-base-hover text-text-secondary',
<PopoverTrigger
nativeButton
render={(
<button
type="button"
className={cn(
'flex h-8 cursor-pointer items-center rounded-md pr-1.5 pl-2 text-[13px] font-medium text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
fontSizeSelectorShow && 'bg-state-base-hover text-text-secondary',
)}
>
<RiFontSize className="mr-1 h-4 w-4" />
{FONT_SIZE_LIST.find(font => font.key === fontSize)?.value || t('nodes.note.editor.small', { ns: 'workflow' })}
</button>
)}
>
<RiFontSize className="mr-1 h-4 w-4" />
{FONT_SIZE_LIST.find(font => font.key === fontSize)?.value || t('nodes.note.editor.small', { ns: 'workflow' })}
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent>
/>
<PopoverContent
placement="bottom-start"
sideOffset={2}
popupClassName="border-none bg-transparent shadow-none"
>
<div className="w-[120px] rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 text-text-secondary shadow-xl">
{
FONT_SIZE_LIST.map(font => (
@@ -77,8 +84,8 @@ const FontSizeSelector = () => {
))
}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
</PopoverContent>
</Popover>
)
}