Skip to content

Commit

Permalink
fix(pro:transfer): tree transfer with 'off' stategy target error (#1388)
Browse files Browse the repository at this point in the history
tree transfer with 'off' stategy shouldn't add parent node to target data when parent isn't selected
  • Loading branch information
sallerli1 committed Jan 6, 2023
1 parent 59fc80e commit 796bfaf
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 28 deletions.
92 changes: 90 additions & 2 deletions packages/pro/transfer/__tests__/proTransfer.spec.ts
Expand Up @@ -15,7 +15,7 @@ interface Data extends TreeTransferData<'children'> {
disabled?: boolean
}

const createData = (idx: number): Data => ({
const createData = (idx: number, includeDisabled = true): Data => ({
key: `${idx}`,
disabled: false,
label: `Selection-${idx}`,
Expand All @@ -37,7 +37,7 @@ const createData = (idx: number): Data => ({
},
{
key: `${idx}-2-2`,
disabled: true,
disabled: includeDisabled,
label: `Selection-${idx}-2-2`,
},
],
Expand All @@ -62,6 +62,7 @@ describe('ProTransfer', () => {
.flat()

const mockedTreeDataSource = Array.from(new Array(5)).map((_, idx) => createData(idx + 1))
const noneDisabledTreeDataSource = Array.from(new Array(5)).map((_, idx) => createData(idx + 1, false))

const ProTableTransferMount = (options?: MountingOptions<Partial<ProTransferProps>>) => {
const sourceColumns: TableColumn[] = [
Expand Down Expand Up @@ -277,6 +278,93 @@ describe('ProTransfer', () => {
expect(wrapper.findAll('.ix-tree')[1]).toBeUndefined()
})

test('tree cascaderStrategy off work', async () => {
const onChange = vi.fn()
const wrapper = proTreeTransferMount({
props: { dataSource: noneDisabledTreeDataSource, onChange, treeProps: { cascaderStrategy: 'off' } },
})

const [appendTrigger, removeTrigger] = wrapper.findComponent(TransferOperations).findAllComponents(IxButton)

const [sourceTree] = wrapper.findAll('.ix-tree')
const sourceNodes = sourceTree.findAll('.ix-tree-node')
await sourceNodes[2].find('input').setValue(true)
await sourceNodes[3].find('input').setValue(true)
await sourceNodes[4].find('input').setValue(true)
await appendTrigger.trigger('click')

const targetList = wrapper.findComponent(IxTransferList)

expect(targetList.findAll('.ix-transfer-list-item').length).toBe(3)
expect(onChange).toBeCalledWith(['1-2', '1-2-1', '1-2-2'], [])

await onChange.mockClear()

await targetList.findAll('.ix-transfer-list-item')[0].find('input').setValue(true)
await removeTrigger.trigger('click')

expect(targetList.findAll('.ix-transfer-list-item').length).toBe(2)
expect(onChange).toBeCalledWith(['1-2-1', '1-2-2'], ['1-2', '1-2-1', '1-2-2'])
})

test('tree cascaderStrategy child work', async () => {
const onChange = vi.fn()
const wrapper = proTreeTransferMount({
props: { dataSource: noneDisabledTreeDataSource, onChange, treeProps: { cascaderStrategy: 'child' } },
})

const [appendTrigger, removeTrigger] = wrapper.findComponent(TransferOperations).findAllComponents(IxButton)

const [sourceTree] = wrapper.findAll('.ix-tree')
const sourceNodes = sourceTree.findAll('.ix-tree-node')
await sourceNodes[2].find('input').setValue(true)
await sourceNodes[3].find('input').setValue(true)
await sourceNodes[4].find('input').setValue(true)
await appendTrigger.trigger('click')

const [, targetTree] = wrapper.findAll('.ix-tree')

expect(targetTree.findAll('.ix-tree-node').length).toBe(4)
expect(onChange).toBeCalledWith(['1-2-1', '1-2-2'], [])

await onChange.mockClear()

await targetTree.findAll('.ix-tree-node')[2].find('input').setValue(true)
await removeTrigger.trigger('click')

expect(targetTree.findAll('.ix-tree-node').length).toBe(3)
expect(onChange).toBeCalledWith(['1-2-2'], ['1-2-1', '1-2-2'])
})

test('tree cascaderStrategy parent work', async () => {
const onChange = vi.fn()
const wrapper = proTreeTransferMount({
props: { dataSource: noneDisabledTreeDataSource, onChange, treeProps: { cascaderStrategy: 'parent' } },
})

const [appendTrigger, removeTrigger] = wrapper.findComponent(TransferOperations).findAllComponents(IxButton)

const [sourceTree] = wrapper.findAll('.ix-tree')
const sourceNodes = sourceTree.findAll('.ix-tree-node')
await sourceNodes[2].find('input').setValue(true)
await sourceNodes[3].find('input').setValue(true)
await sourceNodes[4].find('input').setValue(true)
await appendTrigger.trigger('click')

const [, targetTree] = wrapper.findAll('.ix-tree')

expect(targetTree.findAll('.ix-tree-node').length).toBe(4)
expect(onChange).toBeCalledWith(['1-2'], [])

await onChange.mockClear()

await targetTree.findAll('.ix-tree-node')[2].find('input').setValue(true)
await removeTrigger.trigger('click')

expect(targetTree.findAll('.ix-tree-node').length).toBe(3)
expect(onChange).toBeCalledWith(['1-2-2'], ['1-2'])
})

test('flatTargetData work', async () => {
const wrapper = proTreeTransferMount({ props: { flatTargetData: true } })

Expand Down
60 changes: 34 additions & 26 deletions packages/pro/transfer/src/composables/useTreeDataStrategy.ts
Expand Up @@ -136,23 +136,36 @@ function createSeparateDataSourceFn<C extends VKey>(
}
})()

return (data, isSource) =>
filterTree(
data,
childrenKey.value,
(item, parent) => {
const filterRes = filterFn(item, parent, isSource)

// set targetDataKeySet to collect targetData count later
// we collect this during filter process to avoid unecessary traveral
if (!isSource && filterRes) {
targetDataKeySet.add(getKey(item))
}
return (data, isSource) => {
if (isSource || cascaderStrategy !== 'off') {
return filterTree(
data,
childrenKey.value,
(item, parent) => {
const filterRes = filterFn(item, parent, isSource)

// set targetDataKeySet to collect targetData count later
// we collect this during filter process to avoid unecessary traveral
if (!isSource && filterRes) {
targetDataKeySet.add(getKey(item))
}

return filterRes
},
'or',
)
}

return filterRes
},
'or',
)
const res: TreeTransferData<C>[] = []
traverseTree(data, childrenKey.value, (item, parent) => {
const key = getKey(item)
if (filterFn(item, parent, isSource) && !targetDataKeySet.has(key)) {
targetDataKeySet.add(key)
res.push({ ...item, [childrenKey.value]: undefined })
}
})
return res
}
}

return (data, _, selectedKeySet, getKey) => {
Expand Down Expand Up @@ -190,27 +203,22 @@ function createSeparateDataSourceFnWithFlatten<C extends VKey>(

return {
sourceData,
targetData: flattenTargetTree(targetData, childrenKey.value, cascaderStrategy, flatTargetData),
targetData:
cascaderStrategy === 'off' ? targetData : flattenTargetTree(targetData, childrenKey.value, flatTargetData),
}
}
}

function flattenTargetTree<C extends VKey>(
data: TreeTransferData<C>[],
childrenKey: C,
cascaderStrategy: CascaderStrategy,
flatTargetData: boolean | 'all',
) {
if (cascaderStrategy !== 'off' && !flatTargetData) {
if (!flatTargetData) {
return data
}

return flattenTree(
data,
childrenKey,
item => ({ ...item, children: undefined }),
flatTargetData !== 'all' && cascaderStrategy !== 'off',
)
return flattenTree(data, childrenKey, item => ({ ...item, children: undefined }), flatTargetData !== 'all')
}

function createStrategy<C extends VKey>(
Expand Down Expand Up @@ -337,7 +345,7 @@ function createParentStrategy<C extends VKey>(
const { sourceData, targetData } = separateDataSource(data, _, selectedKeySet, getKey)
return {
sourceData,
targetData: flattenTargetTree(targetData, childrenKey.value, 'parent', flatTargetData),
targetData: flattenTargetTree(targetData, childrenKey.value, flatTargetData),
}
},
append(keys, selectedKey, getKey) {
Expand Down

0 comments on commit 796bfaf

Please sign in to comment.