diff --git a/README.md b/README.md index 1850ed53..5843eb15 100644 --- a/README.md +++ b/README.md @@ -183,3 +183,20 @@ The release flow is handled by `@rc-component/np` through the `rc-np` command af ## License @rc-component/tree-select is released under the [MIT](./LICENSE) license. + +### Clear configuration + +`allowClear` accepts a boolean or an `AllowClearConfig` object from `@rc-component/util`: + +```ts +interface AllowClearConfig { + clearIcon?: React.ReactNode; + disabled?: boolean; + tabIndex?: number; + label?: string; +} +``` + +All fields are optional. `disabled: true` behaves like `allowClear={false}`: the clear button and space reserved solely for it are removed; the input remains editable. Existing suffixes and other content are preserved. This changes the previous hidden-button spacing behavior where applicable. + +`tabIndex` affects only the clear button; `-1` removes it from Tab navigation while retaining mouse activation. `label` supplies its accessible name through `aria-label`. Defaults, clear values, callbacks, and component state restrictions are unchanged. diff --git a/package.json b/package.json index 3a6ed9b7..036e5d57 100644 --- a/package.json +++ b/package.json @@ -43,9 +43,9 @@ "*": "prettier --write --ignore-unknown" }, "dependencies": { - "@rc-component/select": "~1.11.0", + "@rc-component/select": "~1.12.0", "@rc-component/tree": "~1.5.3", - "@rc-component/util": "^1.11.1", + "@rc-component/util": "^1.14.0", "clsx": "^2.1.1" }, "devDependencies": { diff --git a/tests/allowClear.spec.tsx b/tests/allowClear.spec.tsx new file mode 100644 index 00000000..c8e85440 --- /dev/null +++ b/tests/allowClear.spec.tsx @@ -0,0 +1,38 @@ +import { fireEvent, render } from '@testing-library/react'; +import * as React from 'react'; +import TreeSelect from '../src'; + +describe.each([false, true])('allowClear treeCheckable=%s', treeCheckable => { + it('passes the complete configuration to Select and preserves clear callbacks', () => { + const onChange = jest.fn(); + const onClear = jest.fn(); + const props = { + treeCheckable, + defaultValue: treeCheckable ? ['one'] : 'one', + treeData: [{ value: 'one', title: 'One' }], + onChange, + onClear, + }; + const config = { clearIcon: 'custom', label: 'Erase', tabIndex: -1 }; + const { container, getByRole, queryByRole, rerender } = render( + , + ); + const markup = container.innerHTML; + rerender(); + expect(container.innerHTML).toBe(markup); + expect(queryByRole('button', { name: 'Erase' })).toBeNull(); + expect(onClear).not.toHaveBeenCalled(); + expect(onChange).not.toHaveBeenCalled(); + rerender(); + const button = getByRole('button', { name: 'Erase' }); + expect(button.tabIndex).toBe(-1); + expect(button).toHaveTextContent('custom'); + rerender(); + expect(getByRole('button', { name: 'Erase' }).tabIndex).toBe(0); + fireEvent.click(getByRole('button', { name: 'Erase' })); + expect(onClear).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange.mock.calls[0][0]).toEqual(treeCheckable ? [] : undefined); + expect(queryByRole('button', { name: 'Erase' })).toBeNull(); + }); +});