refactor: normalize search input and dify-ui focus states (#37413)

This commit is contained in:
yyh
2026-06-15 01:03:31 +00:00
committed by GitHub
parent fbfbbda245
commit 8eb6a19784
14 changed files with 374 additions and 278 deletions
-10
View File
@@ -217,16 +217,6 @@ function List({
placeholder={t('operation.search', { ns: 'common' })}
aria-label={t('gotoAnything.actions.searchApplications', { ns: 'app' })}
/>
{!!keywords && (
<button
type="button"
aria-label={t('operation.clear', { ns: 'common' })}
className="absolute top-1/2 right-2 flex size-4 -translate-y-1/2 items-center justify-center text-components-input-text-placeholder hover:text-components-input-text-filled"
onClick={() => setKeywords('')}
>
<span aria-hidden className="i-ri-close-circle-fill size-4" />
</button>
)}
</div>
</div>
<Link
+1 -1
View File
@@ -71,7 +71,7 @@ export function Infotip({
aria-label={ariaLabel}
onClick={handleClick}
className={cn(
'inline-flex size-4 shrink-0 cursor-pointer items-center justify-center border-0 bg-transparent p-0 focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden',
'inline-flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid',
className,
)}
>
@@ -35,6 +35,23 @@ describe('SearchInput', () => {
const clearButton = screen.getByLabelText('common.operation.clear')
expect(clearButton).toBeInTheDocument()
})
it('uses the design-system focus treatment for the clear button', () => {
render(<SearchInput value="has value" onValueChange={() => {}} />)
const clearButton = screen.getByRole('button', { name: 'common.operation.clear' })
expect(clearButton).toHaveClass(
'right-1.5',
'size-5',
'focus-visible:bg-components-input-bg-hover',
'focus-visible:ring-2',
'focus-visible:ring-state-accent-solid',
'focus-visible:ring-inset',
)
expect(clearButton).not.toHaveClass('size-4')
expect(clearButton).not.toHaveClass('focus-visible:ring-1')
expect(clearButton).not.toHaveClass('focus-visible:ring-components-input-border-active')
})
})
describe('Interaction', () => {
@@ -84,6 +84,7 @@ export function SearchInput({
onValueChange(e.currentTarget.value)
}}
autoComplete="off"
// eslint-disable-next-line jsx-a11y/no-autofocus
autoFocus={autoFocus}
enterKeyHint="search"
/>
@@ -91,7 +92,7 @@ export function SearchInput({
<button
type="button"
aria-label={t('operation.clear', { ns: 'common' })}
className="group/clear absolute top-1/2 right-2 flex size-4 -translate-y-1/2 cursor-pointer touch-manipulation items-center justify-center rounded-md border-none bg-transparent p-0 outline-hidden focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:ring-inset"
className="group/clear absolute top-1/2 right-1.5 flex size-5 -translate-y-1/2 cursor-pointer touch-manipulation items-center justify-center rounded-md border-none bg-transparent p-0 outline-hidden focus-visible:bg-components-input-bg-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:ring-inset"
onClick={handleClear}
>
<span className="i-ri-close-circle-fill size-4 text-text-quaternary group-hover/clear:text-text-tertiary" aria-hidden="true" />
@@ -146,7 +146,7 @@ describe('List', () => {
it('should render the search input', () => {
render(<List />)
expect(screen.getByRole('textbox')).toBeInTheDocument()
expect(screen.getByRole('searchbox')).toBeInTheDocument()
})
it('should render tag filter', () => {
@@ -195,7 +195,7 @@ describe('List', () => {
it('should update search input value', () => {
render(<List />)
const input = screen.getByRole('textbox')
const input = screen.getByRole('searchbox')
fireEvent.change(input, { target: { value: 'test search' } })
expect(input).toHaveValue('test search')
@@ -259,7 +259,7 @@ describe('List', () => {
it('should clear search input when onClear is called', () => {
render(<List />)
const input = screen.getByRole('textbox')
const input = screen.getByRole('searchbox')
// First set a value
fireEvent.change(input, { target: { value: 'test search' } })
expect(input).toHaveValue('test search')
+4 -7
View File
@@ -7,7 +7,7 @@ import { useBoolean, useDebounceFn } from 'ahooks'
// Libraries
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import { SearchInput } from '@/app/components/base/search-input'
import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label'
import { useAppContext, useSelector as useAppContextSelector } from '@/context/app-context'
import { useExternalApiPanel } from '@/context/external-api-panel-context'
@@ -69,13 +69,10 @@ const List = () => {
/>
)}
<TagFilter type="knowledge" value={tagFilterValue} onChange={handleTagsChange} onOpenTagManagement={() => setShowTagManagementModal(true)} />
<Input
showLeftIcon
showClearIcon
wrapperClassName="w-[200px]"
<SearchInput
className="w-50"
value={keywords}
onChange={e => handleKeywordsChange(e.target.value)}
onClear={() => handleKeywordsChange('')}
onValueChange={handleKeywordsChange}
/>
{
isCurrentWorkspaceManager && (