Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/examples/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class Combobox extends React.Component {
console.log('Ref:', this.textareaRef);
}

onActive = (value) => {
console.log('onActive', value);
};

onChange = (value, option) => {
console.log('onChange', value, option);
this.setState({
Expand Down Expand Up @@ -83,6 +87,7 @@ class Combobox extends React.Component {
value={value}
mode="combobox"
onChange={this.onChange}
onActive={this.onActive}
filterOption={(inputValue, option) => {
if (!inputValue) {
return true;
Expand Down
5 changes: 5 additions & 0 deletions docs/examples/controlled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ class Controlled extends React.Component<{}, ControlledState> {
this.setState({ open });
};

onActive = (value) => {
console.error('onActive', value);
};

render() {
const { open, destroy, value } = this.state;
if (destroy) {
Expand All @@ -69,6 +73,7 @@ class Controlled extends React.Component<{}, ControlledState> {
optionFilterProp="text"
onChange={this.onChange}
onPopupVisibleChange={this.onPopupVisibleChange}
onActive={this.onActive}
>
<Option value="01" text="jack" title="jack">
<b
Expand Down
26 changes: 14 additions & 12 deletions src/OptionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,22 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
* `setActive` function will call root accessibility state update which makes re-render.
* So we need to delay to let Input component trigger onChange first.
*/
const timeoutId = setTimeout(() => {
if (!multiple && open && rawValues.size === 1) {
const value: RawValueType = Array.from(rawValues)[0];
// Scroll to the option closest to the searchValue if searching.
const index = memoFlattenOptions.findIndex(({ data }) =>
searchValue ? String(data.value).startsWith(searchValue) : data.value === value,
);

if (index !== -1) {
setActive(index);
let timeoutId: NodeJS.Timeout;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不应该是用 ReturnType<typeof setTimeout> 好一点么


if (!multiple && open && rawValues.size === 1) {
const value: RawValueType = Array.from(rawValues)[0];
// Scroll to the option closest to the searchValue if searching.
const index = memoFlattenOptions.findIndex(({ data }) =>
searchValue ? String(data.value).startsWith(searchValue) : data.value === value,
);

if (index !== -1) {
setActive(index);
timeoutId = setTimeout(() => {
scrollIntoView(index);
}
});
}
});
}

// Force trigger scrollbar visible when open
if (open) {
Expand Down
15 changes: 14 additions & 1 deletion src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export interface SelectProps<ValueType = any, OptionType extends BaseOptionType
// >>> Select
onSelect?: SelectHandler<ArrayElementType<ValueType>, OptionType>;
onDeselect?: SelectHandler<ArrayElementType<ValueType>, OptionType>;
onActive?: (value: ValueType) => void;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

info 是不是也可以一起丢出去?鼠标和键盘


// >>> Options
/**
Expand Down Expand Up @@ -185,6 +186,7 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
// Select
onSelect,
onDeselect,
onActive,
popupMatchSelectWidth = true,

// Options
Expand Down Expand Up @@ -493,15 +495,26 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
const mergedDefaultActiveFirstOption =
defaultActiveFirstOption !== undefined ? defaultActiveFirstOption : mode !== 'combobox';

const activeEventRef = React.useRef<Promise<void>>();

const onActiveValue: OnActiveValue = React.useCallback(
(active, index, { source = 'keyboard' } = {}) => {
setAccessibilityIndex(index);

if (backfill && mode === 'combobox' && active !== null && source === 'keyboard') {
setActiveValue(String(active));
}

// Active will call multiple times.
// We only need trigger the last one.
const promise = Promise.resolve().then(() => {
if (activeEventRef.current === promise) {
onActive?.(active);
}
});
activeEventRef.current = promise;
},
[backfill, mode],
[backfill, mode, onActive],
);

// ========================= OptionList =========================
Expand Down
8 changes: 8 additions & 0 deletions tests/Accessibility.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ describe('Select.Accessibility', () => {

// https://github.com/ant-design/ant-design/issues/31850
it('active index should keep', () => {
const onActive = jest.fn();

const { container } = render(
<Select
showSearch
Expand All @@ -40,6 +42,7 @@ describe('Select.Accessibility', () => {
value: 'light',
},
]}
onActive={onActive}
/>,
);

Expand All @@ -52,13 +55,18 @@ describe('Select.Accessibility', () => {
document.querySelector('.rc-select-item-option-active .rc-select-item-option-content')
.textContent,
).toEqual('Bamboo');
expect(onActive).toHaveBeenCalledWith('bamboo');

keyDown(container.querySelector('input')!, KeyCode.ENTER);
expectOpen(container, false);

// Next Match
fireEvent.change(container.querySelector('input')!, { target: { value: '' } });
expect(onActive).toHaveBeenCalledWith('bamboo');
fireEvent.change(container.querySelector('input')!, { target: { value: 'not exist' } });
expect(onActive).toHaveBeenCalledWith(null);
fireEvent.change(container.querySelector('input')!, { target: { value: 'g' } });
expect(onActive).toHaveBeenCalledWith('light');
jest.runAllTimers();

expectOpen(container);
Expand Down
Loading