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();
+ });
+});