@dimailn/vuetify 2.7.2-alpha47 → 2.7.2-alpha49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -131,4 +131,107 @@ describe('Overlayable.ts', () => {
131
131
 
132
132
  expect(cb).toHaveBeenCalledTimes(2)
133
133
  })
134
+
135
+ describe('stale overlay ref after DOM detach', () => {
136
+ /** Как у VNavigationDrawer: showOverlay управляет genOverlay/removeOverlay */
137
+ const DrawerLike = defineComponent({
138
+ mixins: [Overlayable],
139
+ data: () => ({
140
+ isActive: false,
141
+ isMobile: false,
142
+ temporary: false
143
+ }),
144
+ computed: {
145
+ showOverlay (): boolean {
146
+ return this.isActive && (this.isMobile || this.temporary)
147
+ }
148
+ },
149
+ watch: {
150
+ showOverlay (val: boolean) {
151
+ if (val) this.genOverlay()
152
+ else this.removeOverlay()
153
+ }
154
+ },
155
+ render: () => h('div', { class: 'drawer-host' })
156
+ })
157
+
158
+ let mountDrawerLike: (options?: MountingOptions<any>) => VueWrapper<any>
159
+
160
+ beforeEach(() => {
161
+ document.body.innerHTML = ''
162
+ document.body.setAttribute('data-app', 'true')
163
+
164
+ mountDrawerLike = (options = {}) => mount(DrawerLike, {
165
+ global: {
166
+ mocks: {
167
+ $vuetify: { breakpoint: {} }
168
+ }
169
+ },
170
+ ...options
171
+ })
172
+ })
173
+
174
+ it('genOverlay пересоздаёт оверлей если узел отцеплён от DOM', async () => {
175
+ const wrapper = mountDrawerLike()
176
+
177
+ wrapper.vm.temporary = true
178
+ wrapper.vm.isActive = true
179
+ await nextTick()
180
+ await waitAnimationFrame()
181
+
182
+ const app = document.querySelector('[data-app]') as HTMLElement
183
+ wrapper.vm.overlay!.$el.parentNode!.remove()
184
+
185
+ wrapper.vm.genOverlay()
186
+ await waitAnimationFrame()
187
+
188
+ expect(app.querySelector('.v-overlay--active')).toBeTruthy()
189
+ expect(wrapper.vm.overlay!.$el.isConnected).toBe(true)
190
+ })
191
+
192
+ it('повторное открытие дровера восстанавливает оверлей в [data-app]', async () => {
193
+ const wrapper = mountDrawerLike()
194
+
195
+ wrapper.vm.temporary = true
196
+ wrapper.vm.isActive = true
197
+ await nextTick()
198
+ await waitAnimationFrame()
199
+
200
+ const app = document.querySelector('[data-app]') as HTMLElement
201
+ wrapper.vm.overlay!.$el.parentNode!.remove()
202
+
203
+ wrapper.vm.isActive = false
204
+ await nextTick()
205
+ wrapper.vm.isActive = true
206
+ await nextTick()
207
+ await waitAnimationFrame()
208
+
209
+ expect(app.querySelector('.v-overlay--active')).toBeTruthy()
210
+ expect(wrapper.vm.overlay!.$el.isConnected).toBe(true)
211
+ })
212
+
213
+ it('showOverlay true→true не вызывает genOverlay после потери [data-app]', async () => {
214
+ const genOverlay = jest.fn()
215
+ const removeOverlay = jest.fn()
216
+
217
+ const wrapper = mountDrawerLike()
218
+
219
+ wrapper.vm.genOverlay = genOverlay
220
+ wrapper.vm.removeOverlay = removeOverlay
221
+
222
+ wrapper.vm.temporary = true
223
+ wrapper.vm.isActive = true
224
+ await nextTick()
225
+
226
+ genOverlay.mockClear()
227
+ removeOverlay.mockClear()
228
+
229
+ document.body.innerHTML = ''
230
+ document.body.setAttribute('data-app', 'true')
231
+
232
+ expect(wrapper.vm.showOverlay).toBe(true)
233
+ expect(genOverlay).not.toHaveBeenCalled()
234
+ expect(removeOverlay).not.toHaveBeenCalled()
235
+ })
236
+ })
134
237
  })
@@ -112,7 +112,17 @@ export default defineComponent({
112
112
 
113
113
  if (this.hideOverlay) return
114
114
 
115
- if (!this.overlay) this.createOverlay()
115
+ if (!this.overlay?.$el?.isConnected) {
116
+ if (this.overlay) {
117
+ cancelAnimationFrame(this.animationFrame)
118
+ const container = this.overlay.$el?.parentNode as HTMLElement | null
119
+ this.overlayApp?.unmount()
120
+ this.overlayApp = null
121
+ if (container?.parentNode) container.parentNode.removeChild(container)
122
+ this.overlay = null
123
+ }
124
+ this.createOverlay()
125
+ }
116
126
 
117
127
  this.animationFrame = requestAnimationFrame(() => {
118
128
  if (!this.overlay) return
@@ -131,6 +141,15 @@ export default defineComponent({
131
141
  /** removeOverlay(false) will not restore the scollbar afterwards */
132
142
  removeOverlay (showScroll = true) {
133
143
  if (this.overlay) {
144
+ if (!this.overlay.$el?.isConnected) {
145
+ cancelAnimationFrame(this.animationFrame)
146
+ this.overlayApp?.unmount()
147
+ this.overlayApp = null
148
+ this.overlay = null
149
+ showScroll && this.showScroll()
150
+ return
151
+ }
152
+
134
153
  addOnceEventListener(this.overlay.$el, 'transitionend', () => {
135
154
  if (
136
155
  !this.overlay ||