From 01bd890b73c8463e108e72a564b227f03760cee2 Mon Sep 17 00:00:00 2001 From: Kev1nzh <66104521+kev1nzh37@users.noreply.github.com> Date: Sun, 19 Sep 2021 14:12:27 +0800 Subject: [PATCH] feat(upload): add `abstract` prop, separate `upload-file-list` and `upload-trigger` components (#1193) * feat(upload): add `abstract` prop, separate `upload-file-list` and `upload-trigger` components (#1102) * docs(upload): add `abstract` props docs and CHANGELOG (#1102) * feat(upload): add upload tests (#1102) * feat(upload): fix `upload-trigger` and `upload-file-list` bug (#1102) * docs(upload): remove `upload-trigger` abstract prop (#1102) * feat(upload) fix upload-file-list method (#1102) * feat(upload): fix lint (#1102) Co-authored-by: kev1nzh Co-authored-by: 07akioni <07akioni2@gmail.com> Co-authored-by: kev1nzh_ark --- CHANGELOG.en-US.md | 1 + CHANGELOG.zh-CN.md | 1 + src/upload/demos/enUS/abstract.demo.md | 45 +++++ src/upload/demos/enUS/index.demo-entry.md | 8 + src/upload/demos/zhCN/abstract.demo.md | 45 +++++ src/upload/demos/zhCN/index.demo-entry.md | 8 + src/upload/index.ts | 2 + src/upload/src/Upload.tsx | 220 +++++++++------------- src/upload/src/UploadFileList.tsx | 66 +++++++ src/upload/src/UploadTrigger.tsx | 89 +++++++++ src/upload/src/interface.ts | 10 +- src/upload/src/styles/index.cssr.ts | 126 +++++++------ src/upload/tests/Upload.spec.ts | 160 +++++++++++++++- 13 files changed, 583 insertions(+), 198 deletions(-) create mode 100644 src/upload/demos/enUS/abstract.demo.md create mode 100644 src/upload/demos/zhCN/abstract.demo.md create mode 100644 src/upload/src/UploadFileList.tsx create mode 100644 src/upload/src/UploadTrigger.tsx diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 65ea3bdb6..8ab72d328 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -5,6 +5,7 @@ ### Breaking Changes - `n-layout-sider`'s `arrow-circle` trigger is changed into new style. +- `n-upload` add `abstract` prop, add `n-upload-trigger` 和 `n-upload-file-list` component, closes [#1102](https://github.com/TuSimple/naive-ui/issues/1102). ### Feats diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 60a85d9b8..4349ea3e0 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -5,6 +5,7 @@ ### Breaking Changes - `n-layout-sider` 的 `arrow-circle` 类型触发按钮采用了新样式 +- `n-upload` 新增 `abstract` 属性,新增 `n-upload-trigger` 和 `n-upload-file-list` 组件,关闭 [#1102](https://github.com/TuSimple/naive-ui/issues/1102) ### Feats diff --git a/src/upload/demos/enUS/abstract.demo.md b/src/upload/demos/enUS/abstract.demo.md new file mode 100644 index 000000000..fc614155a --- /dev/null +++ b/src/upload/demos/enUS/abstract.demo.md @@ -0,0 +1,45 @@ +# No Wrapper DOM + +`n-upload` set `abstract`. + +`n-upload-trigger` and `n-upload-file-list` need to be called from within `n-upload`. + +```html +
+ + + Eat + Sleep + + Upload + + + + + + + +
+``` + +```js +import { defineComponent, ref } from 'vue' +export default defineComponent({ + setup () { + const fileListRef = ref([ + { + id: 'b', + name: 'file.doc', + status: 'finished', + type: 'text/plain' + } + ]) + + return { fileList: fileListRef } + } +}) +``` diff --git a/src/upload/demos/enUS/index.demo-entry.md b/src/upload/demos/enUS/index.demo-entry.md index 95f883343..d28ebcdca 100644 --- a/src/upload/demos/enUS/index.demo-entry.md +++ b/src/upload/demos/enUS/index.demo-entry.md @@ -14,6 +14,7 @@ default-files before-upload image-style image-card-style +abstract ``` ## API @@ -22,6 +23,7 @@ image-card-style | Name | Type | Default | Description | | --- | --- | --- | --- | +| abstract | `boolean` | `false` | Whether or not DOM wrapping does not exist. | | accept | `string` | `undefined` | The accept type of upload. See accept. | | action | `string` | `undefined` | The URL to submit data to. | | create-thumbnail-url | `(file: File) => Promise` | `undefined` | Customize file thumbnails. | @@ -80,3 +82,9 @@ image-card-style | Name | Parameters | Description | | --- | --- | --- | | default | `()` | The content of the upload dragger, use can refer to [Drag to Upload](#drag). | + +### UploadTrigger Slots + +| 名称 | 参数 | 说明 | +| --- | --- | --- | +| default | `(options: { handleClick: () => void, handleDragOver: (e: DragEvent) => void, handleDragEnter: (e: DragEvent) => void, handleDragLeave: (e: DragEvent) => void, handleDrop: (e: DragEvent) => void})` | `handleClick` is the click upload function, `handleDrop` is the drag and drop upload function, `handleDragEnter`, `handleDragOver` and `handleDragLeave` are the drag and drop event functions. | diff --git a/src/upload/demos/zhCN/abstract.demo.md b/src/upload/demos/zhCN/abstract.demo.md new file mode 100644 index 000000000..9c7a6468e --- /dev/null +++ b/src/upload/demos/zhCN/abstract.demo.md @@ -0,0 +1,45 @@ +# 不需要包裹 DOM + +`n-upload` 设置 `abstract`。 + +`n-upload-trigger`和 `n-upload-file-list` 需在 `n-upload` 内调用。 + +```html +
+ + + Eat + Sleep + + Upload + + + + + + + +
+``` + +```js +import { defineComponent, ref } from 'vue' +export default defineComponent({ + setup () { + const fileListRef = ref([ + { + id: 'b', + name: 'file.doc', + status: 'finished', + type: 'text/plain' + } + ]) + + return { fileList: fileListRef } + } +}) +``` diff --git a/src/upload/demos/zhCN/index.demo-entry.md b/src/upload/demos/zhCN/index.demo-entry.md index b1ae3a58c..77d430684 100644 --- a/src/upload/demos/zhCN/index.demo-entry.md +++ b/src/upload/demos/zhCN/index.demo-entry.md @@ -14,6 +14,7 @@ default-files before-upload image-style image-card-style +abstract ``` ## API @@ -22,6 +23,7 @@ image-card-style | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | +| abstract | `boolean` | `false` | 是否不存在 DOM 包裹 | | accept | `string` | `undefined` | 接受的文件类型,参考 accept | | action | `string` | `undefined` | 请求提交的地址 | | create-thumbnail-url | `(file: File) => Promise` | `undefined` | 自定义文件缩略图 | @@ -79,3 +81,9 @@ image-card-style | 名称 | 参数 | 说明 | | ------- | ---- | --------------------------------------------- | | default | `()` | 上传拖动器的内容,使用可参考[拖拽上传](#drag) | + +### UploadTrigger Slots + +| 名称 | 参数 | 说明 | +| --- | --- | --- | +| default | `(options: { handleClick: () => void, handleDragOver: (e: DragEvent) => void, handleDragEnter: (e: DragEvent) => void, handleDragLeave: (e: DragEvent) => void, handleDrop: (e: DragEvent) => void})` | `handleClick` 为点击上传函数,`handleDrop` 为拖拽上传函数,`handleDragEnter`、`handleDragOver` 和 `handleDragLeave` 为拖拽事件函数 | diff --git a/src/upload/index.ts b/src/upload/index.ts index 79899aafd..dd12c9afb 100644 --- a/src/upload/index.ts +++ b/src/upload/index.ts @@ -1,5 +1,7 @@ export { default as NUpload } from './src/Upload' export { default as NUploadDragger } from './src/UploadDragger' +export { default as NUploadTrigger } from './src/UploadTrigger' +export { default as NUploadFileList } from './src/UploadFileList' export type { UploadProps } from './src/Upload' export type { uploadDraggerKey } from './src/UploadDragger' export type { UploadInst, FileInfo as UploadFileInfo } from './src/interface' diff --git a/src/upload/src/Upload.tsx b/src/upload/src/Upload.tsx index 6afef7586..47a42703a 100644 --- a/src/upload/src/Upload.tsx +++ b/src/upload/src/Upload.tsx @@ -7,7 +7,9 @@ import { ref, PropType, CSSProperties, - VNode, + renderSlot, + Fragment, + Teleport, nextTick } from 'vue' import { createId } from 'seemly' @@ -19,12 +21,11 @@ import { getFirstSlotVNode, warn, MaybeArray, - call + call, + throwError } from '../../_utils' -import { NFadeInExpandTransition } from '../../_internal' import { uploadLight, UploadTheme } from '../styles' -import NUploadFile from './UploadFile' -import NUploadDragger, { uploadDraggerKey } from './UploadDragger' +import { uploadDraggerKey } from './UploadDragger' import style from './styles/index.cssr' import { XhrHandlers, @@ -44,8 +45,8 @@ import { CreateThumbnailUrl } from './interface' import { createImageDataUrl } from './utils' -import { NImageGroup } from '../../image' - +import NUploadTrigger from './UploadTrigger' +import NUploadFileList from './UploadFileList' /** * fils status ['pending', 'uploading', 'finished', 'removed', 'error'] */ @@ -257,7 +258,8 @@ const uploadProps = { default: 'text' }, onPreview: Function as PropType, - createThumbnailUrl: Function as PropType + createThumbnailUrl: Function as PropType, + abstract: Boolean } as const export type UploadProps = ExtractPublicPropTypes @@ -266,6 +268,12 @@ export default defineComponent({ name: 'Upload', props: uploadProps, setup (props) { + if (props.abstract && props.listType === 'image-card') { + throwError( + 'upload', + 'when the list-type is image-card, abstract is not supported.' + ) + } const { mergedClsPrefixRef } = useConfig(props) const themeRef = useTheme( 'Upload', @@ -292,32 +300,6 @@ export default defineComponent({ function openFileDialog (): void { inputElRef.value?.click() } - function handleTriggerClick (): void { - if (mergedDisabledRef.value) return - openFileDialog() - } - function handleTriggerDragOver (e: DragEvent): void { - e.preventDefault() - dragOverRef.value = true - } - function handleTriggerDragEnter (e: DragEvent): void { - e.preventDefault() - dragOverRef.value = true - } - function handleTriggerDragLeave (e: DragEvent): void { - e.preventDefault() - dragOverRef.value = false - } - function handleTriggerDrop (e: DragEvent): void { - e.preventDefault() - if (!draggerInsideRef.value || mergedDisabledRef.value) return - const dataTransfer = e.dataTransfer - const files = dataTransfer?.files - if (files) { - handleFileAddition(files) - } - dragOverRef.value = false - } function handleFileInputChange (e: Event): void { const target = e.target as HTMLInputElement handleFileAddition(target.files, e) @@ -456,6 +438,46 @@ export default defineComponent({ ? await createThumbnailUrl(file.file as File) : await createImageDataUrl(file.file as File) } + const cssVarsRef = computed(() => { + const { + common: { cubicBezierEaseInOut }, + self: { + draggerColor, + draggerBorder, + draggerBorderHover, + itemColorHover, + itemColorHoverError, + itemTextColorError, + itemTextColorSuccess, + itemTextColor, + itemIconColor, + itemDisabledOpacity, + lineHeight, + borderRadius, + fontSize, + itemBorderImageCardError, + itemBorderImageCard + } + } = themeRef.value + return { + '--bezier': cubicBezierEaseInOut, + '--border-radius': borderRadius, + '--dragger-border': draggerBorder, + '--dragger-border-hover': draggerBorderHover, + '--dragger-color': draggerColor, + '--font-size': fontSize, + '--item-color-hover': itemColorHover, + '--item-color-hover-error': itemColorHoverError, + '--item-disabled-opacity': itemDisabledOpacity, + '--item-icon-color': itemIconColor, + '--item-text-color': itemTextColor, + '--item-text-color-error': itemTextColorError, + '--item-text-color-success': itemTextColorSuccess, + '--line-height': lineHeight, + '--item-border-image-card-error': itemBorderImageCardError, + '--item-border-image-card': itemBorderImageCard + } as any + }) provide(uploadInjectionKey, { mergedClsPrefixRef, @@ -473,114 +495,53 @@ export default defineComponent({ doChange, showPreivewButtonRef: toRef(props, 'showPreivewButton'), onPreviewRef: toRef(props, 'onPreview'), - getFileThumbnailUrl + getFileThumbnailUrl, + listTypeRef: toRef(props, 'listType'), + dragOverRef, + openFileDialog, + draggerInsideRef, + handleFileAddition, + fileListStyle: props.fileListStyle, + abstractRef: toRef(props, 'abstract'), + cssVarsRef }) return { mergedClsPrefix: mergedClsPrefixRef, draggerInsideRef, inputElRef, - mergedFileList: mergedFileListRef, mergedDisabled: mergedDisabledRef, mergedTheme: themeRef, dragOver: dragOverRef, - handleTriggerDrop, - handleTriggerDragLeave, - handleTriggerDragEnter, - handleTriggerDragOver, - handleTriggerClick, handleFileInputChange, submit, - openFileDialog, - cssVars: computed(() => { - const { - common: { cubicBezierEaseInOut }, - self: { - draggerColor, - draggerBorder, - draggerBorderHover, - itemColorHover, - itemColorHoverError, - itemTextColorError, - itemTextColorSuccess, - itemTextColor, - itemIconColor, - itemDisabledOpacity, - lineHeight, - borderRadius, - fontSize, - itemBorderImageCardError, - itemBorderImageCard - } - } = themeRef.value - return { - '--bezier': cubicBezierEaseInOut, - '--border-radius': borderRadius, - '--dragger-border': draggerBorder, - '--dragger-border-hover': draggerBorderHover, - '--dragger-color': draggerColor, - '--font-size': fontSize, - '--item-color-hover': itemColorHover, - '--item-color-hover-error': itemColorHoverError, - '--item-disabled-opacity': itemDisabledOpacity, - '--item-icon-color': itemIconColor, - '--item-text-color': itemTextColor, - '--item-text-color-error': itemTextColorError, - '--item-text-color-success': itemTextColorSuccess, - '--line-height': lineHeight, - '--item-border-image-card-error': itemBorderImageCardError, - '--item-border-image-card': itemBorderImageCard - } - }) + cssVars: cssVarsRef } }, render () { - const { draggerInsideRef, mergedClsPrefix, listType, $slots } = this - if ($slots.default) { + const { draggerInsideRef, mergedClsPrefix, $slots } = this + if ($slots.default && !this.abstract) { const firstChild = getFirstSlotVNode($slots, 'default') // @ts-expect-error if (firstChild?.type?.[uploadDraggerKey]) { draggerInsideRef.value = true } } - const isImageCardType = listType === 'image-card' - const uploadTrigger = ( -
- {isImageCardType ? ( - {this.$slots} - ) : ( - this.$slots - )} -
- ) - const createFileList = (): VNode[] => - this.mergedFileList.map((file) => ( - - )) - const uploadFileList = isImageCardType ? ( - {{ default: createFileList }} + + return this.abstract ? ( + <> + {renderSlot(this.$slots, 'default')} + + + + ) : ( - - {{ - default: createFileList - }} - - ) - return (
- {this.listType !== 'image-card' && uploadTrigger} - {this.showFileList && ( -
- {uploadFileList} - {isImageCardType && uploadTrigger} -
+ {this.listType !== 'image-card' && ( + {this.$slots} )} + {this.showFileList && {this.$slots}}
) } diff --git a/src/upload/src/UploadFileList.tsx b/src/upload/src/UploadFileList.tsx new file mode 100644 index 000000000..d936b89ee --- /dev/null +++ b/src/upload/src/UploadFileList.tsx @@ -0,0 +1,66 @@ +import { h, defineComponent, inject, VNode, CSSProperties, computed } from 'vue' +import { throwError } from '../../_utils' +import { uploadInjectionKey } from './interface' +import NUploadFile from './UploadFile' +import { NImageGroup } from '../../image' +import { NFadeInExpandTransition } from '../../_internal' +import NUploadTrigger from './UploadTrigger' + +export default defineComponent({ + name: 'UploadFileList', + setup (_, { slots }) { + const NUpload = inject(uploadInjectionKey, null) + if (!NUpload) { + throwError( + 'upload-file-list', + '`n-upload-file-list` must be placed inside `n-upload`.' + ) + } + + const { + mergedClsPrefixRef, + listTypeRef, + mergedFileListRef, + fileListStyle, + cssVarsRef + } = NUpload + const isImageCardTypeRef = computed( + () => listTypeRef.value === 'image-card' + ) + + const createFileList = (): VNode[] => + mergedFileListRef.value.map((file) => ( + + )) + + const createUploadFileList = (): VNode => + isImageCardTypeRef.value ? ( + {{ default: createFileList }} + ) : ( + + {{ + default: createFileList + }} + + ) + + return () => ( +
+ {createUploadFileList()} + {isImageCardTypeRef.value && {slots}} +
+ ) + } +}) diff --git a/src/upload/src/UploadTrigger.tsx b/src/upload/src/UploadTrigger.tsx new file mode 100644 index 000000000..164398c52 --- /dev/null +++ b/src/upload/src/UploadTrigger.tsx @@ -0,0 +1,89 @@ +import { h, defineComponent, inject, renderSlot, computed } from 'vue' +import { throwError } from '../../_utils' +import { uploadInjectionKey } from './interface' +import NUploadDragger from './UploadDragger' + +export default defineComponent({ + name: 'UploadTrigger', + setup (_, { slots }) { + const NUpload = inject(uploadInjectionKey, null) + if (!NUpload) { + throwError( + 'upload-trigger', + '`n-upload-trigger` must be placed inside `n-upload`.' + ) + } + + const { + mergedClsPrefixRef, + listTypeRef, + disabledRef, + dragOverRef, + openFileDialog, + draggerInsideRef, + handleFileAddition, + abstractRef + } = NUpload + + const isImageCardTypeRef = computed( + () => listTypeRef.value === 'image-card' + ) + + function handleTriggerClick (): void { + if (disabledRef.value) return + openFileDialog() + } + function handleTriggerDragOver (e: DragEvent): void { + e.preventDefault() + dragOverRef.value = true + } + function handleTriggerDragEnter (e: DragEvent): void { + e.preventDefault() + dragOverRef.value = true + } + function handleTriggerDragLeave (e: DragEvent): void { + e.preventDefault() + dragOverRef.value = false + } + function handleTriggerDrop (e: DragEvent): void { + e.preventDefault() + if (!draggerInsideRef.value || disabledRef.value) return + const dataTransfer = e.dataTransfer + const files = dataTransfer?.files + if (files) { + handleFileAddition(files) + } + dragOverRef.value = false + } + + return () => + abstractRef.value ? ( + renderSlot(slots, 'default', { + handleClick: handleTriggerClick, + handleDrop: handleTriggerDrop, + handleDragOver: handleTriggerDragOver, + handleDragEnter: handleTriggerDragEnter, + handleDragLeave: handleTriggerDragLeave + }) + ) : ( +
+ {isImageCardTypeRef.value ? ( + {slots} + ) : ( + slots + )} +
+ ) + } +}) diff --git a/src/upload/src/interface.ts b/src/upload/src/interface.ts index 42c114404..4a564effa 100644 --- a/src/upload/src/interface.ts +++ b/src/upload/src/interface.ts @@ -1,4 +1,4 @@ -import { Ref, InjectionKey } from 'vue' +import { Ref, InjectionKey, CSSProperties } from 'vue' import type { MergedTheme } from '../../_mixins' import type { UploadTheme } from '../styles' @@ -70,6 +70,14 @@ export interface UploadInjection { showPreivewButtonRef: Ref onPreviewRef: Ref getFileThumbnailUrl: (file: FileInfo) => Promise + listTypeRef: Ref + dragOverRef: Ref + draggerInsideRef: { value: boolean } + handleFileAddition: (files: FileList | null, e?: Event) => void + fileListStyle: string | CSSProperties | undefined + openFileDialog: () => void + abstractRef: Ref + cssVarsRef: Ref } export const uploadInjectionKey: InjectionKey = diff --git a/src/upload/src/styles/index.cssr.ts b/src/upload/src/styles/index.cssr.ts index 366c15584..6a8de0044 100644 --- a/src/upload/src/styles/index.cssr.ts +++ b/src/upload/src/styles/index.cssr.ts @@ -2,50 +2,67 @@ import { c, cM, cB, cE } from '../../../_utils/cssr' import fadeInHeightExpand from '../../../_styles/transitions/fade-in-height-expand.cssr' import createIconSwitchTransition from '../../../_styles/transitions/icon-switch.cssr' -export default cB('upload', [ - cE('file-input', ` - display: block; - width: 0; - height: 0; - opacity: 0; - `), - cE('trigger', ` - display: inline-block; - box-sizing: border-box; - `, [ - cM('image-card', [ +export default c([ + cB('upload', [ + cE('trigger', ` + display: inline-block; + box-sizing: border-box; + `, [ + cM('image-card', [ + cB('upload-dragger', ` + padding: 0; + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + `) + ]) + ]), + cM('dragger-inside', [ + cE('trigger', ` + display: block; + `) + ]), + cB('upload-dragger', ` + cursor: pointer; + box-sizing: border-box; + width: 100%; + text-align: center; + border-radius: var(--border-radius); + padding: 24px; + transition: + border-color .3s var(--bezier), + background-color .3s var(--bezier); + background-color: var(--dragger-color); + border: var(--dragger-border); + `, [ + c('&:hover', ` + border: var(--dragger-border-hover); + `) + ]), + cM('disabled', ` + opacity: var(--item-disabled-opacity); + `, [ + cE('trigger', ` + cursor: not-allowed; + `), + cB('upload-file', ` + cursor: not-allowed; + `), + cB('upload-file-list', ` + cursor: not-allowed; + `), cB('upload-dragger', ` - padding: 0; - height: 100%; - width: 100%; - display: flex; - align-items: center; - justify-content: center; + cursor: not-allowed; + `) + ]), + cM('drag-over', [ + cB('upload-dragger', ` + border: var(--dragger-border-hover); `) ]) ]), - cM('dragger-inside', [ - cE('trigger', ` - display: block; - `) - ]), - cB('upload-dragger', ` - cursor: pointer; - box-sizing: border-box; - width: 100%; - text-align: center; - border-radius: var(--border-radius); - padding: 24px; - transition: - border-color .3s var(--bezier), - background-color .3s var(--bezier); - background-color: var(--dragger-color); - border: var(--dragger-border); - `, [ - c('&:hover', ` - border: var(--dragger-border-hover); - `) - ]), cB('upload-file-list', ` margin-top: 8px; line-height: var(--line-height); @@ -71,7 +88,7 @@ export default cB('upload', [ }) ]), c('&:hover', ` - background-color: var(--item-color-hover); + background-color: var(--item-color-hover); `, [ cB('upload-file-info', [ cE('action', ` @@ -290,25 +307,10 @@ export default cB('upload', [ ]) ]) ]), - cM('disabled', ` - opacity: var(--item-disabled-opacity); - `, [ - cE('trigger', ` - cursor: not-allowed; - `), - cB('upload-file', ` - cursor: not-allowed; - `), - cB('upload-file-list', ` - cursor: not-allowed; - `), - cB('upload-dragger', ` - cursor: not-allowed; - `) - ]), - cM('drag-over', [ - cB('upload-dragger', ` - border: var(--dragger-border-hover); - `) - ]) + cB('upload__file-input', ` + display: block; + width: 0; + height: 0; + opacity: 0; + `) ]) diff --git a/src/upload/tests/Upload.spec.ts b/src/upload/tests/Upload.spec.ts index f9a3cd553..0333c1436 100644 --- a/src/upload/tests/Upload.spec.ts +++ b/src/upload/tests/Upload.spec.ts @@ -1,6 +1,10 @@ import { mount } from '@vue/test-utils' -import { NUpload } from '../index' +import { NUpload, NUploadFileList, NUploadTrigger } from '../index' import { sleep } from 'seemly' +import { NButtonGroup, NButton } from '../../button' +import { h } from 'vue' +import { NCard } from '../../card' +import { NImageGroup } from '../../image' const getMockFile = (element: Element, files: File[]): void => { Object.defineProperty(element, 'files', { @@ -184,6 +188,40 @@ describe('n-upload', () => { await button[0].trigger('click') expect(onRemove).toHaveBeenCalled() }) + it('should work with `abstract` prop', async () => { + const wrapper = mount(NUpload, { + props: { abstract: true }, + slots: { + default: () => [ + h(NButtonGroup, null, { + default: () => [ + h(NButton, null, { default: () => 'button1' }), + h( + NUploadTrigger, + { abstract: true }, + { + default: () => + h( + NButton, + { class: 'upload-button' }, + { default: () => 'upload button' } + ) + } + ) + ] + }), + h(NCard, null, { default: () => h(NUploadFileList, null) }) + ] + } + }) + const uploadWrapperDom = wrapper.find('.n-upload') + const uploadTriggerDom = wrapper.find('.upload-button') + const uploadFileLIstDom = wrapper.find('.n-upload-file-list') + + expect(uploadWrapperDom.exists()).toBe(false) + expect(uploadTriggerDom.exists()).toBe(true) + expect(uploadFileLIstDom.exists()).toBe(true) + }) it('should work with `accept` prop', async () => { const wrapper = mount(NUpload) @@ -205,3 +243,123 @@ describe('n-upload', () => { expect(wrapper.find('input').attributes('multiple')).toBe('') }) }) + +describe('n-upload-file-list', () => { + it('should work', async () => { + const wrapper = mount(NUpload, { + props: { + abstract: true, + defaultFileList: [ + { + name: 'test.png', + url: '/testUrl.png', + status: 'finished', + id: 'test', + percentage: 100, + file: null + } + ] + }, + slots: { + default: () => [ + h(NCard, null, { default: () => h(NUploadFileList, null) }) + ] + } + }) + + const fileList = wrapper.findAll('.n-upload-file') + expect(fileList.length).toEqual(1) + }) + it('should work with `list-type` prop', async () => { + const wrapper = mount(NUpload, { + props: { + listType: 'image-card', + defaultFileList: [ + { + name: 'test.png', + url: '/testUrl.png', + status: 'finished', + id: 'test', + percentage: 100, + file: null + } + ] + } + }) + expect(wrapper.findAll('.n-upload-file--image-card-type').length).toBe(1) + expect(wrapper.findComponent(NImageGroup).exists()).toBe(true) + }) + it('should work inside `n-upload`', async () => { + try { + mount(NUploadFileList) + } catch (error) { + expect(String(error)).toBe( + 'Error: [naive/upload-file-list]: `n-upload-file-list` must be placed inside `n-upload`.' + ) + } + }) +}) + +describe('n-upload-trigger', () => { + it('should work', async () => { + const wrapper = mount(NUpload, { + props: { abstract: true }, + slots: { + default: () => [ + h(NButtonGroup, null, { + default: () => [ + h(NButton, null, { default: () => 'button1' }), + h( + NUploadTrigger, + { abstract: true }, + { + default: () => + h( + NButton, + { class: 'upload-button' }, + { default: () => 'upload button' } + ) + } + ) + ] + }), + h(NCard, null, { default: () => h(NUploadFileList, null) }) + ] + } + }) + + const uploadButton = wrapper.findAll('.upload-button') + expect(uploadButton.length).toEqual(1) + }) + it('should work inside `n-upload`', async () => { + try { + mount(NUploadTrigger) + } catch (error) { + expect(String(error)).toBe( + 'Error: [naive/upload-trigger]: `n-upload-trigger` must be placed inside `n-upload`.' + ) + } + }) + + it('should work with drag and drop', async () => { + const wrapper = mount(NUpload, { + slots: { + default: () => h(NButton, null, { default: () => 'button1' }) + } + }) + const triggerItem = wrapper.find('.n-upload__trigger') + await triggerItem.trigger('click') + await triggerItem.trigger('drop') + + expect(wrapper.vm.dragOver).toBe(false) + + await triggerItem.trigger('dragenter') + expect(wrapper.vm.dragOver).toBe(true) + + await triggerItem.trigger('dragleave') + expect(wrapper.vm.dragOver).toBe(false) + + await triggerItem.trigger('dragover') + expect(wrapper.vm.dragOver).toBe(true) + }) +})