mirror of
https://github.com/element-plus/element-plus.git
synced 2025-03-07 15:47:57 +08:00
feat(components): [transfer] add custom empty slot for transfer panels (#18929)
* feat(components): [transfer] add custom empty slot for transfer panels * docs: [transfer] add description and example * test(components): add transfer component test * docs: add version tag * Update docs/en-US/component/transfer.md Co-authored-by: btea <2356281422@qq.com> --------- Co-authored-by: qiang <qw13131wang@gmail.com> Co-authored-by: btea <2356281422@qq.com>
This commit is contained in:
parent
e28e0f303e
commit
349a2e9c16
@ -33,6 +33,16 @@ transfer/customizable
|
||||
|
||||
:::
|
||||
|
||||
## Custom empty content ^(2.9.0)
|
||||
|
||||
You can customize the content when the list is empty or when no filtering results are found.
|
||||
|
||||
:::demo Use `left-empty` and `right-empty` slots to customize the empty content for each panel.
|
||||
|
||||
transfer/empty-content
|
||||
|
||||
:::
|
||||
|
||||
## Prop aliases
|
||||
|
||||
By default, Transfer looks for `key`, `label` and `disabled` in a data item. If your data items have different key names, you can use the `props` attribute to define aliases.
|
||||
@ -74,11 +84,13 @@ transfer/prop-alias
|
||||
|
||||
### Transfer Slots
|
||||
|
||||
| Name | Description |
|
||||
| ------------ | ------------------------------------------------------------------ |
|
||||
| default | Custom content for data items. The scope parameter is `{ option }` |
|
||||
| left-footer | content of left list footer |
|
||||
| right-footer | content of right list footer |
|
||||
| Name | Description |
|
||||
| -------------------- | -------------------------------------------------------------------- |
|
||||
| default | Custom content for data items. The scope parameter is `{ option }` |
|
||||
| left-footer | content of left list footer |
|
||||
| right-footer | content of right list footer |
|
||||
| left-empty ^(2.9.0) | content when left panel is empty or when no data matches the filter |
|
||||
| right-empty ^(2.9.0) | content when right panel is empty or when no data matches the filter |
|
||||
|
||||
### Transfer Exposes
|
||||
|
||||
|
33
docs/examples/transfer/empty-content.vue
Normal file
33
docs/examples/transfer/empty-content.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<el-transfer v-model="value" :data="data">
|
||||
<template #left-empty>
|
||||
<el-empty :image-size="60" description="No data" />
|
||||
</template>
|
||||
<template #right-empty>
|
||||
<el-empty :image-size="60" description="No data" />
|
||||
</template>
|
||||
</el-transfer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
interface DataItem {
|
||||
key: number
|
||||
label: string
|
||||
disabled: boolean
|
||||
}
|
||||
const generateData = (): DataItem[] => {
|
||||
const data: DataItem[] = []
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
data.push({
|
||||
key: i,
|
||||
label: `Option ${i}`,
|
||||
disabled: i % 4 === 0,
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
const data = ref(generateData())
|
||||
const value = ref([])
|
||||
</script>
|
@ -332,4 +332,52 @@ describe('Transfer', () => {
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('empty slots', () => {
|
||||
it('render left-empty and right-empty slots', () => {
|
||||
const wrapper = mount(() => (
|
||||
<Transfer
|
||||
data={[]}
|
||||
v-slots={{
|
||||
'left-empty': () => <span>No data</span>,
|
||||
'right-empty': () => <span>No data</span>,
|
||||
}}
|
||||
/>
|
||||
))
|
||||
|
||||
const panels = wrapper.findAll('.el-transfer-panel__empty')
|
||||
expect(panels).toHaveLength(2)
|
||||
expect(panels[0].text()).toBe('No data')
|
||||
expect(panels[1].text()).toBe('No data')
|
||||
})
|
||||
|
||||
it('render default empty content when slots not provided', () => {
|
||||
const wrapper = mount(() => <Transfer data={[]} />)
|
||||
|
||||
const panels = wrapper.findAll('.el-transfer-panel__empty')
|
||||
expect(panels).toHaveLength(2)
|
||||
expect(panels[0].text()).toBe('No data')
|
||||
expect(panels[1].text()).toBe('No data')
|
||||
})
|
||||
|
||||
it('show no match content when filtering', async () => {
|
||||
const wrapper = mount(() => (
|
||||
<Transfer
|
||||
data={getTestData()}
|
||||
filterable={true}
|
||||
v-slots={{
|
||||
'left-empty': () => <span>No data</span>,
|
||||
}}
|
||||
/>
|
||||
))
|
||||
|
||||
const leftPanel: any = wrapper.findComponent({ name: 'ElTransferPanel' })
|
||||
leftPanel.vm.query = 'non-existing-data'
|
||||
await nextTick()
|
||||
|
||||
const emptyContent = wrapper.find('.el-transfer-panel__empty')
|
||||
expect(emptyContent.exists()).toBe(true)
|
||||
expect(emptyContent.text()).toBe('No data')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -40,9 +40,14 @@
|
||||
<option-content :option="optionRender?.(item)" />
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<p v-show="hasNoMatch || isEmpty(data)" :class="ns.be('panel', 'empty')">
|
||||
{{ hasNoMatch ? t('el.transfer.noMatch') : t('el.transfer.noData') }}
|
||||
</p>
|
||||
<div
|
||||
v-show="hasNoMatch || isEmpty(data)"
|
||||
:class="ns.be('panel', 'empty')"
|
||||
>
|
||||
<slot name="empty">
|
||||
{{ hasNoMatch ? t('el.transfer.noMatch') : t('el.transfer.noData') }}
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="hasFooter" :class="ns.be('panel', 'footer')">
|
||||
<slot />
|
||||
|
@ -13,6 +13,9 @@
|
||||
:props="props.props"
|
||||
@checked-change="onSourceCheckedChange"
|
||||
>
|
||||
<template #empty>
|
||||
<slot name="left-empty" />
|
||||
</template>
|
||||
<slot name="left-footer" />
|
||||
</transfer-panel>
|
||||
<div :class="ns.e('buttons')">
|
||||
@ -48,6 +51,9 @@
|
||||
:props="props.props"
|
||||
@checked-change="onTargetCheckedChange"
|
||||
>
|
||||
<template #empty>
|
||||
<slot name="right-empty" />
|
||||
</template>
|
||||
<slot name="right-footer" />
|
||||
</transfer-panel>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user