diff --git a/.prettierrc b/.prettierrc index f307fb19..7f12e041 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,5 +4,6 @@ "singleQuote": true, "tabWidth": 2, "trailingComma": "all", - "printWidth": 100 + "printWidth": 100, + "arrowParens": "avoid" } diff --git a/examples/debug.tsx b/examples/debug.tsx index 3163b2f5..cea95c85 100644 --- a/examples/debug.tsx +++ b/examples/debug.tsx @@ -25,6 +25,17 @@ class Demo extends React.Component { allowClear treeDefaultExpandAll onChange={this.onChange} + labelRender={entity => { + let current = entity; + const nodes = []; + + while (current) { + nodes.unshift(current.data.title); + current = current.parent; + } + + return nodes.join('>'); + }} > @@ -32,11 +43,7 @@ class Demo extends React.Component { - sss} - key="random3" - /> + sss} key="random3" /> diff --git a/src/generate.tsx b/src/generate.tsx index b3119c8c..08bafa69 100644 --- a/src/generate.tsx +++ b/src/generate.tsx @@ -21,6 +21,7 @@ import type { ChangeEventExtra, LegacyDataNode, SelectSource, + FlattenDataNode, } from './interface'; import { flattenOptions, @@ -130,6 +131,9 @@ export interface TreeSelectProps // Legacy /** `searchPlaceholder` has been removed since search box has been merged into input box */ searchPlaceholder?: React.ReactNode; + + /** @private This is not standard API since we only used in `rc-cascader`. Do not use in your production */ + labelRender?: (entity: FlattenDataNode) => React.ReactNode; } export default function generate(config: { @@ -153,7 +157,7 @@ export default function generate(config: { findValueOption, omitDOMProps: (props: object) => { const cloneProps = { ...props }; - OMIT_PROPS.forEach((prop) => { + OMIT_PROPS.forEach(prop => { delete cloneProps[prop]; }); return cloneProps; @@ -195,6 +199,7 @@ export default function generate(config: { onDropdownVisibleChange, onSelect, onDeselect, + labelRender, } = props; const mergedCheckable: React.ReactNode | boolean = treeCheckable || treeCheckStrictly; const mergedMultiple = multiple || mergedCheckable; @@ -211,7 +216,13 @@ export default function generate(config: { return node.label || node.title; }; - const getTreeNodeLabelProp = (node: DataNode): React.ReactNode => { + const getTreeNodeLabelProp = (entity: FlattenDataNode): React.ReactNode => { + const node = entity.data; + + if (labelRender) { + return labelRender(entity); + } + if (treeNodeLabelProp) { return node[treeNodeLabelProp]; } @@ -259,7 +270,7 @@ export default function generate(config: { const existRawValues = []; // Keep missing value in the cache - newRawValues.forEach((val) => { + newRawValues.forEach(val => { if (getEntityByValue(val)) { existRawValues.push(val); } else { @@ -274,7 +285,7 @@ export default function generate(config: { const valueHalfCheckedKeys: RawValueType[] = []; const newRawValues: RawValueType[] = []; - toArray(value).forEach((item) => { + toArray(value).forEach(item => { if (item && typeof item === 'object' && 'value' in item) { if (item.halfChecked && treeCheckStrictly) { const entity = getEntityByValue(item.value); @@ -290,11 +301,11 @@ export default function generate(config: { // We need do conduction of values if (treeConduction) { const { missingRawValues, existRawValues } = splitRawValues(newRawValues); - const keyList = existRawValues.map((val) => getEntityByValue(val).key); + const keyList = existRawValues.map(val => getEntityByValue(val).key); const { checkedKeys, halfCheckedKeys } = conductCheck(keyList, true, conductKeyEntities); return [ - [...missingRawValues, ...checkedKeys.map((key) => getEntityByKey(key).data.value)], + [...missingRawValues, ...checkedKeys.map(key => getEntityByKey(key).data.value)], halfCheckedKeys, ]; } @@ -319,7 +330,7 @@ export default function generate(config: { if (onChange) { let eventValues: RawValueType[] = newRawValues; if (treeConduction && showCheckedStrategy !== 'SHOW_ALL') { - const keyList = newRawValues.map((val) => { + const keyList = newRawValues.map(val => { const entity = getEntityByValue(val); return entity ? entity.key : val; }); @@ -329,7 +340,7 @@ export default function generate(config: { conductKeyEntities, ); - eventValues = formattedKeyList.map((key) => { + eventValues = formattedKeyList.map(key => { const entity = getEntityByKey(key); return entity ? entity.data.value : key; }); @@ -347,11 +358,11 @@ export default function generate(config: { // We need fill half check back if (treeCheckStrictly) { const halfValues = rawHalfCheckedKeys - .map((key) => { + .map(key => { const entity = getEntityByKey(key); return entity ? entity.data.value : key; }) - .filter((val) => !eventValues.includes(val)); + .filter(val => !eventValues.includes(val)); returnValues = [ ...(returnValues as LabelValueType[]), @@ -391,9 +402,9 @@ export default function generate(config: { mergedMultiple ? returnValues : returnValues[0], mergedLabelInValue ? null - : eventValues.map((val) => { + : eventValues.map(val => { const entity = getEntityByValue(val); - return entity ? getTreeNodeLabelProp(entity.data) : null; + return entity ? getTreeNodeLabelProp(entity) : null; }), additionalInfo, ); @@ -417,11 +428,11 @@ export default function generate(config: { if (treeConduction) { // Should keep missing values const { missingRawValues, existRawValues } = splitRawValues(newRawValues); - const keyList = existRawValues.map((val) => getEntityByValue(val).key); + const keyList = existRawValues.map(val => getEntityByValue(val).key); const { checkedKeys } = conductCheck(keyList, true, conductKeyEntities); newRawValues = [ ...missingRawValues, - ...checkedKeys.map((key) => getEntityByKey(key).data.value), + ...checkedKeys.map(key => getEntityByKey(key).data.value), ]; } @@ -445,7 +456,7 @@ export default function generate(config: { // Remove keys if tree conduction if (treeConduction) { const { missingRawValues, existRawValues } = splitRawValues(newRawValues); - const keyList = existRawValues.map((val) => getEntityByValue(val).key); + const keyList = existRawValues.map(val => getEntityByValue(val).key); const { checkedKeys } = conductCheck( keyList, { checked: false, halfCheckedKeys: rawHalfCheckedKeys }, @@ -453,7 +464,7 @@ export default function generate(config: { ); newRawValues = [ ...missingRawValues, - ...checkedKeys.map((key) => getEntityByKey(key).data.value), + ...checkedKeys.map(key => getEntityByKey(key).data.value), ]; } diff --git a/src/hooks/useSelectValues.ts b/src/hooks/useSelectValues.ts index 6e991f8d..f63ea412 100644 --- a/src/hooks/useSelectValues.ts +++ b/src/hooks/useSelectValues.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import type { DefaultValueType } from 'rc-select/lib/interface/generator'; import type { DataEntity } from 'rc-tree/lib/interface'; -import type { RawValueType, FlattenDataNode, Key, LabelValueType, DataNode } from '../interface'; +import type { RawValueType, FlattenDataNode, Key, LabelValueType } from '../interface'; import type { SkipType } from './useKeyValueMapping'; import { getRawValueLabeled } from '../utils/valueUtil'; import type { CheckedStrategy } from '../utils/strategyUtil'; @@ -19,7 +19,7 @@ interface Config { skipType?: SkipType, ignoreDisabledCheck?: boolean, ) => FlattenDataNode; - getLabelProp: (node: DataNode) => React.ReactNode; + getLabelProp: (entity: FlattenDataNode) => React.ReactNode; } /** Return */ @@ -40,7 +40,7 @@ export default function useSelectValues( if (treeConduction) { const rawKeys = formatStrategyKeys( - rawValues.map((val) => { + rawValues.map(val => { const entity = getEntityByValue(val); return entity ? entity.key : val; }), @@ -48,7 +48,7 @@ export default function useSelectValues( conductKeyEntities, ); - mergedRawValues = rawKeys.map((key) => { + mergedRawValues = rawKeys.map(key => { const entity = getEntityByKey(key); return entity ? entity.data.value : key; }); diff --git a/src/utils/valueUtil.ts b/src/utils/valueUtil.ts index 773f7b80..6e6f54b3 100644 --- a/src/utils/valueUtil.ts +++ b/src/utils/valueUtil.ts @@ -25,12 +25,12 @@ export function toArray(value: T | T[]): T[] { export function findValueOption(values: RawValueType[], options: CompatibleDataNode[]): DataNode[] { const optionMap: Map = new Map(); - options.forEach((flattenItem) => { + options.forEach(flattenItem => { const { data } = flattenItem; optionMap.set(data.value, data); }); - return values.map((val) => fillLegacyProps(optionMap.get(val))); + return values.map(val => fillLegacyProps(optionMap.get(val))); } export function isValueDisabled(value: RawValueType, options: CompatibleDataNode[]): boolean { @@ -68,7 +68,7 @@ function getLevel({ parent }: FlattenNode): number { export function flattenOptions(options: DataNode[]): FlattenDataNode[] { // Add missing key function fillKey(list: DataNode[]): TreeDataNode[] { - return (list || []).map((node) => { + return (list || []).map(node => { const { value, key, children } = node; const clone = { @@ -88,7 +88,7 @@ export function flattenOptions(options: DataNode[]): FlattenDataNode[] { const cacheMap = new Map(); const flattenDateNodeList: (FlattenDataNode & { parentKey?: React.Key })[] = flattenList.map( - (node) => { + node => { const { data } = node; const { key } = data; @@ -106,7 +106,7 @@ export function flattenOptions(options: DataNode[]): FlattenDataNode[] { ); // Fill parent - flattenDateNodeList.forEach((flattenNode) => { + flattenDateNodeList.forEach(flattenNode => { // eslint-disable-next-line no-param-reassign flattenNode.parent = cacheMap.get(flattenNode.parentKey); }); @@ -147,7 +147,7 @@ export function filterOptions( function dig(list: DataNode[], keepAll: boolean = false) { return list - .map((dataNode) => { + .map(dataNode => { const { children } = dataNode; const match = keepAll || filterOptionFunc(searchValue, fillLegacyProps(dataNode)); @@ -161,7 +161,7 @@ export function filterOptions( } return null; }) - .filter((node) => node); + .filter(node => node); } return dig(options); @@ -175,20 +175,20 @@ export function getRawValueLabeled( skipType?: SkipType, ignoreDisabledCheck?: boolean, ) => FlattenDataNode, - getLabelProp: (node: DataNode) => React.ReactNode, + getLabelProp: (entity: FlattenDataNode) => React.ReactNode, ): LabelValueType[] { const valueMap = new Map(); - toArray(prevValue).forEach((item) => { + toArray(prevValue).forEach(item => { if (item && typeof item === 'object' && 'value' in item) { valueMap.set(item.value, item); } }); - return values.map((val) => { + return values.map(val => { const item: LabelValueType = { value: val }; const entity = getEntityByValue(val, 'select', true); - const label = entity ? getLabelProp(entity.data) : val; + const label = entity ? getLabelProp(entity) : val; if (valueMap.has(val)) { const labeledValue = valueMap.get(val); diff --git a/tests/Select.internal.spec.tsx b/tests/Select.internal.spec.tsx new file mode 100644 index 00000000..8bb3ba29 --- /dev/null +++ b/tests/Select.internal.spec.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import TreeSelect from '../src'; + +describe('TreeSelect.InternalAPI', () => { + it('labelRender', () => { + const wrapper = mount( + { + let current = entity; + const nodes = []; + + while (current) { + nodes.unshift(current.data.label); + current = current.parent; + } + + return nodes.join('>'); + }} + />, + ); + expect(wrapper.find('.rc-tree-select-selection-item').text()).toEqual('Bamboo>Light>Little'); + }); +});