fix(web): name log sort and resend controls

This commit is contained in:
yyh
2026-05-10 22:48:50 +08:00
parent 329d1dec1a
commit ef152db4a6
6 changed files with 46 additions and 32 deletions

View File

@ -117,11 +117,9 @@ describe('DetailPanel', () => {
})
it('should render close button', () => {
const { container } = render(<DetailPanel runID="run-123" onClose={defaultOnClose} />)
render(<DetailPanel runID="run-123" onClose={defaultOnClose} />)
// Close button has RiCloseLine icon
const closeButton = container.querySelector('span.cursor-pointer')
expect(closeButton).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'common.operation.close' })).toBeInTheDocument()
})
it('should render Run component with correct URLs', () => {
@ -173,12 +171,11 @@ describe('DetailPanel', () => {
const user = userEvent.setup()
const onClose = vi.fn()
const { container } = render(<DetailPanel runID="run-123" onClose={onClose} />)
render(<DetailPanel runID="run-123" onClose={onClose} />)
const closeButton = container.querySelector('span.cursor-pointer')
expect(closeButton).toBeInTheDocument()
const closeButton = screen.getByRole('button', { name: 'common.operation.close' })
await user.click(closeButton!)
await user.click(closeButton)
expect(onClose).toHaveBeenCalledTimes(1)
})

View File

@ -27,9 +27,14 @@ const DetailPanel: FC<ILogDetail> = ({ runID, onClose, canReplay = false }) => {
return (
<div className="relative flex grow flex-col pt-3">
<span className="absolute top-4 right-3 z-20 cursor-pointer p-1" onClick={onClose}>
<RiCloseLine className="h-4 w-4 text-text-tertiary" />
</span>
<button
type="button"
aria-label={t('operation.close', { ns: 'common' })}
className="absolute top-4 right-3 z-20 cursor-pointer border-none bg-transparent p-1 focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:outline-hidden"
onClick={onClose}
>
<RiCloseLine className="h-4 w-4 text-text-tertiary" aria-hidden="true" />
</button>
<div className="flex items-center bg-components-panel-bg">
<h1 className="shrink-0 px-4 py-1 system-xl-semibold text-text-primary">{t('runDetail.workflowTitle', { ns: 'appLog' })}</h1>
{canReplay && (
@ -38,11 +43,11 @@ const DetailPanel: FC<ILogDetail> = ({ runID, onClose, canReplay = false }) => {
render={(
<button
type="button"
className="mr-1 flex h-6 w-6 items-center justify-center rounded-md hover:bg-state-base-hover"
className="mr-1 flex h-6 w-6 items-center justify-center rounded-md border-none bg-transparent p-0 hover:bg-state-base-hover"
aria-label={t('runDetail.testWithParams', { ns: 'appLog' })}
onClick={handleReplay}
>
<RiPlayLargeLine className="h-4 w-4 text-text-tertiary" />
<RiPlayLargeLine className="h-4 w-4 text-text-tertiary" aria-hidden="true" />
</button>
)}
/>

View File

@ -11,10 +11,10 @@ const mockItems = [
]
describe('Sort component — real portal integration', () => {
const setup = (props = {}) => {
const setup = (props: Partial<React.ComponentProps<typeof Sort>> = {}) => {
const onSelect = vi.fn()
const user = userEvent.setup()
const { container, rerender } = render(
const { rerender } = render(
<Sort value="created_at" items={mockItems} onSelect={onSelect} order="" {...props} />,
)
@ -28,10 +28,9 @@ describe('Sort component — real portal integration', () => {
// helper: returns right-side sort button element
const getSortButton = (): HTMLElement => {
const btn = container.querySelector('.rounded-r-lg')
if (!btn)
throw new Error('Sort button (rounded-r-lg) not found in rendered container')
return btn as HTMLElement
return screen.getByRole('button', {
name: props.order ? 'appLog.filter.ascending' : 'appLog.filter.descending',
})
}
return { user, onSelect, rerender, getTriggerWrapper, getSortButton }

View File

@ -85,10 +85,15 @@ const Sort: FC<Props> = ({
</DropdownMenuContent>
</div>
</DropdownMenu>
<div className="ml-px cursor-pointer rounded-r-lg bg-components-button-tertiary-bg p-2 hover:bg-components-button-tertiary-bg-hover" onClick={() => onSelect(`${order ? '' : '-'}${value}`)}>
{!order && <RiSortAsc className="h-4 w-4 text-components-button-tertiary-text" />}
{order && <RiSortDesc className="h-4 w-4 text-components-button-tertiary-text" />}
</div>
<button
type="button"
aria-label={t(`filter.${order ? 'ascending' : 'descending'}`, { ns: 'appLog' })}
className="ml-px cursor-pointer rounded-r-lg border-none bg-components-button-tertiary-bg p-2 hover:bg-components-button-tertiary-bg-hover focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:outline-hidden"
onClick={() => onSelect(`${order ? '' : '-'}${value}`)}
>
{!order && <RiSortAsc className="h-4 w-4 text-components-button-tertiary-text" aria-hidden="true" />}
{order && <RiSortDesc className="h-4 w-4 text-components-button-tertiary-text" aria-hidden="true" />}
</button>
</div>
)

View File

@ -31,7 +31,7 @@ describe('Countdown', () => {
localStorage.setItem(COUNT_DOWN_KEY, '0')
render(<Countdown />)
expect(screen.getByText('login.checkCode.resend')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'login.checkCode.resend' })).toBeInTheDocument()
expect(screen.queryByText('s')).not.toBeInTheDocument()
})
@ -39,7 +39,7 @@ describe('Countdown', () => {
localStorage.setItem(COUNT_DOWN_KEY, '1000')
render(<Countdown />)
expect(screen.queryByText('login.checkCode.resend')).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: 'login.checkCode.resend' })).not.toBeInTheDocument()
})
})
@ -79,7 +79,7 @@ describe('Countdown', () => {
render(<Countdown onResend={onResend} />)
const resendLink = screen.getByText('login.checkCode.resend')
const resendLink = screen.getByRole('button', { name: 'login.checkCode.resend' })
fireEvent.click(resendLink)
expect(onResend).toHaveBeenCalledTimes(1)
@ -90,7 +90,7 @@ describe('Countdown', () => {
render(<Countdown />)
const resendLink = screen.getByText('login.checkCode.resend')
const resendLink = screen.getByRole('button', { name: 'login.checkCode.resend' })
fireEvent.click(resendLink)
expect(localStorage.setItem).toHaveBeenCalledWith(COUNT_DOWN_KEY, String(COUNT_DOWN_TIME_MS))
@ -101,7 +101,7 @@ describe('Countdown', () => {
render(<Countdown />)
const resendLink = screen.getByText('login.checkCode.resend')
const resendLink = screen.getByRole('button', { name: 'login.checkCode.resend' })
expect(() => fireEvent.click(resendLink)).not.toThrow()
})
})
@ -127,14 +127,14 @@ describe('Countdown', () => {
localStorage.setItem(COUNT_DOWN_KEY, '0')
render(<Countdown />)
expect(screen.getByText('login.checkCode.resend')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'login.checkCode.resend' })).toBeInTheDocument()
})
it('should handle negative time values', () => {
localStorage.setItem(COUNT_DOWN_KEY, '-1000')
render(<Countdown />)
expect(screen.getByText('login.checkCode.resend')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'login.checkCode.resend' })).toBeInTheDocument()
})
it('should round time display correctly', () => {
@ -160,7 +160,7 @@ describe('Countdown', () => {
render(<Countdown onResend={onResend} />)
expect(screen.getByText('login.checkCode.resend')).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'login.checkCode.resend' })).toBeInTheDocument()
})
it('should render correctly without any props', () => {

View File

@ -41,7 +41,15 @@ export default function Countdown({ onResend }: CountdownProps) {
</span>
)}
{
time <= 0 && <span className="cursor-pointer system-xs-medium text-text-accent-secondary" onClick={resend}>{t('checkCode.resend', { ns: 'login' })}</span>
time <= 0 && (
<button
type="button"
className="cursor-pointer border-none bg-transparent p-0 system-xs-medium text-text-accent-secondary focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:outline-hidden"
onClick={resend}
>
{t('checkCode.resend', { ns: 'login' })}
</button>
)
}
</p>
)