diff --git a/README.md b/README.md
index 564fdefd..734916f5 100644
--- a/README.md
+++ b/README.md
@@ -164,3 +164,20 @@ The release flow is handled by `@rc-component/np` through the `rc-np` command af
## License
@rc-component/cascader 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 5eeec21d..603b4ae3 100644
--- a/package.json
+++ b/package.json
@@ -44,9 +44,9 @@
"prepare": "husky"
},
"dependencies": {
- "@rc-component/select": "~1.11.0",
+ "@rc-component/select": "~1.12.0",
"@rc-component/tree": "~1.5.3",
- "@rc-component/util": "^1.13.0",
+ "@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..639ec23c
--- /dev/null
+++ b/tests/allowClear.spec.tsx
@@ -0,0 +1,37 @@
+import { fireEvent, render } from '@testing-library/react';
+import * as React from 'react';
+import type { AllowClearConfig } from '@rc-component/util';
+import Cascader from '../src';
+
+describe.each([false, true])('allowClear checkable=%s', checkable => {
+ it('passes the complete configuration to Select and preserves clear callbacks', () => {
+ const onChange = jest.fn();
+ const onClear = jest.fn();
+ const props = { options: [{ value: 'one', label: 'One' }], onChange, onClear };
+ const renderCascader = (allowClear: boolean | AllowClearConfig) =>
+ checkable ? (
+
+ ) : (
+
+ );
+ const config = { clearIcon: 'custom', label: 'Erase', tabIndex: -1 };
+ const { container, getByRole, queryByRole, rerender } = render(renderCascader(false));
+ const markup = container.innerHTML;
+ rerender(renderCascader({ ...config, disabled: true }));
+ expect(container.innerHTML).toBe(markup);
+ expect(queryByRole('button', { name: 'Erase' })).toBeNull();
+ expect(onClear).not.toHaveBeenCalled();
+ expect(onChange).not.toHaveBeenCalled();
+ rerender(renderCascader(config));
+ const button = getByRole('button', { name: 'Erase' });
+ expect(button.tabIndex).toBe(-1);
+ expect(button).toHaveTextContent('custom');
+ rerender(renderCascader({ ...config, tabIndex: 0 }));
+ 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(checkable ? [] : undefined);
+ expect(queryByRole('button', { name: 'Erase' })).toBeNull();
+ });
+});