mirror of
https://mirror.skon.top/github.com/langgenius/dify.git
synced 2026-05-01 03:30:02 +08:00
refactor(web): update type definitions and improve test mocks for Popover components
This commit is contained in:
@@ -5,7 +5,12 @@ import DatePicker from '../index'
|
||||
|
||||
vi.mock('@langgenius/dify-ui/popover', async () => await import('@/__mocks__/base-ui-popover'))
|
||||
vi.mock('@langgenius/dify-ui/button', () => ({
|
||||
Button: ({ children, onClick, disabled, className }: Record<string, unknown>) => (
|
||||
Button: ({ children, onClick, disabled, className }: {
|
||||
children?: React.ReactNode
|
||||
onClick?: () => void
|
||||
disabled?: boolean
|
||||
className?: string
|
||||
}) => (
|
||||
<button onClick={onClick as (() => void) | undefined} disabled={disabled as boolean | undefined} className={className as string | undefined}>
|
||||
{children}
|
||||
</button>
|
||||
|
||||
@@ -5,7 +5,12 @@ import TimePicker from '../index'
|
||||
|
||||
vi.mock('@langgenius/dify-ui/popover', async () => await import('@/__mocks__/base-ui-popover'))
|
||||
vi.mock('@langgenius/dify-ui/button', () => ({
|
||||
Button: ({ children, onClick, disabled, className }: Record<string, unknown>) => (
|
||||
Button: ({ children, onClick, disabled, className }: {
|
||||
children?: React.ReactNode
|
||||
onClick?: () => void
|
||||
disabled?: boolean
|
||||
className?: string
|
||||
}) => (
|
||||
<button onClick={onClick as (() => void) | undefined} disabled={disabled as boolean | undefined} className={className as string | undefined}>
|
||||
{children}
|
||||
</button>
|
||||
|
||||
@@ -28,7 +28,7 @@ export type DatePickerProps = {
|
||||
onChange: (date: Dayjs | undefined) => void
|
||||
onClear: () => void
|
||||
triggerWrapClassName?: string
|
||||
renderTrigger?: (props: TriggerProps) => React.ReactNode
|
||||
renderTrigger?: (props: TriggerProps) => React.ReactElement
|
||||
minuteFilter?: (minutes: string[]) => string[]
|
||||
popupZIndexClassname?: string
|
||||
noConfirm?: boolean
|
||||
@@ -62,7 +62,7 @@ export type TimePickerProps = {
|
||||
placeholder?: string
|
||||
onChange: (date: Dayjs | undefined) => void
|
||||
onClear: () => void
|
||||
renderTrigger?: (props: TriggerParams) => React.ReactNode
|
||||
renderTrigger?: (props: TriggerParams) => React.ReactElement
|
||||
title?: string
|
||||
minuteFilter?: (minutes: string[]) => string[]
|
||||
popupClassName?: string
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('FileUploadSettings (setting-modal)', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should call onOpen with toggle function when trigger is clicked', () => {
|
||||
it('should call onOpen with true when trigger is clicked', () => {
|
||||
const onOpen = vi.fn()
|
||||
renderWithProvider(
|
||||
<FileUploadSettings open={false} onOpen={onOpen}>
|
||||
@@ -71,12 +71,7 @@ describe('FileUploadSettings (setting-modal)', () => {
|
||||
|
||||
fireEvent.click(screen.getByText('Upload Settings'))
|
||||
|
||||
expect(onOpen).toHaveBeenCalled()
|
||||
// The toggle function should flip the open state
|
||||
const toggleFn = onOpen.mock.calls[0]![0]
|
||||
expect(typeof toggleFn).toBe('function')
|
||||
expect(toggleFn(false)).toBe(true)
|
||||
expect(toggleFn(true)).toBe(false)
|
||||
expect(onOpen).toHaveBeenCalledWith(true)
|
||||
})
|
||||
|
||||
it('should not call onOpen when disabled', () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { FC } from 'react'
|
||||
import type { Dataset } from './index'
|
||||
import type { EventEmitterValue } from '@/context/event-emitter'
|
||||
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
import { useState } from 'react'
|
||||
@@ -16,11 +17,6 @@ type ContextBlockComponentProps = {
|
||||
canNotAddContext?: boolean
|
||||
}
|
||||
|
||||
type DatasetsEventPayload = {
|
||||
type?: string
|
||||
payload?: Dataset[]
|
||||
}
|
||||
|
||||
const ContextBlockComponent: FC<ContextBlockComponentProps> = ({
|
||||
nodeKey,
|
||||
datasets = [],
|
||||
@@ -33,9 +29,12 @@ const ContextBlockComponent: FC<ContextBlockComponentProps> = ({
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [localDatasets, setLocalDatasets] = useState<Dataset[]>(datasets)
|
||||
|
||||
eventEmitter?.useSubscription((event?: DatasetsEventPayload) => {
|
||||
if (event?.type === UPDATE_DATASETS_EVENT_EMITTER && event.payload)
|
||||
setLocalDatasets(event.payload)
|
||||
eventEmitter?.useSubscription((event?: EventEmitterValue) => {
|
||||
if (typeof event === 'string')
|
||||
return
|
||||
|
||||
if (event?.type === UPDATE_DATASETS_EVENT_EMITTER && Array.isArray(event.payload))
|
||||
setLocalDatasets(event.payload as Dataset[])
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -56,12 +55,14 @@ const ContextBlockComponent: FC<ContextBlockComponentProps> = ({
|
||||
>
|
||||
<PopoverTrigger
|
||||
nativeButton={false}
|
||||
ref={triggerRef}
|
||||
render={(
|
||||
<div className={`
|
||||
<div
|
||||
className={`
|
||||
flex h-[18px] w-[18px] cursor-pointer items-center justify-center rounded text-[11px] font-semibold
|
||||
${open ? 'bg-[#6938EF] text-white' : 'bg-white/50 group-hover:bg-white group-hover:shadow-xs'}
|
||||
`}
|
||||
ref={triggerRef}
|
||||
onClick={e => e.preventDefault()}
|
||||
>
|
||||
{localDatasets.length}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { FC } from 'react'
|
||||
import type { RoleName } from './index'
|
||||
import type { EventEmitterValue } from '@/context/event-emitter'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
import {
|
||||
RiMoreFill,
|
||||
@@ -18,11 +19,6 @@ type HistoryBlockComponentProps = {
|
||||
onEditRole: () => void
|
||||
}
|
||||
|
||||
type HistoryEventPayload = {
|
||||
type?: string
|
||||
payload?: RoleName
|
||||
}
|
||||
|
||||
const HistoryBlockComponent: FC<HistoryBlockComponentProps> = ({
|
||||
nodeKey,
|
||||
roleName = { user: '', assistant: '' },
|
||||
@@ -34,9 +30,12 @@ const HistoryBlockComponent: FC<HistoryBlockComponentProps> = ({
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [localRoleName, setLocalRoleName] = useState<RoleName>(roleName)
|
||||
|
||||
eventEmitter?.useSubscription((event?: HistoryEventPayload) => {
|
||||
if (event?.type === UPDATE_HISTORY_EVENT_EMITTER && event.payload)
|
||||
setLocalRoleName(event.payload)
|
||||
eventEmitter?.useSubscription((event?: EventEmitterValue) => {
|
||||
if (typeof event === 'string')
|
||||
return
|
||||
|
||||
if (event?.type === UPDATE_HISTORY_EVENT_EMITTER && event.payload && typeof event.payload === 'object')
|
||||
setLocalRoleName(event.payload as RoleName)
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -56,12 +55,14 @@ const HistoryBlockComponent: FC<HistoryBlockComponentProps> = ({
|
||||
>
|
||||
<PopoverTrigger
|
||||
nativeButton={false}
|
||||
ref={triggerRef}
|
||||
render={(
|
||||
<div className={`
|
||||
<div
|
||||
className={`
|
||||
flex h-[18px] w-[18px] cursor-pointer items-center justify-center rounded
|
||||
${open ? 'bg-[#DD2590] text-white' : 'bg-white/50 group-hover:bg-white group-hover:shadow-xs'}
|
||||
`}
|
||||
ref={triggerRef}
|
||||
onClick={e => e.preventDefault()}
|
||||
>
|
||||
<RiMoreFill className="h-3 w-3" />
|
||||
</div>
|
||||
|
||||
@@ -231,8 +231,9 @@ describe('StepTwoPreview', () => {
|
||||
describe('Props Passing', () => {
|
||||
it('should render preview button when isIdle is true', () => {
|
||||
render(<StepTwoPreview {...defaultProps} isIdle={true} />)
|
||||
// ChunkPreview shows a preview button when idle
|
||||
const previewButton = screen.queryByRole('button')
|
||||
const previewButton = screen.getByRole('button', {
|
||||
name: 'datasetPipeline.addDocuments.stepTwo.previewChunks',
|
||||
})
|
||||
expect(previewButton).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@@ -240,13 +241,13 @@ describe('StepTwoPreview', () => {
|
||||
const onPreview = vi.fn()
|
||||
render(<StepTwoPreview {...defaultProps} isIdle={true} onPreview={onPreview} />)
|
||||
|
||||
// Find and click the preview button
|
||||
const buttons = screen.getAllByRole('button')
|
||||
const previewButton = buttons.find(btn => btn.textContent?.toLowerCase().includes('preview'))
|
||||
if (previewButton) {
|
||||
previewButton.click()
|
||||
expect(onPreview).toHaveBeenCalled()
|
||||
}
|
||||
const previewButton = screen.getByRole('button', {
|
||||
name: 'datasetPipeline.addDocuments.stepTwo.previewChunks',
|
||||
})
|
||||
|
||||
previewButton.click()
|
||||
|
||||
expect(onPreview).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -62,31 +62,38 @@ vi.mock('@/app/components/plugins/hooks', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock portal-to-follow-elem with shared open state
|
||||
// Mock popover with shared open state
|
||||
let mockPortalOpenState = false
|
||||
let mockPopoverOnOpenChange: ((open: boolean) => void) | undefined
|
||||
|
||||
vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
PortalToFollowElem: ({ children, open }: {
|
||||
vi.mock('@langgenius/dify-ui/popover', () => ({
|
||||
Popover: ({ children, open, onOpenChange }: {
|
||||
children: React.ReactNode
|
||||
open: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}) => {
|
||||
mockPortalOpenState = open
|
||||
mockPopoverOnOpenChange = onOpenChange
|
||||
return (
|
||||
<div data-testid="portal-elem" data-open={open}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
PortalToFollowElemTrigger: ({ children, onClick, className }: {
|
||||
children: React.ReactNode
|
||||
onClick: () => void
|
||||
PopoverTrigger: ({ children, render, className }: {
|
||||
children?: React.ReactNode
|
||||
render?: React.ReactNode
|
||||
className?: string
|
||||
}) => (
|
||||
<div data-testid="portal-trigger" onClick={onClick} className={className}>
|
||||
{children}
|
||||
<div
|
||||
data-testid="portal-trigger"
|
||||
onClick={() => mockPopoverOnOpenChange?.(!mockPortalOpenState)}
|
||||
className={className}
|
||||
>
|
||||
{render ?? children}
|
||||
</div>
|
||||
),
|
||||
PortalToFollowElemContent: ({ children, className }: {
|
||||
PopoverContent: ({ children, className }: {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}) => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Category, Tag } from '../constant'
|
||||
import type { FilterState } from '../index'
|
||||
import { act, fireEvent, render, renderHook, screen, waitFor } from '@testing-library/react'
|
||||
import { createContext, useContext } from 'react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
// ==================== Imports (after mocks) ====================
|
||||
@@ -68,19 +69,47 @@ vi.mock('../../../hooks', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
// Track portal open state for testing
|
||||
let mockPortalOpenState = false
|
||||
type MockPopoverContextValue = {
|
||||
open: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}
|
||||
|
||||
vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
PortalToFollowElem: ({ children, open }: { children: React.ReactNode, open: boolean }) => {
|
||||
mockPortalOpenState = open
|
||||
return <div data-testid="portal-container" data-open={open}>{children}</div>
|
||||
},
|
||||
PortalToFollowElemTrigger: ({ children, onClick }: { children: React.ReactNode, onClick: () => void }) => (
|
||||
<div data-testid="portal-trigger" onClick={onClick}>{children}</div>
|
||||
const MockPopoverContext = createContext<MockPopoverContextValue>({
|
||||
open: false,
|
||||
})
|
||||
|
||||
vi.mock('@langgenius/dify-ui/popover', () => ({
|
||||
Popover: ({ children, open, onOpenChange }: {
|
||||
children: React.ReactNode
|
||||
open: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}) => (
|
||||
<MockPopoverContext.Provider value={{ open, onOpenChange }}>
|
||||
<div data-testid="portal-container" data-open={open}>{children}</div>
|
||||
</MockPopoverContext.Provider>
|
||||
),
|
||||
PortalToFollowElemContent: ({ children, className }: { children: React.ReactNode, className?: string }) => {
|
||||
if (!mockPortalOpenState)
|
||||
PopoverTrigger: ({ children, render, className }: {
|
||||
children?: React.ReactNode
|
||||
render?: React.ReactNode
|
||||
className?: string
|
||||
}) => {
|
||||
const { open, onOpenChange } = useContext(MockPopoverContext)
|
||||
return (
|
||||
<div
|
||||
data-testid="portal-trigger"
|
||||
onClick={() => onOpenChange?.(!open)}
|
||||
className={className}
|
||||
>
|
||||
{render ?? children}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
PopoverContent: ({ children, className }: {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}) => {
|
||||
const { open } = useContext(MockPopoverContext)
|
||||
if (!open)
|
||||
return null
|
||||
return <div data-testid="portal-content" className={className}>{children}</div>
|
||||
},
|
||||
@@ -457,7 +486,6 @@ describe('SearchBox Component', () => {
|
||||
describe('CategoriesFilter Component', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockPortalOpenState = false
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
@@ -694,7 +722,6 @@ describe('CategoriesFilter Component', () => {
|
||||
describe('TagFilter Component', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockPortalOpenState = false
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
@@ -857,7 +884,6 @@ describe('FilterManagement Component', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockInitFilters = createFilterState()
|
||||
mockPortalOpenState = false
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
|
||||
@@ -192,27 +192,6 @@ vi.mock('ahooks', () => ({
|
||||
useKeyPress: vi.fn(),
|
||||
}))
|
||||
|
||||
let portalOpenState = false
|
||||
vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
PortalToFollowElem: ({ children, open, onOpenChange: _onOpenChange }: PropsWithChildren<{
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
placement?: string
|
||||
offset?: unknown
|
||||
}>) => {
|
||||
portalOpenState = open
|
||||
return <div data-testid="portal-elem" data-open={open}>{children}</div>
|
||||
},
|
||||
PortalToFollowElemTrigger: ({ children, onClick }: PropsWithChildren<{ onClick?: () => void }>) => (
|
||||
<div data-testid="portal-trigger" onClick={onClick}>{children}</div>
|
||||
),
|
||||
PortalToFollowElemContent: ({ children }: PropsWithChildren) => {
|
||||
if (!portalOpenState)
|
||||
return null
|
||||
return <div data-testid="portal-content">{children}</div>
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('../../../publish-as-knowledge-pipeline-modal', () => ({
|
||||
default: ({ onConfirm, onCancel }: {
|
||||
onConfirm: (name: string, icon: unknown, description?: string) => void
|
||||
@@ -229,7 +208,6 @@ vi.mock('../../../publish-as-knowledge-pipeline-modal', () => ({
|
||||
describe('RagPipelineHeader', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
portalOpenState = false
|
||||
mockStoreState = {
|
||||
pipelineId: 'test-pipeline-id',
|
||||
showDebugAndPreviewPanel: false,
|
||||
@@ -351,7 +329,6 @@ describe('InputFieldButton', () => {
|
||||
describe('Publisher', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
portalOpenState = false
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
@@ -367,9 +344,9 @@ describe('Publisher', () => {
|
||||
expect(button)!.toHaveClass('px-2')
|
||||
})
|
||||
|
||||
it('should render portal trigger element', () => {
|
||||
it('should render publish trigger button', () => {
|
||||
render(<Publisher />)
|
||||
expect(screen.getByTestId('portal-trigger'))!.toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: /workflow\.common\.publish/i }))!.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -377,7 +354,7 @@ describe('Publisher', () => {
|
||||
it('should call handleSyncWorkflowDraft when opening', () => {
|
||||
render(<Publisher />)
|
||||
|
||||
fireEvent.click(screen.getByTestId('portal-trigger'))
|
||||
fireEvent.click(screen.getByRole('button', { name: /workflow\.common\.publish/i }))
|
||||
|
||||
expect(mockHandleSyncWorkflowDraft).toHaveBeenCalledWith(true)
|
||||
})
|
||||
@@ -385,12 +362,14 @@ describe('Publisher', () => {
|
||||
it('should toggle open state when trigger clicked', () => {
|
||||
render(<Publisher />)
|
||||
|
||||
const portal = screen.getByTestId('portal-elem')
|
||||
expect(portal)!.toHaveAttribute('data-open', 'false')
|
||||
const trigger = screen.getByRole('button', { name: /workflow\.common\.publish/i })
|
||||
expect(trigger)!.toHaveAttribute('aria-expanded', 'false')
|
||||
|
||||
fireEvent.click(screen.getByTestId('portal-trigger'))
|
||||
fireEvent.click(trigger)
|
||||
|
||||
expect(mockHandleSyncWorkflowDraft).toHaveBeenCalled()
|
||||
expect(trigger)!.toHaveAttribute('aria-expanded', 'true')
|
||||
expect(screen.getByText(/workflow\.common\.publishUpdate/i))!.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -978,7 +957,6 @@ describe('RunMode', () => {
|
||||
describe('Integration', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
portalOpenState = false
|
||||
mockStoreState = {
|
||||
pipelineId: 'test-pipeline-id',
|
||||
showDebugAndPreviewPanel: false,
|
||||
|
||||
@@ -16,12 +16,16 @@ vi.mock('@langgenius/dify-ui/alert-dialog', () => ({
|
||||
)
|
||||
: null
|
||||
),
|
||||
AlertDialogActions: ({ children }: { children: unknown }) => <div>{children}</div>,
|
||||
AlertDialogCancelButton: ({ children }: { children: unknown }) => <button>{children}</button>,
|
||||
AlertDialogConfirmButton: ({ children, onClick, disabled }: Record<string, unknown>) => <button onClick={onClick as (() => void) | undefined} disabled={disabled as boolean | undefined}>{children}</button>,
|
||||
AlertDialogContent: ({ children }: { children: unknown }) => <div>{children}</div>,
|
||||
AlertDialogDescription: ({ children }: { children: unknown }) => <div>{children}</div>,
|
||||
AlertDialogTitle: ({ children }: { children: unknown }) => <div>{children}</div>,
|
||||
AlertDialogActions: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
|
||||
AlertDialogCancelButton: ({ children }: { children?: React.ReactNode }) => <button>{children}</button>,
|
||||
AlertDialogConfirmButton: ({ children, onClick, disabled }: {
|
||||
children?: React.ReactNode
|
||||
onClick?: () => void
|
||||
disabled?: boolean
|
||||
}) => <button onClick={onClick} disabled={disabled}>{children}</button>,
|
||||
AlertDialogContent: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
|
||||
AlertDialogDescription: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
|
||||
AlertDialogTitle: ({ children }: { children?: React.ReactNode }) => <div>{children}</div>,
|
||||
}))
|
||||
|
||||
const mockPublishWorkflow = vi.fn().mockResolvedValue({ created_at: '2024-01-01T00:00:00Z' })
|
||||
|
||||
@@ -27,7 +27,7 @@ const Publisher = () => {
|
||||
onOpenChange={handleOpenChange}
|
||||
>
|
||||
<PopoverTrigger
|
||||
nativeButton={false}
|
||||
nativeButton
|
||||
render={(
|
||||
<Button
|
||||
className="px-2"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ const ColorPicker = ({
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
<PopoverTrigger
|
||||
nativeButton={false}
|
||||
nativeButton
|
||||
render={(
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -39,7 +39,7 @@ const FontSizeSelector = () => {
|
||||
onOpenChange={handleOpenFontSizeSelector}
|
||||
>
|
||||
<PopoverTrigger
|
||||
nativeButton={false}
|
||||
nativeButton
|
||||
render={(
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user