Skip to content

Commit 5cb966c

Browse files
chore: Options null value warning (#303)
* chore: add latest prettier to devDependencies * feat: add null is a value warning * test: add null is a value warning test cases
1 parent d7fac6a commit 5cb966c

File tree

5 files changed

+128
-27
lines changed

5 files changed

+128
-27
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"gh-pages": "^3.1.0",
5757
"glob": "^7.1.6",
5858
"np": "^7.6.0",
59+
"prettier": "^2.7.1",
5960
"rc-form": "^2.4.0",
6061
"rc-trigger": "^5.0.4",
6162
"react": "^16.0.0",

src/Cascader.tsx

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import * as React from 'react';
1+
import type { BaseSelectProps, BaseSelectPropsWithoutPrivate, BaseSelectRef } from 'rc-select';
2+
import { BaseSelect } from 'rc-select';
3+
import type { DisplayValueType, Placement } from 'rc-select/lib/BaseSelect';
24
import useId from 'rc-select/lib/hooks/useId';
35
import { conductCheck } from 'rc-tree/lib/utils/conductUtil';
46
import useMergedState from 'rc-util/lib/hooks/useMergedState';
5-
import type { DisplayValueType, Placement } from 'rc-select/lib/BaseSelect';
6-
import type { BaseSelectRef, BaseSelectPropsWithoutPrivate, BaseSelectProps } from 'rc-select';
7-
import { BaseSelect } from 'rc-select';
8-
import OptionList from './OptionList';
7+
import * as React from 'react';
98
import CascaderContext from './context';
10-
import { fillFieldNames, toPathKey, toPathKeys, SHOW_PARENT, SHOW_CHILD } from './utils/commonUtil';
119
import useDisplayValues from './hooks/useDisplayValues';
12-
import useRefFunc from './hooks/useRefFunc';
1310
import useEntities from './hooks/useEntities';
14-
import { formatStrategyValues, toPathOptions } from './utils/treeUtil';
11+
import useMissingValues from './hooks/useMissingValues';
12+
import useRefFunc from './hooks/useRefFunc';
1513
import useSearchConfig from './hooks/useSearchConfig';
1614
import useSearchOptions from './hooks/useSearchOptions';
17-
import warning from 'rc-util/lib/warning';
18-
import useMissingValues from './hooks/useMissingValues';
15+
import OptionList from './OptionList';
16+
import { fillFieldNames, SHOW_CHILD, SHOW_PARENT, toPathKey, toPathKeys } from './utils/commonUtil';
17+
import { formatStrategyValues, toPathOptions } from './utils/treeUtil';
18+
import warningProps, { warningNullOptions } from './utils/warningPropsUtil';
1919

2020
export interface ShowSearchType<OptionType extends BaseOptionType = DefaultOptionType> {
2121
filter?: (inputValue: string, options: OptionType[], fieldNames: FieldNames) => boolean;
@@ -134,7 +134,7 @@ export type CascaderProps<OptionType extends BaseOptionType = DefaultOptionType>
134134
| SingleCascaderProps<OptionType>
135135
| MultipleCascaderProps<OptionType>;
136136

137-
type InternalCascaderProps<OptionType extends BaseOptionType = DefaultOptionType> = Omit<
137+
export type InternalCascaderProps<OptionType extends BaseOptionType = DefaultOptionType> = Omit<
138138
SingleCascaderProps<OptionType> | MultipleCascaderProps<OptionType>,
139139
'onChange'
140140
> & {
@@ -412,22 +412,6 @@ const Cascader = React.forwardRef<CascaderRef, InternalCascaderProps>((props, re
412412
};
413413

414414
// ============================ Open ============================
415-
if (process.env.NODE_ENV !== 'production') {
416-
warning(
417-
!onPopupVisibleChange,
418-
'`onPopupVisibleChange` is deprecated. Please use `onDropdownVisibleChange` instead.',
419-
);
420-
warning(popupVisible === undefined, '`popupVisible` is deprecated. Please use `open` instead.');
421-
warning(
422-
popupClassName === undefined,
423-
'`popupClassName` is deprecated. Please use `dropdownClassName` instead.',
424-
);
425-
warning(
426-
popupPlacement === undefined,
427-
'`popupPlacement` is deprecated. Please use `placement` instead.',
428-
);
429-
}
430-
431415
const mergedOpen = open !== undefined ? open : popupVisible;
432416

433417
const mergedDropdownClassName = dropdownClassName || popupClassName;
@@ -439,6 +423,12 @@ const Cascader = React.forwardRef<CascaderRef, InternalCascaderProps>((props, re
439423
onPopupVisibleChange?.(nextVisible);
440424
};
441425

426+
// ========================== Warning ===========================
427+
if (process.env.NODE_ENV !== 'production') {
428+
warningProps(props);
429+
warningNullOptions(mergedOptions, mergedFieldNames);
430+
}
431+
442432
// ========================== Context ===========================
443433
const cascaderContext = React.useMemo(
444434
() => ({

src/utils/warningPropsUtil.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import warning from 'rc-util/lib/warning';
2+
import type { DefaultOptionType, FieldNames, InternalCascaderProps } from '../Cascader';
3+
4+
function warningProps(props: InternalCascaderProps) {
5+
const { onPopupVisibleChange, popupVisible, popupClassName, popupPlacement } = props;
6+
7+
warning(
8+
!onPopupVisibleChange,
9+
'`onPopupVisibleChange` is deprecated. Please use `onDropdownVisibleChange` instead.',
10+
);
11+
warning(popupVisible === undefined, '`popupVisible` is deprecated. Please use `open` instead.');
12+
warning(
13+
popupClassName === undefined,
14+
'`popupClassName` is deprecated. Please use `dropdownClassName` instead.',
15+
);
16+
warning(
17+
popupPlacement === undefined,
18+
'`popupPlacement` is deprecated. Please use `placement` instead.',
19+
);
20+
}
21+
22+
// value in Cascader options should not be null
23+
export function warningNullOptions(options: DefaultOptionType[], fieldNames: FieldNames) {
24+
if (options) {
25+
const recursiveOptions = (optionsList: DefaultOptionType[]) => {
26+
for (let i = 0; i < optionsList.length; i++) {
27+
const option = optionsList[i];
28+
29+
if (option[fieldNames?.value] === null) {
30+
warning(false, '`value` in Cascader options should not be `null`.');
31+
return true;
32+
}
33+
34+
if (
35+
Array.isArray(option[fieldNames?.children]) &&
36+
recursiveOptions(option[fieldNames?.children])
37+
) {
38+
return true;
39+
}
40+
}
41+
};
42+
43+
recursiveOptions(options);
44+
}
45+
}
46+
47+
export default warningProps;

tests/fieldNames.spec.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,36 @@ describe('Cascader.FieldNames', () => {
135135

136136
expect(wrapper.find('.rc-cascader-menu-item').last().text()).toEqual('Not Found');
137137
});
138+
139+
it('`null` is a value in fieldNames options should throw a warning', () => {
140+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => null);
141+
mount(
142+
<Cascader
143+
fieldNames={fieldNames}
144+
options={[
145+
{
146+
customTitle: '四川',
147+
customValue: 'sc',
148+
customChildren: [
149+
{
150+
customTitle: '成都',
151+
customValue: 'cd',
152+
customChildren: [
153+
{
154+
customTitle: '天府新区',
155+
customValue: null,
156+
},
157+
],
158+
},
159+
],
160+
},
161+
]}
162+
/>,
163+
);
164+
165+
expect(errorSpy).toHaveBeenCalledWith(
166+
'Warning: `value` in Cascader options should not be `null`.',
167+
);
168+
errorSpy.mockReset();
169+
});
138170
});

tests/index.spec.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,4 +946,35 @@ describe('Cascader.Basic', () => {
946946
it('not crash when value type is not array', () => {
947947
mount(<Cascader value={'bamboo' as any} />);
948948
});
949+
950+
it('`null` is a value in Cascader options should throw a warning', () => {
951+
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => null);
952+
mount(
953+
<Cascader
954+
options={[
955+
{
956+
label: '四川',
957+
value: 'sc',
958+
children: [
959+
{
960+
label: '成都',
961+
value: 'cd',
962+
children: [
963+
{
964+
label: '天府新区',
965+
value: null,
966+
},
967+
],
968+
},
969+
],
970+
},
971+
]}
972+
/>,
973+
);
974+
975+
expect(errorSpy).toHaveBeenCalledWith(
976+
'Warning: `value` in Cascader options should not be `null`.',
977+
);
978+
errorSpy.mockReset();
979+
});
949980
});

0 commit comments

Comments
 (0)