Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion examples/dynamic-options.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Demo extends React.Component {
{
label: `${targetOption.label}动态加载1`,
value: 'dynamic1',
isLeaf: false,
},
{
label: `${targetOption.label}动态加载2`,
Expand All @@ -50,7 +51,7 @@ class Demo extends React.Component {
// eslint-disable-next-line react/no-access-state-in-setstate
options: [...this.state.options],
});
}, 1000);
}, 500);
};

render() {
Expand Down
4 changes: 2 additions & 2 deletions src/OptionList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const RefOptionList = React.forwardRef<RefOptionListProps, OptionListProps>((pro
const { options: optionList } = restoreCompatibleValue(entity as any, fieldNames);
const rawOptionList = optionList.map(opt => opt.node);

setLoadingKeys(keys => [...keys, optionList[optionList.length - 1].value]);
setLoadingKeys(keys => [...keys, entity.key]);

loadData(rawOptionList);
}
Expand All @@ -64,7 +64,7 @@ const RefOptionList = React.forwardRef<RefOptionListProps, OptionListProps>((pro
if (loadingKeys.length) {
loadingKeys.forEach(loadingKey => {
const option = flattenOptions.find(opt => opt.value === loadingKey);
if (option.data.children || option.data.isLeaf === true) {
if (!option || option.data.children || option.data.isLeaf === true) {
setLoadingKeys(keys => keys.filter(key => key !== loadingKey));
}
});
Expand Down
131 changes: 0 additions & 131 deletions tests/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable react/jsx-no-bind */

import React from 'react';
import { act } from 'react-dom/test-utils';
import { resetWarned } from 'rc-util/lib/warning';
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
import { mount } from './enzyme';
Expand Down Expand Up @@ -511,136 +510,6 @@ describe('Cascader.Basic', () => {
expect(wrapper.isOpen()).toBeFalsy();
});

describe('loadData', () => {
it('basic load', () => {
const loadData = jest.fn();
const wrapper = mount(
<Cascader
loadingIcon={<div className="loading-icon" />}
options={[
{
label: 'Bamboo',
value: 'bamboo',
isLeaf: false,
},
]}
loadData={loadData}
open
/>,
);

wrapper.find('.rc-cascader-menu-item-content').first().simulate('click');
expect(wrapper.exists('.loading-icon')).toBeTruthy();
expect(loadData).toHaveBeenCalledWith([
expect.objectContaining({
value: 'bamboo',
}),
]);

expect(wrapper.exists('.rc-cascader-menu-item-loading')).toBeTruthy();
expect(wrapper.exists('.rc-cascader-menu-item-loading-icon')).toBeTruthy();

// Fill data
wrapper.setProps({
options: [
{
label: 'Bamboo',
value: 'bamboo',
isLeaf: false,
children: [],
},
],
});
wrapper.update();
expect(wrapper.exists('.loading-icon')).toBeFalsy();
});

it('not load leaf', () => {
const loadData = jest.fn();
const onValueChange = jest.fn();
const wrapper = mount(
<Cascader
open
loadData={loadData}
onChange={onValueChange}
options={[
{
label: 'Light',
value: 'light',
},
]}
/>,
);

wrapper.clickOption(0, 0);
expect(onValueChange).toHaveBeenCalled();
expect(loadData).not.toHaveBeenCalled();
});

// https://github.com/ant-design/ant-design/issues/9084
it('should trigger loadData when expandTrigger is hover', () => {
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
isLeaf: false,
},
{
value: 'jiangsu',
label: 'Jiangsu',
isLeaf: false,
},
];
const loadData = jest.fn();
const wrapper = mount(
<Cascader options={options} loadData={loadData} changeOnSelect expandTrigger="hover">
<input readOnly />
</Cascader>,
);
wrapper.find('input').simulate('click');
const menus = wrapper.find('.rc-cascader-menu');
const menu1Items = menus.at(0).find('.rc-cascader-menu-item');
menu1Items.at(0).simulate('mouseEnter');
jest.runAllTimers();
expect(loadData).toHaveBeenCalled();
});

it('change isLeaf back to true should not loop loading', async () => {
const Demo = () => {
const [options, setOptions] = React.useState([
{ value: 'zhejiang', label: 'Zhejiang', isLeaf: false },
]);

const loadData = () => {
Promise.resolve().then(() => {
act(() => {
setOptions([
{
value: 'zhejiang',
label: 'Zhejiang',
isLeaf: true,
},
]);
});
});
};

return <Cascader options={options} loadData={loadData} open />;
};

const wrapper = mount(<Demo />);
wrapper.find('.rc-cascader-menu-item-content').first().simulate('click');
expect(wrapper.exists('.rc-cascader-menu-item-loading')).toBeTruthy();

for (let i = 0; i < 3; i += 1) {
await Promise.resolve();
}
wrapper.update();

expect(wrapper.exists('.rc-cascader-menu-item-loading')).toBeFalsy();
});
});

// https://github.com/ant-design/ant-design/issues/9793
it('should not trigger onBlur and onFocus when select item', () => {
// This function is handled by `rc-select` instead
Expand Down
185 changes: 185 additions & 0 deletions tests/loadData.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/* eslint-disable react/jsx-no-bind */

import React from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from './enzyme';
import Cascader from '../src';

describe('Cascader.LoadData', () => {
beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

it('basic load', () => {
const loadData = jest.fn();
const wrapper = mount(
<Cascader
loadingIcon={<div className="loading-icon" />}
options={[
{
label: 'Bamboo',
value: 'bamboo',
isLeaf: false,
},
]}
loadData={loadData}
open
/>,
);

wrapper.find('.rc-cascader-menu-item-content').first().simulate('click');
expect(wrapper.exists('.loading-icon')).toBeTruthy();
expect(loadData).toHaveBeenCalledWith([
expect.objectContaining({
value: 'bamboo',
}),
]);

expect(wrapper.exists('.rc-cascader-menu-item-loading')).toBeTruthy();
expect(wrapper.exists('.rc-cascader-menu-item-loading-icon')).toBeTruthy();

// Fill data
wrapper.setProps({
options: [
{
label: 'Bamboo',
value: 'bamboo',
isLeaf: false,
children: [],
},
],
});
wrapper.update();
expect(wrapper.exists('.loading-icon')).toBeFalsy();
});

it('not load leaf', () => {
const loadData = jest.fn();
const onValueChange = jest.fn();
const wrapper = mount(
<Cascader
open
loadData={loadData}
onChange={onValueChange}
options={[
{
label: 'Light',
value: 'light',
},
]}
/>,
);

wrapper.clickOption(0, 0);
expect(onValueChange).toHaveBeenCalled();
expect(loadData).not.toHaveBeenCalled();
});

// https://github.com/ant-design/ant-design/issues/9084
it('should trigger loadData when expandTrigger is hover', () => {
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
isLeaf: false,
},
{
value: 'jiangsu',
label: 'Jiangsu',
isLeaf: false,
},
];
const loadData = jest.fn();
const wrapper = mount(
<Cascader options={options} loadData={loadData} changeOnSelect expandTrigger="hover">
<input readOnly />
</Cascader>,
);
wrapper.find('input').simulate('click');
const menus = wrapper.find('.rc-cascader-menu');
const menu1Items = menus.at(0).find('.rc-cascader-menu-item');
menu1Items.at(0).simulate('mouseEnter');
jest.runAllTimers();
expect(loadData).toHaveBeenCalled();
});

it('change isLeaf back to true should not loop loading', async () => {
const Demo = () => {
const [options, setOptions] = React.useState([
{ value: 'zhejiang', label: 'Zhejiang', isLeaf: false },
]);

const loadData = () => {
Promise.resolve().then(() => {
act(() => {
setOptions([
{
value: 'zhejiang',
label: 'Zhejiang',
isLeaf: true,
},
]);
});
});
};

return <Cascader options={options} loadData={loadData} open />;
};

const wrapper = mount(<Demo />);
wrapper.find('.rc-cascader-menu-item-content').first().simulate('click');
expect(wrapper.exists('.rc-cascader-menu-item-loading')).toBeTruthy();

for (let i = 0; i < 3; i += 1) {
await Promise.resolve();
}
wrapper.update();

expect(wrapper.exists('.rc-cascader-menu-item-loading')).toBeFalsy();
});

it('nest load should not crash', async () => {
const Demo = () => {
const [options, setOptions] = React.useState([{ label: 'top', value: 'top', isLeaf: false }]);

const loadData = selectedOptions => {
Promise.resolve().then(() => {
act(() => {
selectedOptions[selectedOptions.length - 1].children = [
{
label: 'child',
value: 'child',
isLeaf: false,
},
];
setOptions(list => [...list]);
});
});
};

return <Cascader options={options} loadData={loadData} open />;
};

const wrapper = mount(<Demo />);

// First column click
wrapper.find('.rc-cascader-menu-item-content').last().simulate('click');
for (let i = 0; i < 3; i += 1) {
await Promise.resolve();
}
wrapper.update();

// Second column click
wrapper.find('.rc-cascader-menu-item-content').last().simulate('click');
for (let i = 0; i < 3; i += 1) {
await Promise.resolve();
}
wrapper.update();

expect(wrapper.find('ul.rc-cascader-menu')).toHaveLength(3);
});
});