Skip to content

Commit

Permalink
Merge pull request #951 from Tencent/fix/select-context
Browse files Browse the repository at this point in the history
fix(Select): onChange callback param
  • Loading branch information
honkinglin authored Jun 24, 2022
2 parents a5ed6f7 + a18cd31 commit 025a05d
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 92 deletions.
69 changes: 33 additions & 36 deletions src/select/__tests__/__snapshots__/select.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -185,52 +185,48 @@ exports[`creatable.jsx 1`] = `
exports[`custom-options.jsx 1`] = `
<DocumentFragment>
<div
style="display: flex;"
class="t-select__wrap"
style="width: 80%;"
>
<div
class="t-select__wrap"
style="width: 40%;"
class="t-popup__reference t-select t-select-input"
>
<div
class="t-popup__reference t-select t-select-input"
class="t-input__wrap"
value="用户一"
>
<div
class="t-input__wrap"
value="Apple"
class="t-input t-is-readonly t-size-m t-align-left t-is-default t-input--prefix t-input--suffix"
>
<div
class="t-input t-is-readonly t-size-m t-align-left t-is-default t-input--prefix t-input--suffix"
class="t-input__prefix"
/>
<input
class="t-input__inner"
placeholder="请选择"
readonly=""
type="text"
value="用户一"
/>
<span
class="t-input__suffix t-input__suffix-icon"
>
<div
class="t-input__prefix"
/>
<input
class="t-input__inner"
placeholder="请选择"
readonly=""
type="text"
value="Apple"
/>
<span
class="t-input__suffix t-input__suffix-icon"
<svg
class="t-fake-arrow t-select__right-icon"
fill="none"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<svg
class="t-fake-arrow t-select__right-icon"
fill="none"
height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M3.75 5.7998L7.99274 10.0425L12.2361 5.79921"
stroke="black"
stroke-opacity="0.9"
stroke-width="1.3"
/>
</svg>
</span>
</div>
<path
d="M3.75 5.7998L7.99274 10.0425L12.2361 5.79921"
stroke="black"
stroke-opacity="0.9"
stroke-width="1.3"
/>
</svg>
</span>
</div>
</div>
</div>
Expand Down Expand Up @@ -1201,6 +1197,7 @@ exports[`prefix.jsx 1`] = `
class="t-icon t-icon-browse"
fill="none"
height="1em"
style="margin-right: 8px;"
viewBox="0 0 16 16"
width="1em"
>
Expand Down
56 changes: 43 additions & 13 deletions src/select/_example/custom-options.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,52 @@ import { Select } from 'tdesign-react';

const { Option } = Select;

const CustomOptions = () => {
const [value, setValue] = useState('apple');
const options = [
{ label: '用户一', value: '1', description: '这是一段用户描述信息,可自定义内容' },
{ label: '用户二', value: '2', description: '这是一段用户描述信息,可自定义内容' },
{ label: '用户三', value: '3', description: '这是一段用户描述信息,可自定义内容' },
{ label: '用户四', value: '4', description: '这是一段用户描述信息,可自定义内容' },
{ label: '用户五', value: '5', description: '这是一段用户描述信息,可自定义内容' },
{ label: '用户六', value: '6', description: '这是一段用户描述信息,可自定义内容' },
{ label: '用户七', value: '7', description: '这是一段用户描述信息,可自定义内容' },
{ label: '用户八', value: '8', description: '这是一段用户描述信息,可自定义内容' },
{ label: '用户九', value: '9', description: '这是一段用户描述信息,可自定义内容' },
];

const avatarUrl = 'https://tdesign.gtimg.com/site/avatar.jpg';

export default function CustomOptions() {
const [value, setValue] = useState('1');
const onChange = (value) => {
setValue(value);
};

return (
<div style={{ display: 'flex' }}>
<Select value={value} onChange={onChange} style={{ width: '40%' }} clearable>
<Option key="apple" label="Apple" value="apple" />
<Option key="orange" value="orange" label="orange">
this is a orange option
<Select value={value} onChange={onChange} style={{ width: '80%' }} clearable>
{options.map((option, idx) => (
<Option style={{ height: '60px' }} key={idx} value={option.value} label={option.label}>
<div style={{ display: 'flex' }}>
<img
src={avatarUrl}
style={{
maxWidth: '40px',
borderRadius: '50%',
}}
/>
<div style={{ marginLeft: '16px' }}>
<div>{option.label}</div>
<div
style={{
fontSize: '13px',
color: 'var(--td-gray-color-9)',
}}
>
{option.description}
</div>
</div>
</div>
</Option>
<Option key="banana" label="Banana" value="banana" />
</Select>
</div>
))}
</Select>
);
};

export default CustomOptions;
}
16 changes: 12 additions & 4 deletions src/select/_example/panel.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Select, Textarea, Button, Input } from 'tdesign-react';
import { Select, Textarea, Button, Input, Divider } from 'tdesign-react';

const OPTIONS = [
{ label: '架构云', value: '1' },
Expand Down Expand Up @@ -49,9 +49,17 @@ export default function PanelExample() {
panelBottomContent={
<div className="select-panel-footer">
{editOrCreate === 'edit' ? (
<Button theme="primary" variant="text" onClick={() => toggleEditOrCreate('create')}>
新增选项
</Button>
<div
style={{
borderTop: '1px solid var(--td-border-level-2-color)',
marginTop: '8px',
padding: '8px 0',
}}
>
<Button theme="primary" variant="text" onClick={() => toggleEditOrCreate('create')}>
新增选项
</Button>
</div>
) : (
<div>
<Input autoFocus value={inputVal} onChange={(v) => changeInputVal(v)}></Input>
Expand Down
7 changes: 6 additions & 1 deletion src/select/_example/prefix.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ const SelectPrefix = () => {
setValue(value);
};
return (
<Select value={value} onChange={onChange} style={{ width: '40%' }} prefixIcon={<BrowseIcon />}>
<Select
value={value}
onChange={onChange}
style={{ width: '40%' }}
prefixIcon={<BrowseIcon style={{ marginRight: '8px' }} />}
>
<Option key="apple" label="Apple" value="apple" />
<Option key="orange" label="Orange" value="orange" disabled />
<Option key="banana" label="Banana" value="banana" />
Expand Down
18 changes: 13 additions & 5 deletions src/select/base/PopupContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { Children, Ref, forwardRef, isValidElement, cloneElement } from '
import classNames from 'classnames';
import { useLocaleReceiver } from '../../locale/LocalReceiver';
import { getSelectValueArr } from '../util/helper';
import { TdSelectProps, SelectValue, TdOptionProps } from '../type';
import { TdSelectProps, SelectValue, TdOptionProps, SelectValueChangeTrigger } from '../type';
import useConfig from '../../_util/useConfig';
import Option, { SelectOptionProps } from './Option';

Expand All @@ -24,7 +24,15 @@ interface SelectPopupProps
| 'panelTopContent'
| 'panelBottomContent'
> {
onChange?: (value: SelectValue, context?: { label?: string | number; restData?: Record<string, any> }) => void;
onChange?: (
value: SelectValue,
context?: {
label?: string | number;
restData?: Record<string, any>;
e: React.MouseEvent;
trigger: SelectValueChangeTrigger;
},
) => void;
/**
* 是否展示popup
*/
Expand Down Expand Up @@ -63,7 +71,7 @@ const PopupContent = forwardRef((props: SelectPopupProps, ref: Ref<HTMLDivElemen
const { classPrefix } = useConfig();
if (!children && !props.options) return null;

const onSelect: SelectOptionProps['onSelect'] = (selectedValue, { label, selected, restData }) => {
const onSelect: SelectOptionProps['onSelect'] = (selectedValue, { label, selected, event, restData }) => {
const isValObj = valueType === 'object';
let objVal = {};
if (isValObj) {
Expand All @@ -82,12 +90,12 @@ const PopupContent = forwardRef((props: SelectPopupProps, ref: Ref<HTMLDivElemen
if (multiple) {
// calc multiple select values
const values = getSelectValueArr(value, selectedValue, selected, valueType, keys, objVal);
onChange(values, { label });
onChange(values, { label, e: event, trigger: 'check' });
} else {
// calc single select value
const selectVal = valueType === 'object' ? objVal : selectedValue;

onChange(selectVal, { label });
onChange(selectVal, { label, e: event, trigger: 'check' });
setShowPopup(!showPopup);
}
};
Expand Down
46 changes: 24 additions & 22 deletions src/select/base/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useState, useEffect, Ref, useMemo, useCallback, useRef } from 'r
import classNames from 'classnames';
import isFunction from 'lodash/isFunction';
import get from 'lodash/get';
import Tag from '../../tag';
import useControlled from '../../hooks/useControlled';
import { useLocaleReceiver } from '../../locale/LocalReceiver';
import useConfig from '../../_util/useConfig';
Expand All @@ -13,11 +12,12 @@ import noop from '../../_util/noop';
import FakeArrow from '../../common/FakeArrow';
import Loading from '../../loading';
import SelectInput from '../../select-input';
import Option, { SelectOptionProps } from './Option';
import Option from './Option';
import OptionGroup from './OptionGroup';
import PopupContent from './PopupContent';
import Tag from '../../tag';

import { TdSelectProps, TdOptionProps, SelectOption } from '../type';
import { TdSelectProps, TdOptionProps, SelectOption, SelectValueChangeTrigger } from '../type';
import { StyledProps } from '../../common';
import { selectDefaultProps } from '../defaultProps';

Expand Down Expand Up @@ -97,13 +97,6 @@ const Select = forwardRefWithStatics(
const [valueToOption, setValueToOption] = useState({});
const [selectedOptions, setSelectedOptions] = useState([]);

const selectedLabel = useMemo(() => {
if (multiple) {
return selectedOptions.map((selectedOption) => get(selectedOption || {}, keys?.label || 'label') || '');
}
return get(selectedOptions[0] || {}, keys?.label || 'label') || '';
}, [selectedOptions, keys, multiple]);

// 处理设置option的逻辑
useEffect(() => {
if (keys) {
Expand All @@ -123,7 +116,6 @@ const Select = forwardRefWithStatics(
}, [options, keys, children]);

// 同步value对应的options
// 没太看明白effect的必要,感觉是一个useMemo而已
useEffect(() => {
setSelectedOptions((oldSelectedOptions) => {
const valueKey = keys?.value || 'value';
Expand Down Expand Up @@ -161,19 +153,25 @@ const Select = forwardRefWithStatics(
});
}, [value, keys, valueType, valueToOption]);

const selectedLabel = useMemo(() => {
if (multiple) {
return selectedOptions.map((selectedOption) => get(selectedOption || {}, keys?.label || 'label') || '');
}
return get(selectedOptions[0] || {}, keys?.label || 'label') || '';
}, [selectedOptions, keys, multiple]);

const handleShowPopup = (visible: boolean) => {
if (disabled) return;
setShowPopup(visible);
onVisibleChange?.(visible);
visible && onInputChange('');
};

// 可以根据触发来源,自由定制标签变化时的筛选器行为
const onTagChange = (currentTags, context) => {
const { trigger, index, item, e: event } = context;
const { trigger, index, item, e } = context;
// backspace
if (trigger === 'backspace') {
event.stopPropagation();
e.stopPropagation();

let closest = -1;
let len = index;
Expand All @@ -188,35 +186,39 @@ const Select = forwardRefWithStatics(
return;
}
const values = getSelectValueArr(value, value[closest], true, valueType, keys);
onChange(values, context);
onChange(values, { e, trigger });
return;
}

if (trigger === 'clear') {
event.stopPropagation();
onChange([], context);
e.stopPropagation();
onChange([], { e, trigger });
return;
}

if (trigger === 'tag-remove') {
event.stopPropagation();
e.stopPropagation();
const values = getSelectValueArr(value, value[index], true, valueType, keys);
onChange(values, context);
onChange(values, { e, trigger });
if (isFunction(onRemove)) {
onRemove({
value: value[index],
data: {
label: item,
value: value[index],
},
e: event as React.MouseEvent<HTMLDivElement, MouseEvent>,
e,
});
}
}
};

// 选中 Popup 某项
const handleChange: SelectOptionProps['onSelect'] = (value) => {
const handleChange = (
value: string | number,
context: { e: React.MouseEvent; trigger: SelectValueChangeTrigger },
) => {
const { e, trigger } = context;
if (multiple) {
!reserveKeyword && onInputChange('', { trigger: 'clear' });
}
Expand All @@ -225,7 +227,7 @@ const Select = forwardRefWithStatics(
onCreate(value);
}
}
onChange?.(value, null);
onChange?.(value, { e, trigger });
};

// 处理filter逻辑
Expand Down
7 changes: 4 additions & 3 deletions src/select/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ popupVisible | Boolean | undefined | 是否显示下拉框 | N
prefixIcon | TElement | - | 组件前置图标。TS 类型:`TNode`[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
readonly | Boolean | false | 只读状态,值为真会隐藏输入框,且无法打开下拉框 | N
reserveKeyword | Boolean | false | 多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词 | N
selectInputProps | Object | - | 【开发中】透传 SelectInput 筛选器输入框组件的全部属性。TS 类型:`SelectInputProps`[SelectInput API Documents](./select-input?tab=api)[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/select/type.ts) | N
scroll | Object | - | 【开发中】懒加载和虚拟滚动。为保证组件收益最大化,当数据量小于阈值 `scroll.threshold` 时,无论虚拟滚动的配置是否存在,组件内部都不会开启虚拟滚动,`scroll.threshold` 默认为 `100`。TS 类型:`TScroll`[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
selectInputProps | Object | - | 透传 SelectInput 筛选器输入框组件的全部属性。TS 类型:`SelectInputProps`[SelectInput API Documents](./select-input?tab=api)[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/select/type.ts) | N
showArrow | Boolean | true | 是否显示右侧箭头,默认显示 | N
size | String | medium | 组件尺寸。可选项:small/medium/large。TS 类型:`SizeEnum`[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
tagInputProps | Object | - | 【开发中】透传 TagInput 标签输入框组件的全部属性。TS 类型:`TagInputProps`[TagInput API Documents](./tag-input?tab=api)[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/select/type.ts) | N
tagProps | Object | - | 【开发中】透传 Tag 标签组件全部属性。TS 类型:`TagProps`[Tag API Documents](./tag?tab=api)[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/select/type.ts) | N
tagInputProps | Object | - | 透传 TagInput 标签输入框组件的全部属性。TS 类型:`TagInputProps`[TagInput API Documents](./tag-input?tab=api)[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/select/type.ts) | N
tagProps | Object | - | 透传 Tag 标签组件全部属性。TS 类型:`TagProps`[Tag API Documents](./tag?tab=api)[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/select/type.ts) | N
value | String / Number / Object / Array | - | 选中值。TS 类型:`SelectValue` `type SelectValue<T extends SelectOption = SelectOption> = string | number | T | Array<SelectValue<T>>`[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/select/type.ts) | N
defaultValue | String / Number / Object / Array | - | 选中值。非受控属性。TS 类型:`SelectValue` `type SelectValue<T extends SelectOption = SelectOption> = string | number | T | Array<SelectValue<T>>`[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/select/type.ts) | N
valueDisplay | TNode | - | 自定义选中项呈现方式。TS 类型:`string | TNode<{ value: SelectValue; onClose: (index: number, item?: any) => void }>`[通用类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/common.ts) | N
Expand Down
Loading

0 comments on commit 025a05d

Please sign in to comment.