From 0a9aabd6af212eb6ab335f860b4e669fefc4369b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jess=C3=A9=20Correia=20Lins?= Date: Tue, 20 Feb 2024 06:26:07 -0300 Subject: [PATCH] feat(useCurrentElement): Allow get current element from a specific component (#3750) Co-authored-by: Anthony Fu --- packages/core/useCurrentElement/index.md | 24 ++++++++ packages/core/useCurrentElement/index.test.ts | 58 +++++++++++++++++++ packages/core/useCurrentElement/index.ts | 11 +++- 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 packages/core/useCurrentElement/index.test.ts diff --git a/packages/core/useCurrentElement/index.md b/packages/core/useCurrentElement/index.md index a150e1cd1b9..34521a1da3c 100644 --- a/packages/core/useCurrentElement/index.md +++ b/packages/core/useCurrentElement/index.md @@ -14,6 +14,30 @@ import { useCurrentElement } from '@vueuse/core' const el = useCurrentElement() // ComputedRef ``` +Or pass a specific vue component + +```vue + + + +``` + +::: info +Only works for Vue 3 because it uses [computedWithControl](https://vueuse.org/shared/computedWithControl/#manual-triggering) under the hood +::: + ## Caveats This functions uses [`$el` under the hood](https://vuejs.org/api/component-instance.html#el). diff --git a/packages/core/useCurrentElement/index.test.ts b/packages/core/useCurrentElement/index.test.ts new file mode 100644 index 00000000000..3da047bdcaa --- /dev/null +++ b/packages/core/useCurrentElement/index.test.ts @@ -0,0 +1,58 @@ +import { defineComponent, isVue2, shallowRef } from 'vue-demi' +import { mount } from '@vue/test-utils' +import { describe, expect, it } from 'vitest' +import { useCurrentElement } from '.' + +// Manual triggering only works for Vue 3 - https://vueuse.org/shared/computedWithControl/#manual-triggering +describe.skipIf(isVue2)('useCurrentElement', () => { + it('should be defined', () => { + expect(useCurrentElement).toBeDefined() + }) + + it('should return the root element from the current component', () => { + const wrapper = mount({ + template: '

test

', + setup() { + const el = shallowRef() + const currentElement = useCurrentElement() + + return { el, currentElement } + }, + }) + const vm = wrapper.vm + + expect(vm.currentElement).toBe(vm.el) + wrapper.unmount() + }) + + it('should return the root element from the passed component', () => { + const TestVueComponent = defineComponent({ + setup() { + const rootEl = shallowRef() + + return { rootEl } + }, + template: '
Hello world
', + }) + const wrapper = mount({ + components: { + TestVueComponent, + }, + template: `

+ Testing + +

`, + setup() { + const el = shallowRef() + const currentElementEl = useCurrentElement(el) + + return { el, currentElementEl } + }, + }) + const vm = wrapper.vm + + expect(vm.currentElementEl).toBe((vm.el as typeof TestVueComponent).rootEl) + expect((vm.currentElementEl as HTMLElement).textContent).toBe('Hello world') + wrapper.unmount() + }) +}) diff --git a/packages/core/useCurrentElement/index.ts b/packages/core/useCurrentElement/index.ts index acfee876e89..dac96a6deae 100644 --- a/packages/core/useCurrentElement/index.ts +++ b/packages/core/useCurrentElement/index.ts @@ -1,12 +1,19 @@ // eslint-disable-next-line no-restricted-imports import { getCurrentInstance, onMounted, onUpdated } from 'vue-demi' import { computedWithControl } from '@vueuse/shared' +import type { MaybeElement, MaybeElementRef, VueInstance } from '../unrefElement' +import { unrefElement } from '../unrefElement' -export function useCurrentElement() { +export function useCurrentElement< + T extends MaybeElement = MaybeElement, + R extends VueInstance = VueInstance, +>( + rootComponent?: MaybeElementRef, +) { const vm = getCurrentInstance()! const currentElement = computedWithControl( () => null, - () => vm.proxy!.$el as T, + () => (rootComponent ? unrefElement(rootComponent) : vm.proxy!.$el) as T, ) onUpdated(currentElement.trigger)