Skip to content

Commit

Permalink
feat: add showCheckedStrategy (#255)
Browse files Browse the repository at this point in the history
* feat: add showCheckedStrategy

* feat: update variable
  • Loading branch information
heiyu4585 authored Mar 10, 2022
1 parent 0107340 commit 6a3efc9
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 10 deletions.
26 changes: 22 additions & 4 deletions src/Cascader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type { BaseSelectRef, BaseSelectPropsWithoutPrivate, BaseSelectProps } fr
import { BaseSelect } from 'rc-select';
import OptionList from './OptionList';
import CascaderContext from './context';
import { fillFieldNames, toPathKey, toPathKeys } from './utils/commonUtil';
import type { SHOW_CHILD } from './utils/commonUtil';
import { fillFieldNames, toPathKey, toPathKeys, SHOW_PARENT } from './utils/commonUtil';
import useDisplayValues from './hooks/useDisplayValues';
import useRefFunc from './hooks/useRefFunc';
import useEntities from './hooks/useEntities';
Expand Down Expand Up @@ -43,6 +44,7 @@ export interface InternalFieldNames extends Required<FieldNames> {
export type SingleValueType = (string | number)[];

export type ValueType = SingleValueType | SingleValueType[];
export type ShowCheckedStrategy = typeof SHOW_PARENT | typeof SHOW_CHILD;

export interface BaseOptionType {
disabled?: boolean;
Expand Down Expand Up @@ -71,6 +73,7 @@ interface BaseCascaderProps<OptionType extends BaseOptionType = DefaultOptionTyp
changeOnSelect?: boolean;
displayRender?: (label: string[], selectedOptions?: OptionType[]) => React.ReactNode;
checkable?: boolean | React.ReactNode;
showCheckedStrategy?: ShowCheckedStrategy;

// Search
showSearch?: boolean | ShowSearchType<OptionType>;
Expand Down Expand Up @@ -209,6 +212,7 @@ const Cascader = React.forwardRef<CascaderRef, InternalCascaderProps>((props, re
// Children
children,
dropdownMatchSelectWidth = false,
showCheckedStrategy = SHOW_PARENT,
...restProps
} = props;

Expand Down Expand Up @@ -296,10 +300,20 @@ const Cascader = React.forwardRef<CascaderRef, InternalCascaderProps>((props, re

const deDuplicatedValues = React.useMemo(() => {
const checkedKeys = toPathKeys(checkedValues);
const deduplicateKeys = formatStrategyValues(checkedKeys, getPathKeyEntities);
const deduplicateKeys = formatStrategyValues(
checkedKeys,
getPathKeyEntities,
showCheckedStrategy,
);

return [...missingCheckedValues, ...getValueByKeyPath(deduplicateKeys)];
}, [checkedValues, getPathKeyEntities, getValueByKeyPath, missingCheckedValues]);
}, [
checkedValues,
getPathKeyEntities,
getValueByKeyPath,
missingCheckedValues,
showCheckedStrategy,
]);

const displayValues = useDisplayValues(
deDuplicatedValues,
Expand Down Expand Up @@ -374,7 +388,11 @@ const Cascader = React.forwardRef<CascaderRef, InternalCascaderProps>((props, re
}

// Roll up to parent level keys
const deDuplicatedKeys = formatStrategyValues(checkedKeys, getPathKeyEntities);
const deDuplicatedKeys = formatStrategyValues(
checkedKeys,
getPathKeyEntities,
showCheckedStrategy,
);
nextCheckedValues = getValueByKeyPath(deDuplicatedKeys);
}

Expand Down
2 changes: 2 additions & 0 deletions src/utils/commonUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type {
} from '../Cascader';

export const VALUE_SPLIT = '__RC_CASCADER_SPLIT__';
export const SHOW_PARENT = 'SHOW_PARENT';
export const SHOW_CHILD = 'SHOW_CHILD';

export function toPathKey(value: SingleValueType) {
return value.join(VALUE_SPLIT);
Expand Down
22 changes: 16 additions & 6 deletions src/utils/treeUtil.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import type { SingleValueType, DefaultOptionType, InternalFieldNames } from '../Cascader';
import type {
SingleValueType,
DefaultOptionType,
InternalFieldNames,
ShowCheckedStrategy,
} from '../Cascader';
import type { GetEntities } from '../hooks/useEntities';
import { SHOW_CHILD } from './commonUtil';

export function formatStrategyValues(pathKeys: React.Key[], getKeyPathEntities: GetEntities) {
export function formatStrategyValues(
pathKeys: React.Key[],
getKeyPathEntities: GetEntities,
showCheckedStrategy: ShowCheckedStrategy,
) {
const valueSet = new Set(pathKeys);
const keyPathEntities = getKeyPathEntities();

return pathKeys.filter(key => {
const entity = keyPathEntities[key];
const parent = entity ? entity.parent : null;
const children = entity ? entity.children : null;

if (parent && !parent.node.disabled && valueSet.has(parent.key)) {
return false;
}
return true;
return showCheckedStrategy === SHOW_CHILD
? !(children && children.some(child => child.key && valueSet.has(child.key)))
: !(parent && !parent.node.disabled && valueSet.has(parent.key));
});
}

Expand Down
50 changes: 50 additions & 0 deletions tests/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,56 @@ describe('Cascader.Basic', () => {
expect(selectedValue.join(',')).toBe('fj,fuzhou,mawei');
});

it('should support showCheckedStrategy parent', () => {
const wrapper = mount(
<Cascader
checkable
changeOnSelect
options={addressOptions}
onChange={onChange}
showCheckedStrategy={'SHOW_PARENT'}
>
<input readOnly />
</Cascader>,
);
wrapper.find('input').simulate('click');
let menus = wrapper.find('.rc-cascader-menu');
expect(menus.length).toBe(1);
wrapper.clickOption(0, 2);
menus = wrapper.find('.rc-cascader-menu');
expect(menus.length).toBe(2);
wrapper.clickOption(1, 0);
wrapper.clickOption(1, 1);
expect(selectedValue.join(',')).toBe('bj');
});

it('should support showCheckedStrategy child', () => {
const wrapper = mount(
<Cascader
checkable
changeOnSelect
options={addressOptions}
onChange={onChange}
showCheckedStrategy={'SHOW_CHILD'}
>
<input readOnly />
</Cascader>,
);
wrapper.find('input').simulate('click');

// Menu 1
let menus = wrapper.find('.rc-cascader-menu');
expect(menus.length).toBe(1);
wrapper.clickOption(0, 2);
menus = wrapper.find('.rc-cascader-menu');
expect(menus.length).toBe(2);
wrapper.clickOption(1, 0);
wrapper.clickOption(1, 1);
expect(selectedValue[0].join(',')).toBe('bj,chaoyang');
expect(selectedValue[1].join(',')).toBe('bj,haidian');
expect(selectedValue.join(',')).toBe('bj,chaoyang,bj,haidian');
});

it('should has defaultValue', () => {
const wrapper = mount(
<Cascader
Expand Down

1 comment on commit 6a3efc9

@vercel
Copy link

@vercel vercel bot commented on 6a3efc9 Mar 10, 2022

Choose a reason for hiding this comment

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

Please sign in to comment.