From b0b982cf0efa1d32c43f97ceaca650d34ac811c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Tue, 2 Apr 2024 18:50:46 +0800 Subject: [PATCH 01/54] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 5 +- examples/rc-form.tsx | 99 +++++++++++++++----------------------- package.json | 3 +- tests/keyboard.spec.tsx | 1 + 4 files changed, 46 insertions(+), 62 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 92b349a6..df8cfd68 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -105,8 +105,9 @@ jobs: with: path: node_modules key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }} - + - name: tsc + run: npm lint:tsc - name: coverage run: npm test -- --coverage && bash <(curl -s https://codecov.io/bash) - needs: setup \ No newline at end of file + needs: setup diff --git a/examples/rc-form.tsx b/examples/rc-form.tsx index fc995413..bc78e340 100644 --- a/examples/rc-form.tsx +++ b/examples/rc-form.tsx @@ -1,9 +1,8 @@ -/* eslint-disable no-console, react/no-multi-comp, react/prop-types, react/button-has-type,prefer-destructuring,max-len, max-classes-per-file */ import arrayTreeFilter from 'array-tree-filter'; -import { createForm } from 'rc-form'; -import { Component } from 'react'; +import Form, { Field } from 'rc-field-form'; import '../assets/index.less'; import Cascader from '../src'; +import React from 'react'; const addressOptions = [ { @@ -58,71 +57,53 @@ const addressOptions = [ }, ]; -class CascaderInput extends Component { - onChange = value => { - const props = this.props; +const CascaderInput = (props: any) => { + const onChange = value => { if (props.onChange) { props.onChange(value); } }; - getLabel() { - const props = this.props; + const getLabel = () => { const value = props.value || []; - return arrayTreeFilter(props.options, (o, level) => o.value === value[level]) + return arrayTreeFilter(props.options, (o: any, level) => o.value === value[level]) .map(o => o.label) .join(', '); - } - - render() { - const props = this.props; - return ( - - - - ); - } -} - -class Form extends Component { - onSubmit = e => { - const props = this.props; - const { form } = props; - e.preventDefault(); - form.validateFields((error, values) => { - if (!error) { - console.log('ok', values); - } else { - console.error('error', error, values); - } - }); }; - render() { - const props = this.props; - const { form } = props; - const addressFieldError = form.getFieldError('address'); - return ( -
-
-

- {form.getFieldDecorator('address', { - initialValue: [], - rules: [{ required: true, type: 'array' }], - })()} - - {addressFieldError ? addressFieldError.join(' ') : null} - -

-

- -

-
-
- ); - } -} + return ( + + + + ); +}; -const NewForm = createForm()(Form); +const Demo = () => { + return ( +
+
{ + console.error('values', values); + }} + initialValues={{ address: [] }} + > +

+ + + + + {(_, __, { getFieldError }) => { + const hasErrors = getFieldError('address'); + return

{hasErrors ? hasErrors.join(' ') : null}
; + }} + +

+

+ +

+
+
+ ); +}; -export default NewForm; +export default Demo; diff --git a/package.json b/package.json index ea680d35..30bd12fa 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "lint": "eslint src/ examples/ --ext .tsx,.ts,.jsx,.jsx", "now-build": "npm run build", "prepublishOnly": "npm run compile && np --no-cleanup --yolo --no-publish", + "lint:tsc": "tsc -p tsconfig.json --noEmit", "start": "dumi dev", "test": "rc-test" }, @@ -73,7 +74,7 @@ "less": "^4.2.0", "np": "^9.2.0", "prettier": "^3.1.0", - "rc-form": "^2.4.0", + "rc-field-form": "^1.44.0", "rc-test": "^7.0.14", "react": "^16.0.0", "react-dom": "^16.0.0", diff --git a/tests/keyboard.spec.tsx b/tests/keyboard.spec.tsx index ca066129..5bc83a59 100644 --- a/tests/keyboard.spec.tsx +++ b/tests/keyboard.spec.tsx @@ -2,6 +2,7 @@ import { mount } from 'enzyme'; import KeyCode from 'rc-util/lib/KeyCode'; import Cascader from '../src'; import { addressOptions } from './demoOptions'; +import React from 'react'; describe('Cascader.Keyboard', () => { let wrapper; From c5d5c132b512fdb1d91e5dd4d1896c5029d83c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Tue, 2 Apr 2024 19:09:25 +0800 Subject: [PATCH 02/54] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/adjust-overflow.tsx | 41 +++------ examples/animation.tsx | 28 +++--- examples/custom-arrow-icon.tsx | 106 ++++++++++------------ examples/custom-field-name.tsx | 37 +++----- examples/default-expand-single-option.tsx | 40 ++++---- examples/defaultValue.tsx | 30 +++--- examples/disabled.tsx | 27 ++---- examples/dropdown-render.tsx | 48 +++++----- examples/dynamic-options.tsx | 49 ++++------ examples/hover.tsx | 38 ++++---- examples/search.tsx | 18 ++-- examples/simple.tsx | 28 +++--- examples/text-trigger.tsx | 34 +++---- examples/value.tsx | 47 ++++------ examples/visible.tsx | 51 +++++------ tests/index.spec.tsx | 50 +++++----- 16 files changed, 284 insertions(+), 388 deletions(-) diff --git a/examples/adjust-overflow.tsx b/examples/adjust-overflow.tsx index 8108611d..2312d9ee 100644 --- a/examples/adjust-overflow.tsx +++ b/examples/adjust-overflow.tsx @@ -1,6 +1,6 @@ /* eslint-disable no-console, react/prop-types */ import type { BuildInPlacements } from 'rc-trigger/lib/interface'; -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -58,35 +58,24 @@ const addressOptions = [ }, ]; -class MyCascader extends React.Component<{ builtinPlacements?: BuildInPlacements }> { - state = { - inputValue: '', - }; +const MyCascader = ({ builtinPlacements }: { builtinPlacements?: BuildInPlacements }) => { + const [inputValue, setInputValue] = useState(''); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - render() { - const { builtinPlacements } = this.props; - return ( - - - - ); - } -} + return ( + + + + ); +}; const placements = { bottomLeft: { diff --git a/examples/animation.tsx b/examples/animation.tsx index ff512aa5..4562add4 100644 --- a/examples/animation.tsx +++ b/examples/animation.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -56,25 +56,19 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: '', - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(''); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - render() { - return ( - - - - ); - } -} + return ( + + + + ); +}; export default Demo; diff --git a/examples/custom-arrow-icon.tsx b/examples/custom-arrow-icon.tsx index ca6be19e..d693e4e0 100644 --- a/examples/custom-arrow-icon.tsx +++ b/examples/custom-arrow-icon.tsx @@ -1,5 +1,4 @@ -/* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -81,39 +80,33 @@ const loadingPath = ' 7.9 4.1 11.4 1.3C854.5 760.8 912 649.1 912 523.9c0-' + '221.1-179.4-400.2-400.6-399.9z'; -class Demo extends React.Component { - state = { - inputValue: '', - dynamicInputValue: '', - options: [ - { - label: '福建', - isLeaf: false, - value: 'fj', - }, - { - label: '浙江', - isLeaf: false, - value: 'zj', - }, - ], - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(''); + const [dynamicInputValue, setDynamicInputValue] = useState(''); + const [options, setOptions] = useState([ + { + label: '福建', + isLeaf: false, + value: 'fj', + }, + { + label: '浙江', + isLeaf: false, + value: 'zj', + }, + ]); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - onChangeDynamic = (value, selectedOptions) => { + const onChangeDynamic = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - dynamicInputValue: selectedOptions.map(o => o.label).join(', '), - }); + setDynamicInputValue(selectedOptions.map(o => o.label).join(', ')); }; - expandIcon = ( + const expandIcon = ( ); - loadingIcon = ( + const loadingIcon = ( ); - loadData = selectedOptions => { + const loadData = selectedOptions => { const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.loading = true; // 动态加载下级数据 @@ -169,37 +162,32 @@ class Demo extends React.Component { value: 'dynamic2', }, ]; - this.setState({ - // eslint-disable-next-line react/no-access-state-in-setstate - options: [...this.state.options], - }); + setOptions([...options]); }, 1500); }; - render() { - return ( -
- - - - - - -
- ); - } -} + return ( +
+ + + + + + +
+ ); +}; export default Demo; diff --git a/examples/custom-field-name.tsx b/examples/custom-field-name.tsx index 5f587a53..dd4a24e2 100644 --- a/examples/custom-field-name.tsx +++ b/examples/custom-field-name.tsx @@ -1,5 +1,4 @@ -/* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -57,29 +56,23 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: '', - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(''); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.name).join(', '), - }); + setInputValue(selectedOptions.map(o => o.name).join(', ')); }; - render() { - return ( - - - - ); - } -} + return ( + + + + ); +}; export default Demo; diff --git a/examples/default-expand-single-option.tsx b/examples/default-expand-single-option.tsx index ff1d0c78..a7d31034 100644 --- a/examples/default-expand-single-option.tsx +++ b/examples/default-expand-single-option.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +/* eslint-disable @typescript-eslint/no-shadow */ +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -37,35 +38,28 @@ const options = [ }, ]; -class App extends React.Component { - state = { - inputValue: '', - value: [], - }; +const App = () => { + const [inputValue, setInputValue] = useState(''); + + const [value, setValue] = useState([]); - onChange = (value, selectedOptions) => { + const onChange = (value: any, selectedOptions) => { const lastSelected = selectedOptions[selectedOptions.length - 1]; if (lastSelected.children && lastSelected.children.length === 1) { value.push(lastSelected.children[0].value); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - value, - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); + setValue(value); return; } - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - value, - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); + setValue(value); }; - render() { - return ( - - - - ); - } -} + return ( + + + + ); +}; export default App; diff --git a/examples/defaultValue.tsx b/examples/defaultValue.tsx index 6295e091..2e30fba2 100644 --- a/examples/defaultValue.tsx +++ b/examples/defaultValue.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -91,26 +91,20 @@ const defaultOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: defaultOptions.map(o => o.label).join(', '), - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(defaultOptions.map(o => o.label).join(', ')); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - render() { - const defaultValue = defaultOptions.map(o => o.value); - return ( - - - - ); - } -} + const defaultValue = defaultOptions.map(o => o.value); + return ( + + + + ); +}; export default Demo; diff --git a/examples/disabled.tsx b/examples/disabled.tsx index 8cbff45f..f8d0d84d 100644 --- a/examples/disabled.tsx +++ b/examples/disabled.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -56,23 +56,14 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: '', - }; +const Demo = () => { + const [inputValue] = useState(''); - render() { - return ( - - - - ); - } -} + return ( + + + + ); +}; export default Demo; diff --git a/examples/dropdown-render.tsx b/examples/dropdown-render.tsx index e16dc66a..bb875fac 100644 --- a/examples/dropdown-render.tsx +++ b/examples/dropdown-render.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -57,35 +57,29 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: '', - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(''); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - render() { - return ( - ( -
- {menus} -
- Hey, DropdownRender, Long DropdownRender, Long DropdownRender -
- )} - > - -
- ); - } -} + return ( + ( +
+ {menus} +
+ Hey, DropdownRender, Long DropdownRender, Long DropdownRender +
+ )} + > + +
+ ); +}; export default Demo; diff --git a/examples/dynamic-options.tsx b/examples/dynamic-options.tsx index 9cc98769..9a79aa05 100644 --- a/examples/dynamic-options.tsx +++ b/examples/dynamic-options.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -16,20 +16,16 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: '', - options: addressOptions, - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(''); + const [options, setOptions] = useState(addressOptions); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log('OnChange:', value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - loadData = selectedOptions => { + const loadData = selectedOptions => { console.log('onLoad:', selectedOptions); const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.loading = true; @@ -47,26 +43,21 @@ class Demo extends React.Component { value: 'dynamic2', }, ]; - this.setState({ - // eslint-disable-next-line react/no-access-state-in-setstate - options: [...this.state.options], - }); + setOptions([...options]); }, 500); }; - render() { - return ( - - - - ); - } -} + return ( + + + + ); +}; export default Demo; diff --git a/examples/hover.tsx b/examples/hover.tsx index 0b667ae9..004d767d 100644 --- a/examples/hover.tsx +++ b/examples/hover.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -71,8 +71,8 @@ const addressOptions = [ { label: '高雄', value: 'gaoxiong', - } - ] + }, + ], }, { label: '香港', @@ -80,28 +80,22 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: '', - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(''); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - render() { - return ( -
-

Hover to expand children

- - - -
- ); - } -} + return ( +
+

Hover to expand children

+ + + +
+ ); +}; export default Demo; diff --git a/examples/search.tsx b/examples/search.tsx index 79f1baf8..af73f022 100644 --- a/examples/search.tsx +++ b/examples/search.tsx @@ -66,12 +66,16 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - render() { - return ( - - ); - } -} +const Demo = () => { + return ( + + ); +}; export default Demo; diff --git a/examples/simple.tsx b/examples/simple.tsx index 98e09bb1..427917e2 100644 --- a/examples/simple.tsx +++ b/examples/simple.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -57,25 +57,19 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: '', - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(''); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - render() { - return ( - - - - ); - } -} + return ( + + + + ); +}; export default Demo; diff --git a/examples/text-trigger.tsx b/examples/text-trigger.tsx index 6420b7fe..8a9ce9dd 100644 --- a/examples/text-trigger.tsx +++ b/examples/text-trigger.tsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -56,28 +56,22 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - inputValue: '未选择', - }; +const Demo = () => { + const [inputValue, setInputValue] = useState(''); - onChange = (value, selectedOptions) => { + const onChange = (value, selectedOptions) => { console.log(value, selectedOptions); - this.setState({ - inputValue: selectedOptions.map(o => o.label).join(', '), - }); + setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - render() { - return ( - - {this.state.inputValue} - - 切换地区 - - - ); - } -} + return ( + + {inputValue} + + 切换地区 + + + ); +}; export default Demo; diff --git a/examples/value.tsx b/examples/value.tsx index 2025ce4c..ec4c3e6c 100644 --- a/examples/value.tsx +++ b/examples/value.tsx @@ -1,6 +1,6 @@ -/* eslint-disable no-console,react/button-has-type */ +/* eslint-disable @typescript-eslint/no-shadow */ import arrayTreeFilter from 'array-tree-filter'; -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -57,38 +57,31 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - value: [], - }; - - onChange = value => { +const Demo = () => { + const [value, setValue] = useState([]); + const onChange = value => { console.log(value); - this.setState({ value }); + setValue(value); }; - setValue = () => { - this.setState({ - value: ['bj', 'chaoyang'], - }); + const handleSetValue = () => { + setValue(['bj', 'chaoyang']); }; - getLabel() { - return arrayTreeFilter(addressOptions, (o, level) => o.value === this.state.value[level]) + const getLabel = () => { + return arrayTreeFilter(addressOptions, (o, level) => o.value === value[level]) .map(o => o.label) .join(', '); - } + }; - render() { - return ( -
- - - - -
- ); - } -} + return ( +
+ + + + +
+ ); +}; export default Demo; diff --git a/examples/visible.tsx b/examples/visible.tsx index 763ce8eb..8dcdbb58 100644 --- a/examples/visible.tsx +++ b/examples/visible.tsx @@ -1,6 +1,7 @@ +/* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable no-console */ import arrayTreeFilter from 'array-tree-filter'; -import React from 'react'; +import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -57,39 +58,35 @@ const addressOptions = [ }, ]; -class Demo extends React.Component { - state = { - value: [], - popupVisible: false, - }; +const Demo = () => { + const [value, setValue] = useState([]); + const [popupVisible, setPopupVisible] = useState(false); - onChange = value => { - this.setState({ value }); + const onChange = value => { + setValue(value); }; - onPopupVisibleChange = popupVisible => { - this.setState({ popupVisible }); + const onPopupVisibleChange = popupVisible => { + setPopupVisible(popupVisible); }; - getLabel() { - return arrayTreeFilter(addressOptions, (o, level) => o.value === this.state.value[level]) + const getLabel = () => { + return arrayTreeFilter(addressOptions, (o, level) => o.value === value[level]) .map(o => o.label) .join(', '); - } + }; - render() { - return ( - - - - ); - } -} + return ( + + + + ); +}; export default Demo; diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 219fa8fe..f0d46ca3 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -2,7 +2,7 @@ import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; import { resetWarned } from 'rc-util/lib/warning'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import Cascader from '../src'; import { addressOptions, addressOptionsForUneven, optionsForActiveMenuItems } from './demoOptions'; import { mount } from './enzyme'; @@ -438,33 +438,25 @@ describe('Cascader.Basic', () => { // https://github.com/ant-design/ant-design/issues/5666 it('should have not change active value when value is not changed', () => { - class Demo extends React.Component { - state = { - value: [], - }; - - timeout = null; + const Demo = () => { + const [value, setValue] = useState([]); - componentDidMount() { - this.timeout = setTimeout(() => { - this.setState({ - value: [], - }); + // const timeout = null; + useEffect(() => { + const timeout = setTimeout(() => { + setValue([]); }, 10); - } - - componentWillUnmount() { - clearTimeout(this.timeout); - } - - render() { - return ( - - - - ); - } - } + return () => { + clearTimeout(timeout); + }; + }, []); + + return ( + + + + ); + }; const wrapper = mount(); wrapper.find('input').simulate('click'); let menus = wrapper.find('.rc-cascader-menu'); @@ -654,7 +646,8 @@ describe('Cascader.Basic', () => { changeOnSelect expandTrigger="hover" options={addressOptionsForUneven} - onChange={onChange}> + onChange={onChange} + >
, ); @@ -802,9 +795,8 @@ describe('Cascader.Basic', () => { expect(activeItems).toHaveLength(2); expect(activeItems.last().text()).toEqual('高雄'); }); - }) + }); }); - }); it('defaultValue not exist', () => { From b3a30f88256bae2c7ef9ba6f74804c323ae7ca0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Wed, 3 Apr 2024 09:12:43 +0800 Subject: [PATCH 03/54] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/adjust-overflow.tsx | 1 - examples/animation.tsx | 1 - examples/change-on-select.tsx | 88 +++++++++++------------ examples/debug.tsx | 1 - examples/default-expand-single-option.tsx | 2 +- examples/defaultValue.tsx | 1 - examples/disabled.tsx | 1 - examples/dropdown-render.tsx | 1 - examples/dynamic-options.tsx | 1 - examples/hover.tsx | 1 - examples/multiple.tsx | 3 +- examples/panel.tsx | 1 - examples/search.tsx | 1 - examples/simple.tsx | 1 - examples/text-trigger.tsx | 1 - examples/visible.tsx | 1 - src/OptionList/index.tsx | 1 - tests/Panel.spec.tsx | 2 - tests/checkable.spec.tsx | 2 - tests/fieldNames.spec.tsx | 2 - tests/index.spec.tsx | 2 - tests/loadData.spec.tsx | 2 - tests/private.spec.tsx | 2 - tests/search.spec.tsx | 5 +- 24 files changed, 48 insertions(+), 76 deletions(-) diff --git a/examples/adjust-overflow.tsx b/examples/adjust-overflow.tsx index 2312d9ee..637b91db 100644 --- a/examples/adjust-overflow.tsx +++ b/examples/adjust-overflow.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console, react/prop-types */ import type { BuildInPlacements } from 'rc-trigger/lib/interface'; import React, { useState } from 'react'; import '../assets/index.less'; diff --git a/examples/animation.tsx b/examples/animation.tsx index 4562add4..4909d666 100644 --- a/examples/animation.tsx +++ b/examples/animation.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/change-on-select.tsx b/examples/change-on-select.tsx index 43a7f556..e0825919 100644 --- a/examples/change-on-select.tsx +++ b/examples/change-on-select.tsx @@ -1,79 +1,79 @@ -/* eslint-disable no-console */ import React from 'react'; import '../assets/index.less'; import Cascader from '../src'; const options = [ { - label: "Women Clothing", - value: "Women Clothing", + label: 'Women Clothing', + value: 'Women Clothing', children: [ { - label: "Women Tops, Blouses & Tee", - value: "Women Tops, Blouses & Tee", + label: 'Women Tops, Blouses & Tee', + value: 'Women Tops, Blouses & Tee', children: [ { - label: "Women T-Shirts", - value: "Women T-Shirts" + label: 'Women T-Shirts', + value: 'Women T-Shirts', }, { - label: "Women Tops", - value: "Women Tops" + label: 'Women Tops', + value: 'Women Tops', }, { - label: "Women Tank Tops & Camis", - value: "Women Tank Tops & Camis" + label: 'Women Tank Tops & Camis', + value: 'Women Tank Tops & Camis', }, { - label: "Women Blouses", - value: "Women Blouses" - } - ] + label: 'Women Blouses', + value: 'Women Blouses', + }, + ], }, { - label: "Women Suits", - value: "Women Suits", + label: 'Women Suits', + value: 'Women Suits', children: [ { - label: "Women Suit Pants", - value: "Women Suit Pants" + label: 'Women Suit Pants', + value: 'Women Suit Pants', }, { - label: "Women Suit Sets", - value: "Women Suit Sets" + label: 'Women Suit Sets', + value: 'Women Suit Sets', }, { - label: "Women Blazers", - value: "Women Blazers" - } - ] + label: 'Women Blazers', + value: 'Women Blazers', + }, + ], }, { - label: "Women Co-ords", - value: "Women Co-ords", + label: 'Women Co-ords', + value: 'Women Co-ords', children: [ { - label: "Two-piece Outfits", - value: "Two-piece Outfits" - } - ] - } - ] - } + label: 'Two-piece Outfits', + value: 'Two-piece Outfits', + }, + ], + }, + ], + }, ]; const onChange = (value, selectedOptions) => { - console.log(value, selectedOptions); + console.log(value, selectedOptions); }; - -const Demo = () => console.log('loadData')} -/>; +const Demo = () => ( + console.log('loadData')} + /> +); export default Demo; diff --git a/examples/debug.tsx b/examples/debug.tsx index 01f77c07..f655f608 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -1,4 +1,3 @@ -/* eslint-disable */ import React from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/default-expand-single-option.tsx b/examples/default-expand-single-option.tsx index a7d31034..cd1cff59 100644 --- a/examples/default-expand-single-option.tsx +++ b/examples/default-expand-single-option.tsx @@ -43,7 +43,7 @@ const App = () => { const [value, setValue] = useState([]); - const onChange = (value: any, selectedOptions) => { + const onChange = (value, selectedOptions) => { const lastSelected = selectedOptions[selectedOptions.length - 1]; if (lastSelected.children && lastSelected.children.length === 1) { value.push(lastSelected.children[0].value); diff --git a/examples/defaultValue.tsx b/examples/defaultValue.tsx index 2e30fba2..cfe30588 100644 --- a/examples/defaultValue.tsx +++ b/examples/defaultValue.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/disabled.tsx b/examples/disabled.tsx index f8d0d84d..be8b773e 100644 --- a/examples/disabled.tsx +++ b/examples/disabled.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/dropdown-render.tsx b/examples/dropdown-render.tsx index bb875fac..d18bc21d 100644 --- a/examples/dropdown-render.tsx +++ b/examples/dropdown-render.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/dynamic-options.tsx b/examples/dynamic-options.tsx index 9a79aa05..27e0d903 100644 --- a/examples/dynamic-options.tsx +++ b/examples/dynamic-options.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/hover.tsx b/examples/hover.tsx index 004d767d..763cfb10 100644 --- a/examples/hover.tsx +++ b/examples/hover.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/multiple.tsx b/examples/multiple.tsx index 428b80e7..4f071e61 100644 --- a/examples/multiple.tsx +++ b/examples/multiple.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React from 'react'; import '../assets/index.less'; import Cascader from '../src'; @@ -15,7 +14,7 @@ const optionLists = [ value: 'jiangsu', label: 'Jiangsu', isLeaf: false, - disableCheckbox: false + disableCheckbox: false, }, ]; diff --git a/examples/panel.tsx b/examples/panel.tsx index 492124f3..17e6a2ad 100644 --- a/examples/panel.tsx +++ b/examples/panel.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/search.tsx b/examples/search.tsx index af73f022..3662125a 100644 --- a/examples/search.tsx +++ b/examples/search.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/simple.tsx b/examples/simple.tsx index 427917e2..bada51f3 100644 --- a/examples/simple.tsx +++ b/examples/simple.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/text-trigger.tsx b/examples/text-trigger.tsx index 8a9ce9dd..03b92923 100644 --- a/examples/text-trigger.tsx +++ b/examples/text-trigger.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import React, { useState } from 'react'; import '../assets/index.less'; import Cascader from '../src'; diff --git a/examples/visible.tsx b/examples/visible.tsx index 8dcdbb58..23e52539 100644 --- a/examples/visible.tsx +++ b/examples/visible.tsx @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-shadow */ -/* eslint-disable no-console */ import arrayTreeFilter from 'array-tree-filter'; import React, { useState } from 'react'; import '../assets/index.less'; diff --git a/src/OptionList/index.tsx b/src/OptionList/index.tsx index 1180a8a8..7aa8a47e 100644 --- a/src/OptionList/index.tsx +++ b/src/OptionList/index.tsx @@ -1,4 +1,3 @@ -/* eslint-disable default-case */ import { useBaseProps } from 'rc-select'; import type { RefOptionListProps } from 'rc-select/lib/OptionList'; import * as React from 'react'; diff --git a/tests/Panel.spec.tsx b/tests/Panel.spec.tsx index 6718fe4f..10ffb9f3 100644 --- a/tests/Panel.spec.tsx +++ b/tests/Panel.spec.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-no-bind */ - import { fireEvent, render } from '@testing-library/react'; import React from 'react'; import Cascader, { type CascaderProps } from '../src'; diff --git a/tests/checkable.spec.tsx b/tests/checkable.spec.tsx index 56cc137e..5ccf02c2 100644 --- a/tests/checkable.spec.tsx +++ b/tests/checkable.spec.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-no-bind */ - import { fireEvent, render } from '@testing-library/react'; import React from 'react'; import Cascader from '../src'; diff --git a/tests/fieldNames.spec.tsx b/tests/fieldNames.spec.tsx index 33560b79..c90d3227 100644 --- a/tests/fieldNames.spec.tsx +++ b/tests/fieldNames.spec.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-no-bind */ - import React from 'react'; import { mount } from './enzyme'; import Cascader from '../src'; diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index f0d46ca3..3a6a0c0d 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-no-bind */ - import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; import { resetWarned } from 'rc-util/lib/warning'; import React, { useEffect, useState } from 'react'; diff --git a/tests/loadData.spec.tsx b/tests/loadData.spec.tsx index e7bb5522..7b71ea52 100644 --- a/tests/loadData.spec.tsx +++ b/tests/loadData.spec.tsx @@ -1,5 +1,3 @@ -/* eslint-disable react/jsx-no-bind */ - import React from 'react'; import { act } from 'react-dom/test-utils'; import { mount } from './enzyme'; diff --git a/tests/private.spec.tsx b/tests/private.spec.tsx index 612caec7..ab9ff361 100644 --- a/tests/private.spec.tsx +++ b/tests/private.spec.tsx @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/consistent-type-imports */ - import { render } from '@testing-library/react'; import React from 'react'; import Cascader from '../src'; diff --git a/tests/search.spec.tsx b/tests/search.spec.tsx index 5125d443..279c5d6a 100644 --- a/tests/search.spec.tsx +++ b/tests/search.spec.tsx @@ -1,12 +1,11 @@ -/* eslint-disable @typescript-eslint/consistent-type-imports */ - import { fireEvent, render } from '@testing-library/react'; import KeyCode from 'rc-util/lib/KeyCode'; import { resetWarned } from 'rc-util/lib/warning'; import React from 'react'; import Cascader from '../src'; import { optionsForActiveMenuItems } from './demoOptions'; -import { mount, ReactWrapper } from './enzyme'; +import type { ReactWrapper } from './enzyme'; +import { mount } from './enzyme'; describe('Cascader.Search', () => { function doSearch(wrapper: ReactWrapper, search: string) { From eb08674c3ee1cf7fd61314a8aaa3c7accb156089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Wed, 3 Apr 2024 11:38:58 +0800 Subject: [PATCH 04/54] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/adjust-overflow.tsx | 4 +- examples/animation.tsx | 4 +- examples/change-on-select.tsx | 4 +- examples/custom-arrow-icon.tsx | 17 +++-- examples/custom-field-name.tsx | 9 ++- examples/debug.tsx | 37 +++++------ examples/default-expand-single-option.tsx | 8 ++- examples/defaultValue.tsx | 4 +- examples/dropdown-render.tsx | 4 +- examples/dynamic-options.tsx | 6 +- examples/hover.tsx | 4 +- examples/multiple.tsx | 20 ++++-- examples/panel.tsx | 33 ++++++---- examples/rc-form.tsx | 4 +- examples/simple.tsx | 4 +- examples/text-trigger.tsx | 4 +- examples/utils.tsx | 17 +++++ examples/value.tsx | 6 +- examples/visible.tsx | 12 ++-- package.json | 2 +- src/Cascader.tsx | 75 ++++++++++------------- src/OptionList/Checkbox.tsx | 2 +- src/OptionList/Column.tsx | 26 ++++---- src/OptionList/List.tsx | 21 ++++--- src/OptionList/useActive.ts | 8 ++- src/OptionList/useKeyboard.ts | 29 +++++---- src/Panel.tsx | 15 ++--- src/context.ts | 9 ++- src/hooks/useDisplayValues.ts | 7 +-- src/hooks/useEntities.ts | 8 ++- src/hooks/useOptions.ts | 2 +- src/hooks/useSearchOptions.ts | 21 +++++-- src/hooks/useSelect.ts | 6 +- src/index.tsx | 1 - src/utils/commonUtil.ts | 18 ++++-- src/utils/treeUtil.ts | 10 +-- src/utils/warningPropsUtil.ts | 6 +- tests/Panel.spec.tsx | 4 +- tests/checkable.spec.tsx | 4 +- tests/fieldNames.spec.tsx | 4 +- tests/index.spec.tsx | 9 +-- tests/keyboard.spec.tsx | 17 ++--- tests/loadData.spec.tsx | 3 +- tests/search.spec.tsx | 20 +++--- tsconfig.json | 15 ++--- 45 files changed, 314 insertions(+), 229 deletions(-) create mode 100644 examples/utils.tsx diff --git a/examples/adjust-overflow.tsx b/examples/adjust-overflow.tsx index 637b91db..f979a10c 100644 --- a/examples/adjust-overflow.tsx +++ b/examples/adjust-overflow.tsx @@ -1,7 +1,9 @@ import type { BuildInPlacements } from 'rc-trigger/lib/interface'; import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -60,7 +62,7 @@ const addressOptions = [ const MyCascader = ({ builtinPlacements }: { builtinPlacements?: BuildInPlacements }) => { const [inputValue, setInputValue] = useState(''); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/animation.tsx b/examples/animation.tsx index 4909d666..5eaab0d7 100644 --- a/examples/animation.tsx +++ b/examples/animation.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -58,7 +60,7 @@ const addressOptions = [ const Demo = () => { const [inputValue, setInputValue] = useState(''); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/change-on-select.tsx b/examples/change-on-select.tsx index e0825919..6d615424 100644 --- a/examples/change-on-select.tsx +++ b/examples/change-on-select.tsx @@ -1,6 +1,8 @@ import React from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const options = [ { @@ -61,7 +63,7 @@ const options = [ }, ]; -const onChange = (value, selectedOptions) => { +const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); }; diff --git a/examples/custom-arrow-icon.tsx b/examples/custom-arrow-icon.tsx index d693e4e0..587bc21d 100644 --- a/examples/custom-arrow-icon.tsx +++ b/examples/custom-arrow-icon.tsx @@ -1,8 +1,10 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { CascaderProps, SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; -const addressOptions = [ +const addressOptions: CascaderProps['options'] = [ { label: '福建', value: 'fj', @@ -83,7 +85,7 @@ const loadingPath = const Demo = () => { const [inputValue, setInputValue] = useState(''); const [dynamicInputValue, setDynamicInputValue] = useState(''); - const [options, setOptions] = useState([ + const [options, setOptions] = useState['options']>([ { label: '福建', isLeaf: false, @@ -96,12 +98,15 @@ const Demo = () => { }, ]); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - const onChangeDynamic = (value, selectedOptions) => { + const onChangeDynamic: SingleCascaderProps['onChange'] = ( + value, + selectedOptions, + ) => { console.log(value, selectedOptions); setDynamicInputValue(selectedOptions.map(o => o.label).join(', ')); }; @@ -146,7 +151,7 @@ const Demo = () => {
); - const loadData = selectedOptions => { + const loadData: SingleCascaderProps['loadData'] = selectedOptions => { const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.loading = true; // 动态加载下级数据 @@ -162,7 +167,7 @@ const Demo = () => { value: 'dynamic2', }, ]; - setOptions([...options]); + setOptions([...(options || [])]); }, 1500); }; diff --git a/examples/custom-field-name.tsx b/examples/custom-field-name.tsx index dd4a24e2..ab127e74 100644 --- a/examples/custom-field-name.tsx +++ b/examples/custom-field-name.tsx @@ -1,8 +1,10 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option } from './utils'; -const addressOptions = [ +const addressOptions: Option[] = [ { name: '福建', code: 'fj', @@ -59,7 +61,10 @@ const addressOptions = [ const Demo = () => { const [inputValue, setInputValue] = useState(''); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = ( + value, + selectedOptions, + ) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.name).join(', ')); }; diff --git a/examples/debug.tsx b/examples/debug.tsx index f655f608..d427ccf2 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -1,8 +1,9 @@ import React from 'react'; import '../assets/index.less'; import Cascader from '../src'; +import type { Option2 } from './utils'; -const addressOptions = [ +const addressOptions: Option2[] = [ // ...new Array(20).fill(null).map((_, i) => ({ label: String(i), value: `99${i}` })), { label: 空孩子, @@ -105,27 +106,19 @@ const Demo = () => { /> Multiple - { - // console.log(props); - // return props.label as any; - // }} - // direction="rtl" - // searchValue="福a" - // changeOnSelect - /> + {multiple ? ( + + ) : ( + + )} ); }; diff --git a/examples/default-expand-single-option.tsx b/examples/default-expand-single-option.tsx index cd1cff59..a4dc04ef 100644 --- a/examples/default-expand-single-option.tsx +++ b/examples/default-expand-single-option.tsx @@ -1,7 +1,9 @@ /* eslint-disable @typescript-eslint/no-shadow */ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const options = [ { @@ -41,12 +43,12 @@ const options = [ const App = () => { const [inputValue, setInputValue] = useState(''); - const [value, setValue] = useState([]); + const [value, setValue] = useState([]); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { const lastSelected = selectedOptions[selectedOptions.length - 1]; if (lastSelected.children && lastSelected.children.length === 1) { - value.push(lastSelected.children[0].value); + value.push(lastSelected.children[0].value as string); setInputValue(selectedOptions.map(o => o.label).join(', ')); setValue(value); return; diff --git a/examples/defaultValue.tsx b/examples/defaultValue.tsx index cfe30588..943d4d6c 100644 --- a/examples/defaultValue.tsx +++ b/examples/defaultValue.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -93,7 +95,7 @@ const defaultOptions = [ const Demo = () => { const [inputValue, setInputValue] = useState(defaultOptions.map(o => o.label).join(', ')); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/dropdown-render.tsx b/examples/dropdown-render.tsx index d18bc21d..b236483d 100644 --- a/examples/dropdown-render.tsx +++ b/examples/dropdown-render.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -59,7 +61,7 @@ const addressOptions = [ const Demo = () => { const [inputValue, setInputValue] = useState(''); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/dynamic-options.tsx b/examples/dynamic-options.tsx index 27e0d903..f9e64562 100644 --- a/examples/dynamic-options.tsx +++ b/examples/dynamic-options.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -19,12 +21,12 @@ const Demo = () => { const [inputValue, setInputValue] = useState(''); const [options, setOptions] = useState(addressOptions); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log('OnChange:', value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - const loadData = selectedOptions => { + const loadData: SingleCascaderProps['loadData'] = selectedOptions => { console.log('onLoad:', selectedOptions); const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.loading = true; diff --git a/examples/hover.tsx b/examples/hover.tsx index 763cfb10..01888d38 100644 --- a/examples/hover.tsx +++ b/examples/hover.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -82,7 +84,7 @@ const addressOptions = [ const Demo = () => { const [inputValue, setInputValue] = useState(''); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/multiple.tsx b/examples/multiple.tsx index 4f071e61..80ea87df 100644 --- a/examples/multiple.tsx +++ b/examples/multiple.tsx @@ -1,6 +1,10 @@ -import React from 'react'; +/* eslint-disable @typescript-eslint/no-shadow */ +import React, { useState } from 'react'; import '../assets/index.less'; +import type { MultipleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; + const { SHOW_CHILD } = Cascader; const optionLists = [ @@ -19,13 +23,16 @@ const optionLists = [ ]; const Demo = () => { - const [options, setOptions] = React.useState(optionLists); + const [options, setOptions] = + React.useState['options']>>(optionLists); + const [value, setValue] = useState([]); - const onChange = (value, selectedOptions) => { + const onChange: MultipleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); + setValue(value); }; - const loadData = selectedOptions => { + const loadData: MultipleCascaderProps['loadData'] = selectedOptions => { const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.loading = true; @@ -44,17 +51,18 @@ const Demo = () => { disableCheckbox: true, }, ]; - setOptions([...options]); + setOptions([...(options || [])]); }, 1000); }; // 直接选中一级选项,但是此时二级选项没有全部选中 return ( - checkable options={options} showCheckedStrategy={SHOW_CHILD} loadData={loadData} + value={value} onChange={onChange} changeOnSelect /> diff --git a/examples/panel.tsx b/examples/panel.tsx index 17e6a2ad..5e0a51f6 100644 --- a/examples/panel.tsx +++ b/examples/panel.tsx @@ -1,6 +1,9 @@ +/* eslint-disable @typescript-eslint/no-shadow */ import React from 'react'; import '../assets/index.less'; +import type { MultipleCascaderProps, SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -56,7 +59,22 @@ const addressOptions = [ ]; export default () => { - const [value, setValue] = React.useState([]); + const [value, setValue] = React.useState([]); + + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { + console.log(value, selectedOptions); + setValue(value); + }; + + const [value2, setValue2] = React.useState([]); + + const onMultipleChange: MultipleCascaderProps['onChange'] = ( + value, + selectedOptions, + ) => { + console.log(value, selectedOptions); + setValue2(value2); + }; return ( <> @@ -68,22 +86,13 @@ export default () => { > Set Value - { - console.log('Change:', nextValue); - setValue(nextValue); - }} - /> + { - console.log('Change:', nextValue); - }} + onChange={onMultipleChange} /> diff --git a/examples/rc-form.tsx b/examples/rc-form.tsx index bc78e340..c0c46cd7 100644 --- a/examples/rc-form.tsx +++ b/examples/rc-form.tsx @@ -1,8 +1,10 @@ import arrayTreeFilter from 'array-tree-filter'; import Form, { Field } from 'rc-field-form'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; import React from 'react'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -58,7 +60,7 @@ const addressOptions = [ ]; const CascaderInput = (props: any) => { - const onChange = value => { + const onChange: SingleCascaderProps['onChange'] = value => { if (props.onChange) { props.onChange(value); } diff --git a/examples/simple.tsx b/examples/simple.tsx index bada51f3..e709d6f1 100644 --- a/examples/simple.tsx +++ b/examples/simple.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -59,7 +61,7 @@ const addressOptions = [ const Demo = () => { const [inputValue, setInputValue] = useState(''); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/text-trigger.tsx b/examples/text-trigger.tsx index 03b92923..87d6a252 100644 --- a/examples/text-trigger.tsx +++ b/examples/text-trigger.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -58,7 +60,7 @@ const addressOptions = [ const Demo = () => { const [inputValue, setInputValue] = useState(''); - const onChange = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/utils.tsx b/examples/utils.tsx new file mode 100644 index 00000000..c5657894 --- /dev/null +++ b/examples/utils.tsx @@ -0,0 +1,17 @@ +export interface Option { + code?: string | number; + name?: string; + nodes?: Option[]; + disabled?: boolean; +} + +export interface Option2 { + value?: string | number; + label?: React.ReactNode; + title?: React.ReactNode; + disabled?: boolean; + disableCheckbox?: boolean; + isLeaf?: boolean; + loading?: boolean; + children?: Option2[]; +} diff --git a/examples/value.tsx b/examples/value.tsx index ec4c3e6c..30366846 100644 --- a/examples/value.tsx +++ b/examples/value.tsx @@ -2,7 +2,9 @@ import arrayTreeFilter from 'array-tree-filter'; import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -58,8 +60,8 @@ const addressOptions = [ ]; const Demo = () => { - const [value, setValue] = useState([]); - const onChange = value => { + const [value, setValue] = useState([]); + const onChange: SingleCascaderProps['onChange'] = value => { console.log(value); setValue(value); }; diff --git a/examples/visible.tsx b/examples/visible.tsx index 23e52539..1463aced 100644 --- a/examples/visible.tsx +++ b/examples/visible.tsx @@ -2,7 +2,9 @@ import arrayTreeFilter from 'array-tree-filter'; import React, { useState } from 'react'; import '../assets/index.less'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; +import type { Option2 } from './utils'; const addressOptions = [ { @@ -58,17 +60,13 @@ const addressOptions = [ ]; const Demo = () => { - const [value, setValue] = useState([]); + const [value, setValue] = useState([]); const [popupVisible, setPopupVisible] = useState(false); - const onChange = value => { + const onChange: SingleCascaderProps['onChange'] = value => { setValue(value); }; - const onPopupVisibleChange = popupVisible => { - setPopupVisible(popupVisible); - }; - const getLabel = () => { return arrayTreeFilter(addressOptions, (o, level) => o.value === value[level]) .map(o => o.label) @@ -80,7 +78,7 @@ const Demo = () => { popupVisible={popupVisible} value={value} options={addressOptions} - onPopupVisibleChange={onPopupVisibleChange} + onPopupVisibleChange={open => setPopupVisible(open)} onChange={onChange} > diff --git a/package.json b/package.json index 30bd12fa..0df13db1 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "rc-test": "^7.0.14", "react": "^16.0.0", "react-dom": "^16.0.0", - "typescript": "^5.3.2" + "typescript": "^5.4.3" }, "peerDependencies": { "react": ">=16.9.0", diff --git a/src/Cascader.tsx b/src/Cascader.tsx index 4c75b769..1d19eec8 100644 --- a/src/Cascader.tsx +++ b/src/Cascader.tsx @@ -26,7 +26,7 @@ import { import { formatStrategyValues, toPathOptions } from './utils/treeUtil'; import warningProps, { warningNullOptions } from './utils/warningPropsUtil'; -export interface ShowSearchType { +export interface ShowSearchType> { filter?: (inputValue: string, options: OptionType[], fieldNames: FieldNames) => boolean; render?: ( inputValue: string, @@ -51,21 +51,17 @@ export interface InternalFieldNames extends Required { export type SingleValueType = (string | number)[]; -export type ValueType = SingleValueType | SingleValueType[]; export type ShowCheckedStrategy = typeof SHOW_PARENT | typeof SHOW_CHILD; -export interface BaseOptionType { - disabled?: boolean; - [name: string]: any; -} -export interface DefaultOptionType extends BaseOptionType { - label: React.ReactNode; - value?: string | number | null; - children?: DefaultOptionType[]; +export interface DefaultOptionType { + label?: React.ReactNode; + value?: T; + children?: DefaultOptionType[]; disableCheckbox?: boolean; + disabled?: boolean; } -interface BaseCascaderProps +interface BaseCascaderProps> extends Omit< BaseSelectPropsWithoutPrivate, 'tokenSeparators' | 'labelInValue' | 'mode' | 'showSearch' @@ -78,8 +74,6 @@ interface BaseCascaderProps React.ReactNode; checkable?: boolean | React.ReactNode; @@ -87,7 +81,7 @@ interface BaseCascaderProps; + showSearch?: boolean | ShowSearchType; searchValue?: string; onSearch?: (value: string) => void; @@ -123,38 +117,31 @@ interface BaseCascaderProps = (value: SingleValueType, selectOptions: OptionType[]) => void; -type OnMultipleChange = ( - value: SingleValueType[], - selectOptions: OptionType[][], -) => void; - -export interface SingleCascaderProps - extends BaseCascaderProps { +export interface SingleCascaderProps> + extends BaseCascaderProps { checkable?: false; - - onChange?: OnSingleChange; + value?: T[]; + defaultValue?: T[]; + onChange?: (value: T[], selectOptions: OptionType[]) => void; } -export interface MultipleCascaderProps - extends BaseCascaderProps { +export interface MultipleCascaderProps + extends BaseCascaderProps { checkable: true | React.ReactNode; - - onChange?: OnMultipleChange; + value?: T[][]; + defaultValue?: T[][]; + onChange?: (value: T[][], selectOptions: OptionType[]) => void; } -export type CascaderProps = - | SingleCascaderProps - | MultipleCascaderProps; +export type CascaderProps> = + | SingleCascaderProps + | MultipleCascaderProps; -export type InternalCascaderProps = Omit< - SingleCascaderProps | MultipleCascaderProps, +export type InternalCascaderProps> = Omit< + SingleCascaderProps | MultipleCascaderProps, 'onChange' > & { - onChange?: ( - value: SingleValueType | SingleValueType[], - selectOptions: OptionType[] | OptionType[][], - ) => void; + onChange?: (value: T | T[], selectOptions: OptionType[] | OptionType[][]) => void; }; export type CascaderRef = Omit; @@ -167,9 +154,9 @@ const Cascader = React.forwardRef((props, re fieldNames, // Value - defaultValue, + defaultValue = [], value, - changeOnSelect, + changeOnSelect = false, onChange, displayRender, checkable, @@ -219,7 +206,7 @@ const Cascader = React.forwardRef((props, re const multiple = !!checkable; // =========================== Values =========================== - const [rawValues, setRawValues] = useMergedState(defaultValue, { + const [rawValues, setRawValues] = useMergedState(defaultValue, { value, postState: toRawValues, }); @@ -301,7 +288,7 @@ const Cascader = React.forwardRef((props, re ); // =========================== Change =========================== - const triggerChange = useEvent((nextValues: ValueType) => { + const triggerChange = useEvent((nextValues: any[]) => { setRawValues(nextValues); // Save perf if no need trigger event @@ -453,12 +440,12 @@ const Cascader = React.forwardRef((props, re placement={mergedPlacement} onDropdownVisibleChange={onInternalDropdownVisibleChange} // Children - getRawInputElement={() => children} + getRawInputElement={() => children as React.ReactElement} /> ); -}) as unknown as (( - props: React.PropsWithChildren> & { +}) as unknown as (>( + props: React.PropsWithChildren> & { ref?: React.Ref; }, ) => React.ReactElement) & { diff --git a/src/OptionList/Checkbox.tsx b/src/OptionList/Checkbox.tsx index f01a639b..f06cfb23 100644 --- a/src/OptionList/Checkbox.tsx +++ b/src/OptionList/Checkbox.tsx @@ -8,7 +8,7 @@ export interface CheckboxProps { halfChecked?: boolean; disabled?: boolean; onClick?: React.MouseEventHandler; - disableCheckbox: boolean; + disableCheckbox?: boolean; } export default function Checkbox({ diff --git a/src/OptionList/Column.tsx b/src/OptionList/Column.tsx index d1bac9f5..8ba110c5 100644 --- a/src/OptionList/Column.tsx +++ b/src/OptionList/Column.tsx @@ -8,10 +8,10 @@ import Checkbox from './Checkbox'; export const FIX_LABEL = '__cascader_fix_label__'; -export interface ColumnProps { +export interface ColumnProps> { prefixCls: string; multiple?: boolean; - options: DefaultOptionType[]; + options: OptionType[]; /** Current Column opened item key */ activeValue?: React.Key; /** The value path before current column */ @@ -26,7 +26,7 @@ export interface ColumnProps { searchValue?: string; } -export default function Column({ +export default function Column>({ prefixCls, multiple, options, @@ -40,7 +40,7 @@ export default function Column({ loadingKeys, isSelectable, searchValue, -}: ColumnProps) { +}: ColumnProps) { const menuPrefixCls = `${prefixCls}-menu`; const menuItemPrefixCls = `${prefixCls}-menu-item`; @@ -59,17 +59,19 @@ export default function Column({ // ============================ Option ============================ const optionInfoList = React.useMemo( () => - options.map(option => { + (options as DefaultOptionType[]).map(option => { const { disabled, disableCheckbox } = option; - const searchOptions = option[SEARCH_MARK]; - const label = option[FIX_LABEL] ?? option[fieldNames.label]; - const value = option[fieldNames.value]; + const searchOptions = (option as Record)[SEARCH_MARK]; + const label = + (option as Record)[FIX_LABEL] ?? + (option as Record)[fieldNames.label]; + const value = (option as Record)[fieldNames.value]; const isMergedLeaf = isLeaf(option, fieldNames); // Get real value of option. Search option is different way. const fullPath = searchOptions - ? searchOptions.map(opt => opt[fieldNames.value]) + ? searchOptions.map((opt: any) => opt[fieldNames.value]) : [...prevValuePath, value]; const fullPathKey = toPathKey(fullPath); @@ -135,9 +137,9 @@ export default function Column({ }; // >>>>> Title - let title: string; - if (typeof option.title === 'string') { - title = option.title; + let title: string | undefined; + if (typeof (option as Record).title === 'string') { + title = (option as Record).title; } else if (typeof label === 'string') { title = label; } diff --git a/src/OptionList/List.tsx b/src/OptionList/List.tsx index 9b0ce3a4..7cd47742 100644 --- a/src/OptionList/List.tsx +++ b/src/OptionList/List.tsx @@ -27,11 +27,11 @@ export type RawOptionListProps = Pick< const RawOptionList = React.forwardRef((props, ref) => { const { prefixCls, multiple, searchValue, toggleOpen, notFoundContent, direction, open } = props; - const containerRef = React.useRef(); + const containerRef = React.useRef(null); const rtl = direction === 'rtl'; const { - options, + options = [], values, halfValues, fieldNames, @@ -46,7 +46,7 @@ const RawOptionList = React.forwardRef(( const mergedPrefixCls = dropdownPrefixCls || prefixCls; // ========================= loadData ========================= - const [loadingKeys, setLoadingKeys] = React.useState([]); + const [loadingKeys, setLoadingKeys] = React.useState([]); const internalLoadData = (valueCells: React.Key[]) => { // Do not load when search @@ -71,13 +71,17 @@ const RawOptionList = React.forwardRef(( React.useEffect(() => { if (loadingKeys.length) { loadingKeys.forEach(loadingKey => { - const valueStrCells = toPathValueStr(loadingKey); + const valueStrCells = toPathValueStr(loadingKey as string); const optionList = toPathOptions(valueStrCells, options, fieldNames, true).map( ({ option }) => option, ); const lastOption = optionList[optionList.length - 1]; - if (!lastOption || lastOption[fieldNames.children] || isLeaf(lastOption, fieldNames)) { + if ( + !lastOption || + (lastOption as Record)[fieldNames.children] || + isLeaf(lastOption, fieldNames) + ) { setLoadingKeys(keys => keys.filter(key => key !== loadingKey)); } }); @@ -134,11 +138,12 @@ const RawOptionList = React.forwardRef(( const activeValueCell = activeValueCells[i]; const currentOption = currentList.find( (option, index) => - (fullPathKeys[index] ? toPathKey(fullPathKeys[index]) : option[fieldNames.value]) === - activeValueCell, + (fullPathKeys[index] + ? toPathKey(fullPathKeys[index]) + : (option as Record)[fieldNames.value]) === activeValueCell, ); - const subOptions = currentOption?.[fieldNames.children]; + const subOptions = (currentOption as Record)?.[fieldNames.children]; if (!subOptions?.length) { break; } diff --git a/src/OptionList/useActive.ts b/src/OptionList/useActive.ts index 7bf02724..ea2aa907 100644 --- a/src/OptionList/useActive.ts +++ b/src/OptionList/useActive.ts @@ -4,9 +4,9 @@ import CascaderContext from '../context'; /** * Control the active open options path. */ -export default ( - multiple: boolean, - open: boolean, +const useActive = ( + multiple?: boolean, + open?: boolean, ): [React.Key[], (activeValueCells: React.Key[]) => void] => { const { values } = React.useContext(CascaderContext); @@ -29,3 +29,5 @@ export default ( return [activeValueCells, setActiveValueCells]; }; + +export default useActive; diff --git a/src/OptionList/useKeyboard.ts b/src/OptionList/useKeyboard.ts index cc607995..994aa87e 100644 --- a/src/OptionList/useKeyboard.ts +++ b/src/OptionList/useKeyboard.ts @@ -13,10 +13,10 @@ export default ( setActiveValueCells: (activeValueCells: React.Key[]) => void, onKeyBoardSelect: (valueCells: SingleValueType, option: DefaultOptionType) => void, contextProps: { - direction: 'ltr' | 'rtl'; + direction?: 'ltr' | 'rtl'; searchValue: string; toggleOpen: (open?: boolean) => void; - open: boolean; + open?: boolean; }, ) => { const { direction, searchValue, toggleOpen, open } = contextProps; @@ -39,8 +39,9 @@ export default ( // Mark the active index for current options const nextActiveIndex = currentOptions.findIndex( (option, index) => - (pathKeys[index] ? toPathKey(pathKeys[index]) : option[fieldNames.value]) === - activeValueCells[i], + (pathKeys[index] + ? toPathKey(pathKeys[index]) + : (option as Record)[fieldNames.value]) === activeValueCells[i], ); if (nextActiveIndex === -1) { @@ -51,13 +52,15 @@ export default ( mergedActiveIndexes.push(activeIndex); mergedActiveValueCells.push(activeValueCells[i]); - currentOptions = currentOptions[activeIndex][fieldNames.children]; + currentOptions = (currentOptions as Record)[activeIndex][fieldNames.children]; } // Fill last active options let activeOptions = options; for (let i = 0; i < mergedActiveIndexes.length - 1; i += 1) { - activeOptions = activeOptions[mergedActiveIndexes[i]][fieldNames.children]; + activeOptions = (activeOptions as Record)[mergedActiveIndexes[i]][ + fieldNames.children + ]; } return [mergedActiveValueCells, activeIndex, activeOptions, pathKeys]; @@ -86,7 +89,7 @@ export default ( .concat( fullPathKeys[currentIndex] ? toPathKey(fullPathKeys[currentIndex]) - : option[fieldNames.value], + : (option as Record)[fieldNames.value], ); internalSetActiveValueCells(nextActiveCells); return; @@ -106,12 +109,15 @@ export default ( const nextColumn = () => { const nextOptions: DefaultOptionType[] = - lastActiveOptions[lastActiveIndex]?.[fieldNames.children] || []; + (lastActiveOptions as Record)[lastActiveIndex]?.[fieldNames.children] || []; const nextOption = nextOptions.find(option => !option.disabled); if (nextOption) { - const nextActiveCells = [...validActiveValueCells, nextOption[fieldNames.value]]; + const nextActiveCells = [ + ...validActiveValueCells, + (nextOption as Record)[fieldNames.value], + ]; internalSetActiveValueCells(nextActiveCells); } }; @@ -176,10 +182,11 @@ export default ( const option = lastActiveOptions[lastActiveIndex]; // Search option should revert back of origin options - const originOptions: DefaultOptionType[] = option?.[SEARCH_MARK] || []; + const originOptions: DefaultOptionType[] = + (option as Record)?.[SEARCH_MARK] || []; if (originOptions.length) { onKeyBoardSelect( - originOptions.map(opt => opt[fieldNames.value]), + originOptions.map(opt => (opt as Record)[fieldNames.value]), originOptions[originOptions.length - 1], ); } else { diff --git a/src/Panel.tsx b/src/Panel.tsx index d1c7d0d3..ef08baad 100644 --- a/src/Panel.tsx +++ b/src/Panel.tsx @@ -1,7 +1,8 @@ import classNames from 'classnames'; import { useEvent, useMergedState } from 'rc-util'; import * as React from 'react'; -import type { CascaderProps, InternalCascaderProps, SingleValueType, ValueType } from './Cascader'; +import type { CascaderProps, InternalCascaderProps, SingleValueType } from './Cascader'; +import type { CascaderContextProps } from './context'; import CascaderContext from './context'; import useMissingValues from './hooks/useMissingValues'; import useOptions from './hooks/useOptions'; @@ -59,7 +60,7 @@ export default function Panel(props: PanelProps) { const multiple = !!checkable; // ========================= Values ========================= - const [rawValues, setRawValues] = useMergedState(defaultValue, { + const [rawValues, setRawValues] = useMergedState(defaultValue, { value, postState: toRawValues, }); @@ -91,7 +92,7 @@ export default function Panel(props: PanelProps) { ); // =========================== Change =========================== - const triggerChange = useEvent((nextValues: ValueType) => { + const triggerChange = useEvent((nextValues: any[]) => { setRawValues(nextValues); // Save perf if no need trigger event @@ -126,7 +127,7 @@ export default function Panel(props: PanelProps) { }); // ======================== Context ========================= - const cascaderContext = React.useMemo( + const cascaderContext = React.useMemo( () => ({ options: mergedOptions, fieldNames: mergedFieldNames, @@ -136,12 +137,12 @@ export default function Panel(props: PanelProps) { onSelect: onInternalSelect, checkable, searchOptions: [], - dropdownPrefixCls: null, + dropdownPrefixCls: '', loadData, expandTrigger, expandIcon, loadingIcon, - dropdownMenuColumnStyle: null, + dropdownMenuColumnStyle: {}, }), [ mergedOptions, @@ -180,7 +181,7 @@ export default function Panel(props: PanelProps) { ) : ( (null); +const CascaderContext = React.createContext({ + options: [], + fieldNames: { key: '', label: '', value: '', children: '' }, + values: [], + halfValues: [], + onSelect: () => undefined, + searchOptions: [], +}); export default CascaderContext; diff --git a/src/hooks/useDisplayValues.ts b/src/hooks/useDisplayValues.ts index 0463bd2e..cc15934f 100644 --- a/src/hooks/useDisplayValues.ts +++ b/src/hooks/useDisplayValues.ts @@ -20,7 +20,7 @@ export default ( displayRender || // Default displayRender (labels => { - const mergedLabels = multiple ? labels.slice(-1) : labels; + const mergedLabels: React.ReactNode[] = multiple ? labels.slice(-1) : labels; const SPLIT = ' / '; if (mergedLabels.every(label => ['string', 'number'].includes(typeof label))) { @@ -36,8 +36,7 @@ export default ( if (index === 0) { return [keyedLabel]; } - - return [...list, SPLIT, keyedLabel]; + return [...(Array.isArray(list) ? list : []), SPLIT, keyedLabel]; }, []); }); @@ -45,7 +44,7 @@ export default ( const valueOptions = toPathOptions(valueCells, options, fieldNames); const label = mergedDisplayRender( - valueOptions.map(({ option, value }) => option?.[fieldNames.label] ?? value), + valueOptions.map(({ option, value }) => (option as any)?.[fieldNames.label] ?? value), valueOptions.map(({ option }) => option), ); diff --git a/src/hooks/useEntities.ts b/src/hooks/useEntities.ts index fdb37f9b..73ff4eee 100644 --- a/src/hooks/useEntities.ts +++ b/src/hooks/useEntities.ts @@ -17,8 +17,8 @@ export default (options: DefaultOptionType[], fieldNames: InternalFieldNames) => options: DefaultOptionType[]; info: OptionsInfo; }>({ - options: null, - info: null, + options: [], + info: { keyEntities: {}, pathKeyEntities: {} }, }); const getEntities: GetEntities = React.useCallback(() => { @@ -31,7 +31,9 @@ export default (options: DefaultOptionType[], fieldNames: InternalFieldNames) => pathKeyEntities: {}, }), processEntity: (entity, wrapper: any) => { - const pathKey = entity.nodes.map(node => node[fieldNames.value]).join(VALUE_SPLIT); + const pathKey = entity.nodes + .map(node => (node as any)[fieldNames.value]) + .join(VALUE_SPLIT); wrapper.pathKeyEntities[pathKey] = entity; diff --git a/src/hooks/useOptions.ts b/src/hooks/useOptions.ts index f0759d71..ea4c93f9 100644 --- a/src/hooks/useOptions.ts +++ b/src/hooks/useOptions.ts @@ -24,7 +24,7 @@ export default function useOptions( return pathKeys.map(pathKey => { const { nodes } = keyPathEntities[pathKey]; - return nodes.map(node => node[mergedFieldNames.value]); + return nodes.map(node => (node as Record)[mergedFieldNames.value]); }); }, [getPathKeyEntities, mergedFieldNames], diff --git a/src/hooks/useSearchOptions.ts b/src/hooks/useSearchOptions.ts index 289a9191..e71f1100 100644 --- a/src/hooks/useSearchOptions.ts +++ b/src/hooks/useSearchOptions.ts @@ -4,10 +4,14 @@ import type { DefaultOptionType, InternalFieldNames, ShowSearchType } from '../C export const SEARCH_MARK = '__rc_cascader_search_mark__'; const defaultFilter: ShowSearchType['filter'] = (search, options, { label }) => - options.some(opt => String(opt[label]).toLowerCase().includes(search.toLowerCase())); + options.some(opt => + String((opt as Record)[label as string]) + .toLowerCase() + .includes(search.toLowerCase()), + ); const defaultRender: ShowSearchType['render'] = (inputValue, path, prefixCls, fieldNames) => - path.map(opt => opt[fieldNames.label]).join(' / '); + path.map(opt => (opt as Record)[fieldNames.label as string]).join(' / '); export default ( search: string, @@ -37,7 +41,7 @@ export default ( } const connectedPathOptions = [...pathOptions, option]; - const children = option[fieldNames.children]; + const children = (option as Record)[fieldNames.children]; const mergedDisabled = parentDisabled || option.disabled; @@ -59,7 +63,7 @@ export default ( prefixCls, fieldNames, ), - [SEARCH_MARK]: connectedPathOptions, + [SEARCH_MARK as any]: connectedPathOptions, [fieldNames.children]: undefined, }); } @@ -67,7 +71,7 @@ export default ( if (children) { dig( - option[fieldNames.children] as DefaultOptionType[], + (option as Record)[fieldNames.children] as DefaultOptionType[], connectedPathOptions, mergedDisabled, ); @@ -80,7 +84,12 @@ export default ( // Do sort if (sort) { filteredOptions.sort((a, b) => { - return sort(a[SEARCH_MARK], b[SEARCH_MARK], search, fieldNames); + return sort( + (a as Record)[SEARCH_MARK], + (b as Record)[SEARCH_MARK], + search, + fieldNames, + ); }); } diff --git a/src/hooks/useSelect.ts b/src/hooks/useSelect.ts index 60338cd4..56bf817f 100644 --- a/src/hooks/useSelect.ts +++ b/src/hooks/useSelect.ts @@ -1,18 +1,18 @@ import { conductCheck } from 'rc-tree/lib/utils/conductUtil'; -import type { ShowCheckedStrategy, SingleValueType, ValueType } from '../Cascader'; +import type { ShowCheckedStrategy, SingleValueType } from '../Cascader'; import { toPathKey, toPathKeys } from '../utils/commonUtil'; import { formatStrategyValues } from '../utils/treeUtil'; import type { GetEntities } from './useEntities'; export default function useSelect( multiple: boolean, - triggerChange: (nextValues: ValueType) => void, + triggerChange: (nextValues: any) => void, checkedValues: SingleValueType[], halfCheckedValues: SingleValueType[], missingCheckedValues: SingleValueType[], getPathKeyEntities: GetEntities, getValueByKeyPath: (pathKeys: React.Key[]) => SingleValueType[], - showCheckedStrategy: ShowCheckedStrategy, + showCheckedStrategy?: ShowCheckedStrategy, ) { return (valuePath: SingleValueType) => { if (!multiple) { diff --git a/src/index.tsx b/src/index.tsx index 35ab7064..75ee4921 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,7 +2,6 @@ import Cascader from './Cascader'; import Panel from './Panel'; export type { - BaseOptionType, CascaderProps, DefaultOptionType, FieldNames, diff --git a/src/utils/commonUtil.ts b/src/utils/commonUtil.ts index c54eb4c0..47770c82 100644 --- a/src/utils/commonUtil.ts +++ b/src/utils/commonUtil.ts @@ -3,7 +3,6 @@ import type { FieldNames, InternalFieldNames, SingleValueType, - ValueType, } from '../Cascader'; import { SEARCH_MARK } from '../hooks/useSearchOptions'; @@ -41,7 +40,10 @@ export function fillFieldNames(fieldNames?: FieldNames): InternalFieldNames { } export function isLeaf(option: DefaultOptionType, fieldNames: FieldNames) { - return option.isLeaf ?? !option[fieldNames.children]?.length; + return ( + (option as Record).isLeaf ?? + !(option as Record)[fieldNames.children as string]?.length + ); } export function scrollIntoParentView(element: HTMLElement) { @@ -59,14 +61,18 @@ export function scrollIntoParentView(element: HTMLElement) { } export function getFullPathKeys(options: DefaultOptionType[], fieldNames: FieldNames) { - return options.map(item => item[SEARCH_MARK]?.map(opt => opt[fieldNames.value])); + return options.map(item => + (item as Record)[SEARCH_MARK]?.map( + (opt: Record) => (opt as Record)[fieldNames.value as string], + ), + ); } -function isMultipleValue(value: ValueType): value is SingleValueType[] { +function isMultipleValue(value: any[]): value is SingleValueType[] { return Array.isArray(value) && Array.isArray(value[0]); } -export function toRawValues(value: ValueType): SingleValueType[] { +export function toRawValues(value: any[]): SingleValueType[] { if (!value) { return []; } @@ -76,4 +82,4 @@ export function toRawValues(value: ValueType): SingleValueType[] { } return (value.length === 0 ? [] : [value]).map(val => (Array.isArray(val) ? val : [val])); -} \ No newline at end of file +} diff --git a/src/utils/treeUtil.ts b/src/utils/treeUtil.ts index 2ba2bfd8..d301f93f 100644 --- a/src/utils/treeUtil.ts +++ b/src/utils/treeUtil.ts @@ -10,7 +10,7 @@ import { SHOW_CHILD } from './commonUtil'; export function formatStrategyValues( pathKeys: React.Key[], getKeyPathEntities: GetEntities, - showCheckedStrategy: ShowCheckedStrategy, + showCheckedStrategy?: ShowCheckedStrategy, ) { const valueSet = new Set(pathKeys); const keyPathEntities = getKeyPathEntities(); @@ -47,18 +47,18 @@ export function toPathOptions( for (let i = 0; i < valueCells.length; i += 1) { const valueCell = valueCells[i]; const foundIndex = currentList?.findIndex(option => { - const val = option[fieldNames.value]; + const val = (option as Record)[fieldNames.value]; return stringMode ? String(val) === String(valueCell) : val === valueCell; }); - const foundOption = foundIndex !== -1 ? currentList?.[foundIndex] : null; + const foundOption: DefaultOptionType = foundIndex !== -1 ? currentList?.[foundIndex] : {}; valueOptions.push({ - value: foundOption?.[fieldNames.value] ?? valueCell, + value: (foundOption as Record)?.[fieldNames.value] ?? valueCell, index: foundIndex, option: foundOption, }); - currentList = foundOption?.[fieldNames.children]; + currentList = (foundOption as Record)?.[fieldNames.children]; } return valueOptions; diff --git a/src/utils/warningPropsUtil.ts b/src/utils/warningPropsUtil.ts index 2b406996..b56af33d 100644 --- a/src/utils/warningPropsUtil.ts +++ b/src/utils/warningPropsUtil.ts @@ -26,14 +26,14 @@ export function warningNullOptions(options: DefaultOptionType[], fieldNames: Fie for (let i = 0; i < optionsList.length; i++) { const option = optionsList[i]; - if (option[fieldNames?.value] === null) { + if ((option as Record)[fieldNames?.value as string] === null) { warning(false, '`value` in Cascader options should not be `null`.'); return true; } if ( - Array.isArray(option[fieldNames?.children]) && - recursiveOptions(option[fieldNames?.children]) + Array.isArray((option as Record)[fieldNames?.children as string]) && + recursiveOptions((option as Record)[fieldNames?.children as string]) ) { return true; } diff --git a/tests/Panel.spec.tsx b/tests/Panel.spec.tsx index 10ffb9f3..452f1be4 100644 --- a/tests/Panel.spec.tsx +++ b/tests/Panel.spec.tsx @@ -88,13 +88,13 @@ describe('Cascader.Panel', () => { it('notFoundContent', () => { const { container } = render(); - expect(container.querySelector('.rc-cascader-panel-empty').textContent).toEqual('Hello World'); + expect(container.querySelector('.rc-cascader-panel-empty')?.textContent).toEqual('Hello World'); }); it('control', () => { const { container } = render(); const checkedLi = container.querySelector('[aria-checked="true"]'); - expect(checkedLi.textContent).toEqual('Little'); + expect(checkedLi?.textContent).toEqual('Little'); }); }); diff --git a/tests/checkable.spec.tsx b/tests/checkable.spec.tsx index 5ccf02c2..032f889f 100644 --- a/tests/checkable.spec.tsx +++ b/tests/checkable.spec.tsx @@ -253,7 +253,9 @@ describe('Cascader.Checkable', () => { />, ); - fireEvent.click(document.querySelector('[data-path-key="China"] .rc-cascader-checkbox')); + fireEvent.click( + document.querySelector('[data-path-key="China"] .rc-cascader-checkbox') as HTMLElement, + ); expect(onChange).toHaveBeenCalledWith([['China', 'beijing'], ['China']], expect.anything()); }); diff --git a/tests/fieldNames.spec.tsx b/tests/fieldNames.spec.tsx index c90d3227..1b1ce288 100644 --- a/tests/fieldNames.spec.tsx +++ b/tests/fieldNames.spec.tsx @@ -87,12 +87,12 @@ describe('Cascader.FieldNames', () => { it('displayRender', () => { const wrapper = mount( - options={options} fieldNames={fieldNames} defaultValue={['bamboo', 'little', 'toy']} displayRender={(labels, selectOptions) => - `${labels.join('->')} & ${selectOptions.map((opt: any) => opt.customValue).join('>>')}` + `${labels.join('->')} & ${selectOptions?.map(opt => opt.customValue).join('>>')}` } />, ); diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index 3a6a0c0d..0f714c02 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -1,13 +1,14 @@ import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; import { resetWarned } from 'rc-util/lib/warning'; import React, { useEffect, useState } from 'react'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; import { addressOptions, addressOptionsForUneven, optionsForActiveMenuItems } from './demoOptions'; import { mount } from './enzyme'; describe('Cascader.Basic', () => { - let selectedValue; - const onChange = function onChange(value) { + let selectedValue: any; + const onChange: SingleCascaderProps['onChange'] = function onChange(value) { selectedValue = value; }; @@ -677,7 +678,7 @@ describe('Cascader.Basic', () => { }); describe('focus test', () => { - let domSpy; + let domSpy: any; let focusTimes = 0; let blurTimes = 0; @@ -1024,7 +1025,7 @@ describe('Cascader.Basic', () => { it('support custom cascader', () => { const wrapper = mount(); - expect(wrapper.find('.rc-cascader-dropdown').props().style.zIndex).toBe(999); + expect(wrapper.find('.rc-cascader-dropdown').props().style?.zIndex).toBe(999); }); it('`null` is a value in Cascader options should throw a warning', () => { diff --git a/tests/keyboard.spec.tsx b/tests/keyboard.spec.tsx index 5bc83a59..c907e5f4 100644 --- a/tests/keyboard.spec.tsx +++ b/tests/keyboard.spec.tsx @@ -1,15 +1,16 @@ import { mount } from 'enzyme'; import KeyCode from 'rc-util/lib/KeyCode'; +import type { SingleCascaderProps } from '../src'; import Cascader from '../src'; import { addressOptions } from './demoOptions'; import React from 'react'; describe('Cascader.Keyboard', () => { - let wrapper; - let selectedValue; - let selectedOptions; + let wrapper: any; + let selectedValue: any; + let selectedOptions: any; let menus; - const onChange = (value, options) => { + const onChange: SingleCascaderProps['onChange'] = (value, options) => { selectedValue = value; selectedOptions = options; }; @@ -78,8 +79,8 @@ describe('Cascader.Keyboard', () => { expect(selectedValue).toEqual(['zj', 'hangzhou', 'yuhang']); expect(selectedOptions).toEqual([ addressOptions[1], - addressOptions[1].children[0], - addressOptions[1].children[0].children[0], + addressOptions[1]?.children?.[0], + addressOptions[1]?.children?.[0]?.children?.[0], ]); }); @@ -91,8 +92,8 @@ describe('Cascader.Keyboard', () => { expect(selectedValue).toEqual(['zj', 'hangzhou', 'yuhang']); expect(selectedOptions).toEqual([ addressOptions[1], - addressOptions[1].children[0], - addressOptions[1].children[0].children[0], + addressOptions[1]?.children?.[0], + addressOptions[1]?.children?.[0]?.children?.[0], ]); }); it('enter on search when has same sub key', () => { diff --git a/tests/loadData.spec.tsx b/tests/loadData.spec.tsx index 7b71ea52..cf996d37 100644 --- a/tests/loadData.spec.tsx +++ b/tests/loadData.spec.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { mount } from './enzyme'; +import type { CascaderProps } from '../src'; import Cascader from '../src'; describe('Cascader.LoadData', () => { @@ -144,7 +145,7 @@ describe('Cascader.LoadData', () => { const Demo = () => { const [options, setOptions] = React.useState([{ label: 'top', value: 'top', isLeaf: false }]); - const loadData = selectedOptions => { + const loadData: CascaderProps['loadData'] = selectedOptions => { Promise.resolve().then(() => { act(() => { selectedOptions[selectedOptions.length - 1].children = [ diff --git a/tests/search.spec.tsx b/tests/search.spec.tsx index 279c5d6a..1587e8eb 100644 --- a/tests/search.spec.tsx +++ b/tests/search.spec.tsx @@ -231,14 +231,14 @@ describe('Cascader.Search', () => { ], }, ]; - function customFilter(inputValue, path) { - return path.some(option => option.name.toLowerCase().indexOf(inputValue.toLowerCase()) > -1); - } const wrapper = mount( + path.some(option => option.name.toLowerCase().indexOf(inputValue.toLowerCase()) > -1), + }} />, ); wrapper.find('input').simulate('change', { target: { value: 'z' } }); @@ -250,16 +250,16 @@ describe('Cascader.Search', () => { const { container } = render(); // Search - fireEvent.change(container.querySelector('input'), { + fireEvent.change(container.querySelector('input') as HTMLElement, { target: { value: 'bamboo', }, }); // Click - fireEvent.click(document.querySelector('.rc-cascader-menu-item-content')); + fireEvent.click(document.querySelector('.rc-cascader-menu-item-content') as HTMLElement); expect(document.querySelector('.rc-cascader-dropdown-hidden')).toBeTruthy(); - expect(document.querySelector('.rc-cascader-menu-item-content').textContent).toBe( + expect(document.querySelector('.rc-cascader-menu-item-content')?.textContent).toBe( 'Label Bamboo / Label Little / Toy Fish', ); }); @@ -300,7 +300,7 @@ describe('Cascader.Search', () => { expect(container.querySelectorAll('.rc-cascader-menu-item')).toHaveLength(1); expect(container.querySelectorAll('.rc-cascader-menu-item-disabled')).toHaveLength(1); - expect(container.querySelector('.rc-cascader-menu-item-disabled').textContent).toEqual( + expect(container.querySelector('.rc-cascader-menu-item-disabled')?.textContent).toEqual( 'bamboo / little', ); }); @@ -312,7 +312,7 @@ describe('Cascader.Search', () => { optionRender={option => `${option.label} - test`} />, ); - expect(container.querySelector('.rc-cascader-menu-item-content').innerHTML).toEqual( + expect(container.querySelector('.rc-cascader-menu-item-content')?.innerHTML).toEqual( 'bamboo - test', ); rerender( @@ -322,7 +322,7 @@ describe('Cascader.Search', () => { optionRender={option => JSON.stringify(option)} />, ); - expect(container.querySelector('.rc-cascader-menu-item-content').innerHTML).toEqual( + expect(container.querySelector('.rc-cascader-menu-item-content')?.innerHTML).toEqual( '{"label":"bamboo","disabled":true,"value":"bamboo"}', ); }); diff --git a/tsconfig.json b/tsconfig.json index 94ced34b..f0d983d0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,17 +6,12 @@ "jsx": "react", "declaration": true, "skipLibCheck": true, + "strict": true, "esModuleInterop": true, "paths": { - "@/*": [ - "src/*" - ], - "@@/*": [ - "src/.umi/*" - ], - "rc-cascader": [ - "src/index.ts" - ] + "@/*": ["src/*"], + "@@/*": ["src/.umi/*"], + "rc-cascader": ["src/index.ts"] } } -} \ No newline at end of file +} From 2addbfa0a90a8051866d36ba75bb151a9818f4e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Wed, 3 Apr 2024 11:49:29 +0800 Subject: [PATCH 05/54] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index df8cfd68..65ad47f9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -106,7 +106,7 @@ jobs: path: node_modules key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }} - name: tsc - run: npm lint:tsc + run: npm run lint:tsc - name: coverage run: npm test -- --coverage && bash <(curl -s https://codecov.io/bash) From 50ad29f7f88e1706ca5a083653717038ad14ea6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Wed, 3 Apr 2024 11:49:55 +0800 Subject: [PATCH 06/54] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index df8cfd68..65ad47f9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -106,7 +106,7 @@ jobs: path: node_modules key: node_modules-${{ hashFiles('**/package-temp-dir/package-lock.json') }} - name: tsc - run: npm lint:tsc + run: npm run lint:tsc - name: coverage run: npm test -- --coverage && bash <(curl -s https://codecov.io/bash) From db85cbcdf216bede477bc9742cf24d0f052126e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Wed, 3 Apr 2024 16:14:31 +0800 Subject: [PATCH 07/54] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/treeUtil.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/treeUtil.ts b/src/utils/treeUtil.ts index d301f93f..e64fac88 100644 --- a/src/utils/treeUtil.ts +++ b/src/utils/treeUtil.ts @@ -50,12 +50,12 @@ export function toPathOptions( const val = (option as Record)[fieldNames.value]; return stringMode ? String(val) === String(valueCell) : val === valueCell; }); - const foundOption: DefaultOptionType = foundIndex !== -1 ? currentList?.[foundIndex] : {}; + const foundOption = foundIndex !== -1 ? currentList?.[foundIndex] : undefined; valueOptions.push({ value: (foundOption as Record)?.[fieldNames.value] ?? valueCell, index: foundIndex, - option: foundOption, + option: foundOption as DefaultOptionType, }); currentList = (foundOption as Record)?.[fieldNames.children]; From 658a6fff9cfa2e5296f5ed084532123585262a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Wed, 3 Apr 2024 16:59:53 +0800 Subject: [PATCH 08/54] feat: test --- package.json | 6 +++--- src/context.ts | 9 +-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 0df13db1..7ea7a59e 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@testing-library/react": "^12.1.5", "@types/classnames": "^2.2.6", "@types/enzyme": "^3.1.15", - "@types/jest": "^29.4.0", + "@types/jest": "^29.5.12", "@types/react": "^17.0.38", "@types/react-dom": "^18.0.11", "@types/warning": "^3.0.0", @@ -66,7 +66,7 @@ "enzyme-adapter-react-16": "^1.15.6", "enzyme-to-json": "^3.2.1", "eslint": "^8.54.0", - "eslint-plugin-jest": "^27.6.0", + "eslint-plugin-jest": "^27.9.0", "eslint-plugin-unicorn": "^50.0.1", "father": "^4.0.0", "gh-pages": "^6.1.1", @@ -75,7 +75,7 @@ "np": "^9.2.0", "prettier": "^3.1.0", "rc-field-form": "^1.44.0", - "rc-test": "^7.0.14", + "rc-test": "^7.0.15", "react": "^16.0.0", "react-dom": "^16.0.0", "typescript": "^5.4.3" diff --git a/src/context.ts b/src/context.ts index d795b358..30a6336b 100644 --- a/src/context.ts +++ b/src/context.ts @@ -24,13 +24,6 @@ export interface CascaderContextProps { optionRender?: CascaderProps['optionRender']; } -const CascaderContext = React.createContext({ - options: [], - fieldNames: { key: '', label: '', value: '', children: '' }, - values: [], - halfValues: [], - onSelect: () => undefined, - searchOptions: [], -}); +const CascaderContext = React.createContext({} as CascaderContextProps); export default CascaderContext; From e2f36b7549ceecc596031a86e99cbe20ff2da44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Wed, 3 Apr 2024 17:09:35 +0800 Subject: [PATCH 09/54] feat: test --- src/OptionList/List.tsx | 2 +- src/context.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OptionList/List.tsx b/src/OptionList/List.tsx index 7cd47742..4d5bcb2b 100644 --- a/src/OptionList/List.tsx +++ b/src/OptionList/List.tsx @@ -31,7 +31,7 @@ const RawOptionList = React.forwardRef(( const rtl = direction === 'rtl'; const { - options = [], + options, values, halfValues, fieldNames, diff --git a/src/context.ts b/src/context.ts index 30a6336b..85e3b6a3 100644 --- a/src/context.ts +++ b/src/context.ts @@ -7,7 +7,7 @@ import type { } from './Cascader'; export interface CascaderContextProps { - options: CascaderProps['options']; + options: NonNullable; fieldNames: InternalFieldNames; values: SingleValueType[]; halfValues: SingleValueType[]; From 1ace0bbcbec4a6d80ee6decb988f0e2ed2176605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B6=E6=9E=AB?= <645381995@qq.com> Date: Mon, 8 Apr 2024 15:45:38 +0800 Subject: [PATCH 10/54] =?UTF-8?q?feat:=20=E7=A7=BB=E9=99=A4=20T?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/adjust-overflow.tsx | 2 +- examples/animation.tsx | 2 +- examples/change-on-select.tsx | 2 +- examples/custom-arrow-icon.tsx | 13 +++---- examples/custom-field-name.tsx | 5 +-- examples/default-expand-single-option.tsx | 8 ++--- examples/defaultValue.tsx | 2 +- examples/dropdown-render.tsx | 2 +- examples/dynamic-options.tsx | 4 +-- examples/hover.tsx | 2 +- examples/multiple.tsx | 12 +++---- examples/panel.tsx | 13 +++---- examples/rc-form.tsx | 2 +- examples/simple.tsx | 2 +- examples/text-trigger.tsx | 2 +- examples/value.tsx | 6 ++-- examples/visible.tsx | 6 ++-- src/Cascader.tsx | 44 ++++++++++++----------- src/OptionList/Column.tsx | 6 ++-- src/index.tsx | 1 + tests/fieldNames.spec.tsx | 2 +- tests/index.spec.tsx | 9 +++-- tests/loadData.spec.tsx | 2 +- 23 files changed, 73 insertions(+), 76 deletions(-) diff --git a/examples/adjust-overflow.tsx b/examples/adjust-overflow.tsx index 5da69fa2..eb6f4647 100644 --- a/examples/adjust-overflow.tsx +++ b/examples/adjust-overflow.tsx @@ -62,7 +62,7 @@ const addressOptions = [ const MyCascader = ({ builtinPlacements }: { builtinPlacements?: BuildInPlacements }) => { const [inputValue, setInputValue] = useState(''); - const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/animation.tsx b/examples/animation.tsx index 5eaab0d7..11aab75d 100644 --- a/examples/animation.tsx +++ b/examples/animation.tsx @@ -60,7 +60,7 @@ const addressOptions = [ const Demo = () => { const [inputValue, setInputValue] = useState(''); - const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; diff --git a/examples/change-on-select.tsx b/examples/change-on-select.tsx index 6d615424..61435f8f 100644 --- a/examples/change-on-select.tsx +++ b/examples/change-on-select.tsx @@ -63,7 +63,7 @@ const options = [ }, ]; -const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { +const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); }; diff --git a/examples/custom-arrow-icon.tsx b/examples/custom-arrow-icon.tsx index 587bc21d..9f3f7f81 100644 --- a/examples/custom-arrow-icon.tsx +++ b/examples/custom-arrow-icon.tsx @@ -4,7 +4,7 @@ import type { CascaderProps, SingleCascaderProps } from '../src'; import Cascader from '../src'; import type { Option2 } from './utils'; -const addressOptions: CascaderProps['options'] = [ +const addressOptions: CascaderProps['options'] = [ { label: '福建', value: 'fj', @@ -85,7 +85,7 @@ const loadingPath = const Demo = () => { const [inputValue, setInputValue] = useState(''); const [dynamicInputValue, setDynamicInputValue] = useState(''); - const [options, setOptions] = useState['options']>([ + const [options, setOptions] = useState['options']>([ { label: '福建', isLeaf: false, @@ -98,15 +98,12 @@ const Demo = () => { }, ]); - const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { + const onChange: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setInputValue(selectedOptions.map(o => o.label).join(', ')); }; - const onChangeDynamic: SingleCascaderProps['onChange'] = ( - value, - selectedOptions, - ) => { + const onChangeDynamic: SingleCascaderProps['onChange'] = (value, selectedOptions) => { console.log(value, selectedOptions); setDynamicInputValue(selectedOptions.map(o => o.label).join(', ')); }; @@ -151,7 +148,7 @@ const Demo = () => {
); - const loadData: SingleCascaderProps['loadData'] = selectedOptions => { + const loadData: SingleCascaderProps['loadData'] = selectedOptions => { const targetOption = selectedOptions[selectedOptions.length - 1]; targetOption.loading = true; // 动态加载下级数据 diff --git a/examples/custom-field-name.tsx b/examples/custom-field-name.tsx index ab127e74..9876adc5 100644 --- a/examples/custom-field-name.tsx +++ b/examples/custom-field-name.tsx @@ -61,10 +61,7 @@ const addressOptions: Option[] = [ const Demo = () => { const [inputValue, setInputValue] = useState(''); - const onChange: SingleCascaderProps['onChange'] = ( - value, - selectedOptions, - ) => { + const onChange: SingleCascaderProps