From c34f35cfdffe05a75bcd1f7a18d6b3dea8149545 Mon Sep 17 00:00:00 2001 From: Kirill Romanov Date: Wed, 4 Dec 2019 21:04:54 +0300 Subject: [PATCH] fix(VTextField): use intersect to recalculate elements width (#9341) * feat: add new mixin to work with IntersectionObserver * fix(VTextField): use intersect to recalculate elements width fixes #9299 --- .../src/components/VTextField/VTextField.ts | 10 +++- .../__tests__/intersectable.spec.ts | 39 +++++++++++++++ .../vuetify/src/mixins/intersectable/index.ts | 49 +++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 packages/vuetify/src/mixins/intersectable/__tests__/intersectable.spec.ts create mode 100644 packages/vuetify/src/mixins/intersectable/index.ts diff --git a/packages/vuetify/src/components/VTextField/VTextField.ts b/packages/vuetify/src/components/VTextField/VTextField.ts index 89db50a5b95..bee012253b3 100644 --- a/packages/vuetify/src/components/VTextField/VTextField.ts +++ b/packages/vuetify/src/components/VTextField/VTextField.ts @@ -9,6 +9,7 @@ import VCounter from '../VCounter' import VLabel from '../VLabel' // Mixins +import Intersectable from '../../mixins/intersectable' import Loadable from '../../mixins/loadable' // Directives @@ -24,7 +25,14 @@ import { VNode } from 'vue/types' const baseMixins = mixins( VInput, - Loadable + Intersectable({ + onVisible: [ + 'setLabelWidth', + 'setPrefixWidth', + 'setPrependWidth', + ], + }), + Loadable, ) interface options extends InstanceType { $refs: { diff --git a/packages/vuetify/src/mixins/intersectable/__tests__/intersectable.spec.ts b/packages/vuetify/src/mixins/intersectable/__tests__/intersectable.spec.ts new file mode 100644 index 00000000000..cece314e3a0 --- /dev/null +++ b/packages/vuetify/src/mixins/intersectable/__tests__/intersectable.spec.ts @@ -0,0 +1,39 @@ +import intersectable from '../index' + +import { + mount, + Wrapper, +} from '@vue/test-utils' +import { ComponentOptions } from 'vue' + +describe('intersectable.ts', () => { + let mountFunction: (options?: ComponentOptions) => Wrapper + + beforeEach(() => { + mountFunction = (options?: ComponentOptions) => { + return mount({ + render: h => h('div'), + ...options, + }) + } + }) + + it('should call callbacks when element is intersected', () => { + const callback = jest.fn() + + const wrapper = mountFunction({ + mixins: [intersectable({ onVisible: ['callback'] })], + methods: { callback }, + }) + + expect(callback).not.toHaveBeenCalled() + + wrapper.vm.onObserve([] as IntersectionObserverEntry[], null as any as IntersectionObserver, false) + + expect(callback).not.toHaveBeenCalled() + + wrapper.vm.onObserve([] as IntersectionObserverEntry[], null as any as IntersectionObserver, true) + + expect(callback).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/vuetify/src/mixins/intersectable/index.ts b/packages/vuetify/src/mixins/intersectable/index.ts new file mode 100644 index 00000000000..f0a17a1b6bb --- /dev/null +++ b/packages/vuetify/src/mixins/intersectable/index.ts @@ -0,0 +1,49 @@ +// Directives +import Intersect from '../../directives/intersect' + +// Utilities +import { consoleWarn } from '../../util/console' + +// Types +import Vue from 'vue' + +export default function intersectable (options: { onVisible: string[] }) { + if (typeof window === 'undefined' || !('IntersectionObserver' in window)) { + // do nothing because intersection observer is not available + return Vue.extend({ name: 'intersectable' }) + } + + return Vue.extend({ + name: 'intersectable', + + mounted () { + Intersect.inserted(this.$el as HTMLElement, { + name: 'intersect', + value: { + handler: this.onObserve, + }, + }) + }, + + destroyed () { + Intersect.unbind(this.$el as HTMLElement) + }, + + methods: { + onObserve (entries: IntersectionObserverEntry[], observer: IntersectionObserver, isIntersecting: boolean) { + if (!isIntersecting) return + + for (let i = 0, length = options.onVisible.length; i < length; i++) { + const callback = (this as any)[options.onVisible[i]] + + if (typeof callback === 'function') { + callback() + continue + } + + consoleWarn(options.onVisible[i] + ' method is not available on the instance but referenced in intersectable mixin options') + } + }, + }, + }) +}