diff --git a/devui/popover/__tests__/popover.spec.ts b/devui/popover/__tests__/popover.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..699d892214d613a444c317e219d6a20ddb0dc5e3 --- /dev/null +++ b/devui/popover/__tests__/popover.spec.ts @@ -0,0 +1,96 @@ +import {shallowMount } from '@vue/test-utils'; +import DPopover from '../src/popover' +describe('DPopover', () => { + it('visible', () => { + const wrapper = shallowMount(DPopover, { + props: { + visible: true + } + }) + expect(wrapper.props().visible).toBeTruthy() + expect(wrapper.classes().includes('devui-popover-isVisible')).toBeTruthy() + }) + + it('position', () => { + const left = 'left' + const wrapper = shallowMount(DPopover, { + props: { + position: left + } + }) + expect(wrapper.props().position).toBe(left) + expect(wrapper.classes().includes(left)).toBeTruthy() + }) + + it('content', () => { + const content = '自定义内容' + const wrapper = shallowMount(DPopover, { + props: { + content + } + }) + expect(wrapper.props().content).toBe(content) + expect(wrapper.find('.devui-popover-content').text()).toBe(content) + }) + + it('trigger click', async () => { + const wrapper = shallowMount(DPopover) + const isVisible = () => expect(wrapper.classes().includes('devui-popover-isVisible')) + isVisible().toBeFalsy() + await wrapper.find('.devui-popover-reference').trigger('click') + isVisible().toBeTruthy(); + }) + + it('trigger mouse', async () => { + const wrapper = shallowMount(DPopover, { + props: { + trigger: 'hover' + } + }) + const isVisible = () => expect(wrapper.classes().includes('devui-popover-isVisible')) + isVisible().toBeFalsy() + wrapper.find('.devui-popover-reference').trigger('onMouseenter') + wrapper.vm.$nextTick(() => { + isVisible().toBeTruthy() + }) + }) + + it('zIndex', () => { + const zIndex = 1000 + const wrapper = shallowMount(DPopover, { + props: { + zIndex + } + }) + expect(wrapper.props().zIndex).toBe(zIndex); + }) + + it('popType', () => { + const wrapper = shallowMount(DPopover, { + props: { + popType: 'warning' + } + }) + expect(wrapper.find('.devui-popover-icon').attributes().name).toBe('warning-o') + }) + + it('showAnimation', () => { + const wrapper = shallowMount(DPopover, { + props: { + showAnimation: false, + } + }) + expect(wrapper.classes().includes('devui-popover-animation')).toBeFalsy() + }) + + it('popMaxWidth', () => { + const popMaxWidth = 30 + const wrapper = shallowMount(DPopover, { + props: { + popMaxWidth + } + }) + expect(wrapper.props().popMaxWidth).toBe(popMaxWidth) + }) + +}) \ No newline at end of file diff --git a/devui/popover/index.ts b/devui/popover/index.ts index 8325a36c012a26349dc8ea457c7281712437fb8c..6d9070c3656c29051f4fd1a6a1f45c62283390c6 100644 --- a/devui/popover/index.ts +++ b/devui/popover/index.ts @@ -10,7 +10,7 @@ export { Popover } export default { title: 'Popover 悬浮提示', category: '反馈', - status: '开发中', // TODO: 组件若开发完成则填入"已完成",并删除该注释 + status: '已完成', install(app: App): void { app.use(Popover as any); } diff --git a/devui/popover/src/debounce.ts b/devui/popover/src/debounce.ts new file mode 100644 index 0000000000000000000000000000000000000000..a9e0115be26301e123b8e44bb28e165fa9eb4f24 --- /dev/null +++ b/devui/popover/src/debounce.ts @@ -0,0 +1,9 @@ +export default ( callBack,wait: number) => { + let time = null + return () => { + time && clearTimeout(time); + time = setTimeout(() => { + callBack?.() + }, wait) + } +} \ No newline at end of file diff --git a/devui/popover/src/popover.scss b/devui/popover/src/popover.scss index 60f8e1d335f7ad328ba68cfecd0e0c2dea8ed063..3c6a1f10e3ba2f77b9bced976732600de215eb85 100644 --- a/devui/popover/src/popover.scss +++ b/devui/popover/src/popover.scss @@ -15,14 +15,13 @@ position: relative; @include some-display; - &::after { + .after { content: ''; width: 12px; display: none; height: 12px; transform: rotate(45deg); position: absolute; - z-index: 1060; background-color: $devui-feedback-overlay-bg; } @@ -31,7 +30,7 @@ @include some-display; } - &::after { + .after { @include some-display; } } @@ -39,6 +38,7 @@ .devui-popover-content { position: absolute; display: none; + overflow: hidden; padding: 5px 14px; align-items: center; flex-wrap: wrap; @@ -71,8 +71,8 @@ @include some-animation; } - &::after { - $devui-popover-animation-wait:0.1s; + .after { + $devui-popover-animation-wait:0.02s; opacity: 0; @include some-animation; @@ -112,7 +112,7 @@ transform: translate(-100%, -50%); } - &::after { + .after { @include left-postion--after; } } @@ -122,7 +122,7 @@ @include left-postion--content; } - &::after { + .after { @include left-postion--after; top: var(--devui-popover-offset); @@ -138,7 +138,7 @@ bottom: 0; } - &::after { + .after { @include left-postion--after; bottom: var(--devui-popover-offset); @@ -168,7 +168,7 @@ transform: translate(-50%, -100%); } - &::after { + .after { left: 50%; @include top-postion--after; } @@ -179,7 +179,7 @@ @include top-postion--content; } - &::after { + .after { @include top-postion--after; left: var(--devui-popover-offset); @@ -196,7 +196,7 @@ right: 0; } - &::after { + .after { @include top-postion--after; right: var(--devui-popover-offset); @@ -224,7 +224,7 @@ transform: translate(100%, -50%); } - &::after { + .after { @include right-postion--after; top: 50%; @@ -239,7 +239,7 @@ top: 0; } - &::after { + .after { @include right-postion--after; } } @@ -252,7 +252,7 @@ top: auto; } - &::after { + .after { @include right-postion--after; bottom: var(--devui-popover-offset); @@ -284,7 +284,7 @@ transform: translate(-50%, 100%); } - &::after { + .after { @include bottom-postion--after; left: 50%; @@ -298,7 +298,7 @@ @include bottom-postion--content; } - &::after { + .after { @include bottom-postion--after; } } @@ -311,7 +311,7 @@ right: 0; } - &::after { + .after { @include bottom-postion--after; left: auto; diff --git a/devui/popover/src/popover.tsx b/devui/popover/src/popover.tsx index 9fbd12ca37fc6c2f7baf8f0fec25b79c698b773b..9d7bbd97f0da66c2d6d8485832879650b03fc7a3 100644 --- a/devui/popover/src/popover.tsx +++ b/devui/popover/src/popover.tsx @@ -1,4 +1,5 @@ import { defineComponent, toRefs, ref, CSSProperties, reactive } from 'vue' +import debounce from './debounce'; import clickoutsideDirective from '../../shared/devui-directive/clickoutside' import './popover.scss' @@ -42,29 +43,41 @@ export default defineComponent({ type: Number as () => CSSProperties, default: 1060 }, + popType: { type: String as () => popType, default: 'default' }, + showAnimation: { type: Boolean, default: true }, + mouseEnterDelay: { type: Number, default: 150 }, + mouseLeaveDelay: { type: Number, default: 100 + }, + + popMaxWidth: { + type: Number, + default: undefined + }, + + popoverStyle: { + type: Object, + default: () => ({}) } }, setup(props, ctx) { - let enter = null - let leave = null const visible = ref(props.visible); - const { position, content, zIndex, trigger, popType, mouseEnterDelay, mouseLeaveDelay, showAnimation } = toRefs(props); + const { position, content, zIndex, trigger, popType, popoverStyle, mouseEnterDelay, mouseLeaveDelay, showAnimation, popMaxWidth } = toRefs(props); const isClick = trigger.value === 'click' const iconType = reactive(popTypeClass[popType.value]) const event = function () { @@ -75,28 +88,21 @@ export default defineComponent({ visible.value = true } const onClick = isClick ? event : null; - const onMouseenter = isClick ? null : () => { - enter && clearTimeout(enter); - enter = setTimeout(() => { - visible.value = true - }, mouseEnterDelay.value) - } - const onMouseleave = isClick ? null : () => { - leave && clearTimeout(leave) - leave = setTimeout(() => { - visible.value = false - }, mouseLeaveDelay.value) - } + const enter = debounce(() => { visible.value = true }, mouseEnterDelay.value) + const leave = debounce(() => { visible.value = false }, mouseLeaveDelay.value) + const onMouseenter = isClick ? null : enter + const onMouseleave = isClick ? null : leave const hiddenContext = function () { visible.value = false } - return () => { const { slots } = ctx; const style: CSSProperties = { - zIndex: zIndex.value + zIndex: zIndex.value, + ...popoverStyle.value } + popMaxWidth.value && (style.maxWidth = `${popMaxWidth.value}px`) return (