From 101abb96348a2cac9e438d6d4d58c8d7f2493939 Mon Sep 17 00:00:00 2001 From: Rijk van Zanten Date: Thu, 12 Mar 2020 13:58:41 -0400 Subject: [PATCH] Use element size (#169) * Install resize observer * Add use-element-size composition * Rename function to match filename * Remove false statement from readme --- package.json | 1 + src/compositions/use-element-size/index.ts | 4 + src/compositions/use-element-size/readme.md | 29 ++++++ .../use-element-size/use-element-size.test.ts | 97 +++++++++++++++++++ .../use-element-size/use-element-size.ts | 27 ++++++ yarn.lock | 5 + 6 files changed, 163 insertions(+) create mode 100644 src/compositions/use-element-size/index.ts create mode 100644 src/compositions/use-element-size/readme.md create mode 100644 src/compositions/use-element-size/use-element-size.test.ts create mode 100644 src/compositions/use-element-size/use-element-size.ts diff --git a/package.json b/package.json index 3228c240b9..52f5fc17cf 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "lodash": "^4.17.15", "nanoid": "^2.1.11", "pinia": "0.0.5", + "resize-observer": "^1.0.0", "stylelint-config-prettier": "^8.0.1", "vue": "^2.6.11", "vue-i18n": "^8.15.5", diff --git a/src/compositions/use-element-size/index.ts b/src/compositions/use-element-size/index.ts new file mode 100644 index 0000000000..e4e42428b8 --- /dev/null +++ b/src/compositions/use-element-size/index.ts @@ -0,0 +1,4 @@ +import useElementSize from './use-element-size'; + +export { useElementSize }; +export default useElementSize; diff --git a/src/compositions/use-element-size/readme.md b/src/compositions/use-element-size/readme.md new file mode 100644 index 0000000000..d4e2140cc0 --- /dev/null +++ b/src/compositions/use-element-size/readme.md @@ -0,0 +1,29 @@ +# `useElementSize` + +```ts +function useElementSize(element: Element): { width: Ref, height: Ref } +``` + +Allows you to reactively watch an elements width and height. + +## Usage +```vue + + + +``` diff --git a/src/compositions/use-element-size/use-element-size.test.ts b/src/compositions/use-element-size/use-element-size.test.ts new file mode 100644 index 0000000000..d5df067255 --- /dev/null +++ b/src/compositions/use-element-size/use-element-size.test.ts @@ -0,0 +1,97 @@ +import mountComposition from '../../../.jest/mount-composition'; +import useElementSize from './use-element-size'; +import { ResizeObserver } from 'resize-observer'; +import { ref } from '@vue/composition-api'; + +jest.mock('resize-observer'); + +const mockResizeObserver = { + observe: jest.fn(), + disconnect: jest.fn() +}; + +describe('Compositions / useElementSize', () => { + beforeEach(() => { + (ResizeObserver as jest.Mock).mockImplementation(() => { + return mockResizeObserver; + }); + }); + + it('Creates a resize observer', () => { + const el = document.createElement('div'); + + mountComposition(() => { + useElementSize(el); + }); + + expect(ResizeObserver).toHaveBeenCalled(); + }); + + it('Calls observe with the passed element on mount', () => { + const el = document.createElement('div'); + + mountComposition(() => { + useElementSize(el); + }); + + expect(mockResizeObserver.observe).toHaveBeenCalledWith(el); + }); + + it('Calls observer with element if ref is passed', () => { + const el = document.createElement('div'); + + mountComposition(() => { + const refEl = ref(el); + useElementSize(refEl); + }); + + expect(mockResizeObserver.observe).toHaveBeenCalledWith(el); + }); + + it('Does not call observe when passed element is null or undefined', () => { + mountComposition(() => { + useElementSize(ref(null)); + }); + + expect(mockResizeObserver.observe).not.toHaveBeenCalled(); + }); + + it('Calls disconnect on unmount', () => { + const el = document.createElement('div'); + + mountComposition(() => { + useElementSize(el); + }).destroy(); + + expect(mockResizeObserver.disconnect).toHaveBeenCalled(); + }); + + it('Sets the returned width and height refs on ResizeObserver handler', () => { + let handler: (_: any) => void; + + (ResizeObserver as jest.Mock).mockImplementation(constructorParam => { + handler = constructorParam; + return mockResizeObserver; + }); + + const el = document.createElement('div'); + + mountComposition(() => { + const { width, height } = useElementSize(el); + expect(width.value).toBe(0); + expect(height.value).toBe(0); + + handler([ + { + contentRect: { + width: 150, + height: 150 + } + } + ]); + + expect(width.value).toBe(150); + expect(height.value).toBe(150); + }); + }); +}); diff --git a/src/compositions/use-element-size/use-element-size.ts b/src/compositions/use-element-size/use-element-size.ts new file mode 100644 index 0000000000..29a1037567 --- /dev/null +++ b/src/compositions/use-element-size/use-element-size.ts @@ -0,0 +1,27 @@ +import { Ref, ref, isRef, onMounted, onUnmounted } from '@vue/composition-api'; +import { notEmpty } from '@/utils/is-empty'; +import { ResizeObserver } from 'resize-observer'; + +export default function useElementSize(target: T | Ref | Ref) { + const width = ref(0); + const height = ref(0); + + const resizeObserver = new ResizeObserver(([entry]) => { + width.value = entry.contentRect.width; + height.value = entry.contentRect.height; + }); + + onMounted(() => { + const t = isRef(target) ? target.value : target; + + if (notEmpty(t)) { + resizeObserver.observe(t); + } + }); + + onUnmounted(() => { + resizeObserver.disconnect(); + }); + + return { width, height }; +} diff --git a/yarn.lock b/yarn.lock index 2285d8ca3c..3d760bb784 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12237,6 +12237,11 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== +resize-observer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resize-observer/-/resize-observer-1.0.0.tgz#4f8380b73b411af4ed7d916fe85a2d59900e71ef" + integrity sha512-D7UFShDm2TgrEDEyeg+/tTEbvOgPWlvPAfJtxiKp+qutu6HowmcGJKjECgGru0PPDIj3SAucn3ZPpOx54fF7DQ== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"