Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

以value查找label时不排除禁用的节点Entity #236

Merged
merged 17 commits into from
Jun 8, 2020
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ build
lib/*
coverage
yarn.lock
package-lock.json
es/*
17 changes: 7 additions & 10 deletions src/hooks/useKeyValueMapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { FlattenDataNode, Key, RawValueType } from '../interface';

export type SkipType = null | 'select' | 'checkbox';

export function isDisabled(
dataNode: FlattenDataNode,
skipType: SkipType,
): boolean {
export function isDisabled(dataNode: FlattenDataNode, skipType: SkipType): boolean {
if (!dataNode) {
return true;
}
Expand All @@ -27,14 +24,14 @@ export default function useKeyValueMapping(
cacheKeyMap: Map<Key, FlattenDataNode>,
cacheValueMap: Map<RawValueType, FlattenDataNode>,
): [
(key: Key, skipType?: SkipType) => FlattenDataNode,
(value: RawValueType, skipType?: SkipType) => FlattenDataNode,
(key: Key, skipType?: SkipType, ignoreDisabledCheck?: boolean) => FlattenDataNode,
(value: RawValueType, skipType?: SkipType, ignoreDisabledCheck?: boolean) => FlattenDataNode,
] {
const getEntityByKey = React.useCallback(
(key: Key, skipType: SkipType = 'select') => {
(key: Key, skipType: SkipType = 'select', ignoreDisabledCheck?: boolean) => {
const dataNode = cacheKeyMap.get(key);

if (isDisabled(dataNode, skipType)) {
if (!ignoreDisabledCheck && isDisabled(dataNode, skipType)) {
return null;
}

Expand All @@ -44,10 +41,10 @@ export default function useKeyValueMapping(
);

const getEntityByValue = React.useCallback(
(value: RawValueType, skipType: SkipType = 'select') => {
(value: RawValueType, skipType: SkipType = 'select', ignoreDisabledCheck?: boolean) => {
const dataNode = cacheValueMap.get(value);

if (isDisabled(dataNode, skipType)) {
if (!ignoreDisabledCheck && isDisabled(dataNode, skipType)) {
return null;
}

Expand Down
8 changes: 6 additions & 2 deletions src/hooks/useSelectValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ interface Config {
value: DefaultValueType;
showCheckedStrategy: CheckedStrategy;
conductKeyEntities: Record<Key, DataEntity>;
getEntityByKey: (key: Key, skipType?: SkipType) => FlattenDataNode;
getEntityByValue: (value: RawValueType, skipType?: SkipType) => FlattenDataNode;
getEntityByKey: (key: Key, skipType?: SkipType, ignoreDisabledCheck?: boolean) => FlattenDataNode;
getEntityByValue: (
value: RawValueType,
skipType?: SkipType,
ignoreDisabledCheck?: boolean,
) => FlattenDataNode;
getLabelProp: (node: DataNode) => React.ReactNode;
}

Expand Down
16 changes: 3 additions & 13 deletions src/utils/legacyUtil.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,7 @@ export function fillAdditionalInfo(
const pos = `${level}-${index}`;
const included = checkedValues.includes(dataNode.value);
const children = dig(dataNode.children || [], pos, included);
const node = (
<TreeNode {...dataNode}>
{children.map(child => child.node)}
</TreeNode>
);
const node = <TreeNode {...dataNode}>{children.map(child => child.node)}</TreeNode>;

// Link with trigger node
if (triggerValue === dataNode.value) {
Expand Down Expand Up @@ -137,21 +133,15 @@ export function fillAdditionalInfo(

Object.defineProperty(extra, 'triggerNode', {
get() {
warning(
false,
'`triggerNode` is deprecated. Please consider decoupling data with node.',
);
warning(false, '`triggerNode` is deprecated. Please consider decoupling data with node.');
generateMap();

return triggerNode;
},
});
Object.defineProperty(extra, 'allCheckedNodes', {
get() {
warning(
false,
'`allCheckedNodes` is deprecated. Please consider decoupling data with node.',
);
warning(false, '`allCheckedNodes` is deprecated. Please consider decoupling data with node.');
generateMap();

if (showPosition) {
Expand Down
16 changes: 5 additions & 11 deletions src/utils/valueUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ export function toArray<T>(value: T | T[]): T[] {
return value !== undefined ? [value] : [];
}

export function findValueOption(
values: RawValueType[],
options: FlattenDataNode[],
): DataNode[] {
export function findValueOption(values: RawValueType[], options: FlattenDataNode[]): DataNode[] {
const optionMap: Map<RawValueType, DataNode> = new Map();

options.forEach(flattenItem => {
Expand All @@ -34,10 +31,7 @@ export function findValueOption(
return values.map(val => fillLegacyProps(optionMap.get(val)));
}

export function isValueDisabled(
value: RawValueType,
options: FlattenDataNode[],
): boolean {
export function isValueDisabled(value: RawValueType, options: FlattenDataNode[]): boolean {
const option = findValueOption([value], options)[0];
if (option) {
return option.disabled;
Expand Down Expand Up @@ -135,8 +129,7 @@ export function filterOptions(
.map(dataNode => {
const { children } = dataNode;

const match =
keepAll || filterOptionFunc(searchValue, fillLegacyProps(dataNode));
const match = keepAll || filterOptionFunc(searchValue, fillLegacyProps(dataNode));
const childList = dig(children || [], match);

if (match || childList.length) {
Expand All @@ -159,6 +152,7 @@ export function getRawValueLabeled(
getEntityByValue: (
value: RawValueType,
skipType?: SkipType,
ignoreDisabledCheck?: boolean,
) => FlattenDataNode,
getLabelProp: (node: DataNode) => React.ReactNode,
): LabelValueType[] {
Expand All @@ -172,7 +166,7 @@ export function getRawValueLabeled(

return values.map(val => {
const item: LabelValueType = { value: val };
const entity = getEntityByValue(val);
const entity = getEntityByValue(val, 'select', true);
const label = entity ? getLabelProp(entity.data) : val;

if (valueMap.has(val)) {
Expand Down
54 changes: 10 additions & 44 deletions tests/Select.props.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@
import React from 'react';
import { mount } from 'enzyme';
import { TreeNode } from 'rc-tree';
import TreeSelect, {
SHOW_ALL,
SHOW_CHILD,
SHOW_PARENT,
TreeNode as SelectNode,
} from '../src';
import TreeSelect, { SHOW_ALL, SHOW_CHILD, SHOW_PARENT, TreeNode as SelectNode } from '../src';

// Promisify timeout to let jest catch works
function timeoutPromise(delay = 0) {
Expand Down Expand Up @@ -47,9 +42,7 @@ describe('TreeSelect.props', () => {
function filterTreeNode(input, child) {
return String(child.props.title).indexOf(input) !== -1;
}
const wrapper = mount(
createOpenSelect({ filterTreeNode, showSearch: true }),
);
const wrapper = mount(createOpenSelect({ filterTreeNode, showSearch: true }));
wrapper.search('Title 1');
expect(wrapper.find('List').props().data).toHaveLength(1);

Expand All @@ -58,9 +51,7 @@ describe('TreeSelect.props', () => {
});

it('false', () => {
const wrapper = mount(
createOpenSelect({ filterTreeNode: false, showSearch: true }),
);
const wrapper = mount(createOpenSelect({ filterTreeNode: false, showSearch: true }));
wrapper.search('Title 1');
expect(wrapper.find('List').props().data).toHaveLength(4);
});
Expand Down Expand Up @@ -122,23 +113,15 @@ describe('TreeSelect.props', () => {
placeholder: 'RC Component',
}),
);
expect(wrapper.find('.rc-tree-select-selection-placeholder').text()).toBe(
'RC Component',
);
expect(wrapper.find('.rc-tree-select-selection-placeholder').text()).toBe('RC Component');
});

// https://github.com/ant-design/ant-design/issues/11746
it('async update treeData when has searchInput', () => {
const treeData = [{ title: 'aaa', value: '111' }];
const Wrapper = props => (
<div>
<TreeSelect
treeData={treeData}
searchValue="111"
showSearch
open
{...props}
/>
<TreeSelect treeData={treeData} searchValue="111" showSearch open {...props} />
</div>
);
const wrapper = mount(<Wrapper />);
Expand Down Expand Up @@ -345,16 +328,8 @@ describe('TreeSelect.props', () => {
// Since after click will render new TreeNode
// [Legacy] FIXME: This is so hard to test
wrapper.selectNode(0);
expect(handleChange).toHaveBeenCalledWith(
arg1,
arg2,
expect.anything(),
);
const {
triggerNode,
allCheckedNodes,
...rest
} = handleChange.mock.calls[0][2];
expect(handleChange).toHaveBeenCalledWith(arg1, arg2, expect.anything());
const { triggerNode, allCheckedNodes, ...rest } = handleChange.mock.calls[0][2];
expect({ ...rest, triggerNode, allCheckedNodes }).toEqual(arg3);
});
});
Expand Down Expand Up @@ -448,9 +423,7 @@ describe('TreeSelect.props', () => {
wrapper.find('.rc-tree-select-tree-switcher').simulate('click');

return timeoutPromise().then(() => {
expect(handleLoadData).toHaveBeenCalledWith(
expect.objectContaining({ value: '0-0' }),
);
expect(handleLoadData).toHaveBeenCalledWith(expect.objectContaining({ value: '0-0' }));
expect(called).toBe(1);
expect(wrapper.find('List').props().data).toHaveLength(2);
});
Expand All @@ -469,9 +442,7 @@ describe('TreeSelect.props', () => {
);

expect(loadData).toHaveBeenCalledTimes(1);
expect(loadData).toHaveBeenCalledWith(
expect.objectContaining({ value: '0-1' }),
);
expect(loadData).toHaveBeenCalledWith(expect.objectContaining({ value: '0-1' }));
});

it('getPopupContainer', () => {
Expand All @@ -489,12 +460,7 @@ describe('TreeSelect.props', () => {
const onChange = jest.fn();
const wrapper = mount(
<div>
<TreeSelect
value={['not exist']}
onChange={onChange}
treeCheckable
open
>
<TreeSelect value={['not exist']} onChange={onChange} treeCheckable open>
<SelectNode title="exist" value="exist" />
</TreeSelect>
</div>,
Expand Down
22 changes: 22 additions & 0 deletions tests/Select.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ describe('TreeSelect.basic', () => {
expect(wrapper.getSelection(0).text()).toEqual('label0');
});

it('sets default value(disabled)', () => {
const wrapper = mount(
<TreeSelect
defaultValue="0"
treeData={[{ key: '0', value: '0', title: 'label0', disabled: true }]}
/>,
);
expect(wrapper.getSelection(0).text()).toEqual('label0');
});

it('select value twice', () => {
const onChange = jest.fn();
const wrapper = mount(
Expand Down Expand Up @@ -411,6 +421,14 @@ describe('TreeSelect.basic', () => {
wrapper.update();
}

function keyUp(code) {
wrapper
.find('input')
.first()
.simulate('keyUp', { which: code });
wrapper.update();
}

function matchValue(value) {
expect(onChange).toHaveBeenCalledWith(value, expect.anything(), expect.anything());
onChange.mockReset();
Expand All @@ -419,11 +437,15 @@ describe('TreeSelect.basic', () => {
wrapper.openSelect();

keyDown(KeyCode.DOWN);
keyUp(KeyCode.DOWN);
keyDown(KeyCode.ENTER);
keyUp(KeyCode.ENTER);
matchValue(['parent']);

keyDown(KeyCode.UP);
keyUp(KeyCode.UP);
keyDown(KeyCode.ENTER);
keyUp(KeyCode.ENTER);
matchValue(['parent', 'child']);
});
});
Expand Down
16 changes: 4 additions & 12 deletions tests/Select.tree.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('TreeSelect.tree', () => {
<SelectNode key="0-0-0" value="0-0-0">
<SelectNode key="0-0-0-0" value="0-0-0-0" />
<SelectNode key="0-0-0-1" />
invalid element
</SelectNode>
<SelectNode key="0-0-1" value="0-0-1">
<SelectNode key="0-0-1-0" value="0-0-1-0" />
Expand Down Expand Up @@ -70,11 +71,7 @@ describe('TreeSelect.tree', () => {
it('warning if node key are not same as value', () => {
resetWarned();
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
mount(
<TreeSelect
treeData={[{ title: 'little', value: 'ttt', key: 'little' }]}
/>,
);
mount(<TreeSelect treeData={[{ title: 'little', value: 'ttt', key: 'little' }]} />);
expect(spy).toHaveBeenCalledWith(
'Warning: `key` or `value` with TreeNode must be the same or you can remove one of them. key: little, value: ttt.',
);
Expand All @@ -86,15 +83,10 @@ describe('TreeSelect.tree', () => {
const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
mount(
<TreeSelect
treeData={[
{ title: 'little', value: 'ttt' },
{ title: 'bamboo', value: 'ttt' },
]}
treeData={[{ title: 'little', value: 'ttt' }, { title: 'bamboo', value: 'ttt' }]}
/>,
);
expect(spy).toHaveBeenCalledWith(
'Warning: Same `value` exist in the tree: ttt',
);
expect(spy).toHaveBeenCalledWith('Warning: Same `value` exist in the tree: ttt');
spy.mockRestore();
});

Expand Down
13 changes: 3 additions & 10 deletions tests/utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,17 @@ import { isDisabled } from '../src/hooks/useKeyValueMapping';

describe('TreeSelect.util', () => {
it('isValueDisabled', () => {
const options = [
{ data: { value: 'disabled', disabled: true } },
{ data: { value: 'pass' } },
];
const options = [{ data: { value: 'disabled', disabled: true } }, { data: { value: 'pass' } }];
expect(isValueDisabled('disabled', options)).toBeTruthy();
expect(isValueDisabled('pass', options)).toBeFalsy();
expect(isValueDisabled('not-exist', options)).toBeFalsy();
});

it('isDisabled', () => {
expect(isDisabled({ data: { disabled: true } }, 'select')).toBeTruthy();
expect(
isDisabled({ data: { disableCheckbox: true } }, 'select'),
).toBeFalsy();
expect(isDisabled({ data: { disableCheckbox: true } }, 'select')).toBeFalsy();
expect(isDisabled({ data: { disabled: true } }, 'checkbox')).toBeTruthy();
expect(
isDisabled({ data: { disableCheckbox: true } }, 'checkbox'),
).toBeTruthy();
expect(isDisabled({ data: { disableCheckbox: true } }, 'checkbox')).toBeTruthy();
expect(isDisabled({ data: { disabled: true } }, null)).toBeFalsy();
});
});