@@ -2,7 +2,7 @@ import * as React from 'react'
22import { forwardRef } from 'react'
33import { render } from 'vitest-browser-react'
44import createMockRaf , { MockRaf } from '@react-spring/mock-raf'
5- import { Globals } from '@react-spring/shared'
5+ import { FluidValue , Globals , callFluidObservers } from '@react-spring/shared'
66import { SpringValue , Animatable } from '@react-spring/core'
77
88import { a } from './index'
@@ -224,6 +224,89 @@ describe('animated component', () => {
224224 } )
225225} )
226226
227+ describe ( 'animated component attribute removal' , ( ) => {
228+ it ( 'removes a boolean-style attribute when its animated value becomes undefined' , ( ) => {
229+ const inert = new TestFluid < true | undefined > ( true )
230+ const { getByTestId } = render (
231+ < a . div inert = { inert as any } data-testid = "wrapper" />
232+ )
233+ const el = getByTestId ( 'wrapper' ) . element ( ) as HTMLElement
234+ expect ( el . hasAttribute ( 'inert' ) ) . toBe ( true )
235+ inert . set ( undefined )
236+ mockRaf . step ( )
237+ expect ( el . hasAttribute ( 'inert' ) ) . toBe ( false )
238+ } )
239+ it ( 'removes a generic attribute when its animated value becomes undefined' , ( ) => {
240+ const value = new TestFluid < string | undefined > ( 'bar' )
241+ const { getByTestId } = render (
242+ < a . div data-foo = { value as any } data-testid = "wrapper" />
243+ )
244+ const el = getByTestId ( 'wrapper' ) . element ( ) as HTMLElement
245+ expect ( el . getAttribute ( 'data-foo' ) ) . toBe ( 'bar' )
246+ value . set ( undefined )
247+ mockRaf . step ( )
248+ expect ( el . hasAttribute ( 'data-foo' ) ) . toBe ( false )
249+ } )
250+ it ( 'removes the viewBox attribute when its animated value becomes undefined' , ( ) => {
251+ const viewBox = new TestFluid < string | undefined > ( '0 0 100 100' )
252+ const { getByTestId } = render (
253+ < a . svg viewBox = { viewBox as any } data-testid = "wrapper" />
254+ )
255+ const el = getByTestId ( 'wrapper' ) . element ( ) as unknown as SVGSVGElement
256+ expect ( el . getAttribute ( 'viewBox' ) ) . toBe ( '0 0 100 100' )
257+ viewBox . set ( undefined )
258+ mockRaf . step ( )
259+ expect ( el . hasAttribute ( 'viewBox' ) ) . toBe ( false )
260+ } )
261+ it ( 'removes the class attribute when className becomes undefined' , ( ) => {
262+ const className = new TestFluid < string | undefined > ( 'initial' )
263+ const { getByTestId } = render (
264+ < a . div className = { className as any } data-testid = "wrapper" />
265+ )
266+ const el = getByTestId ( 'wrapper' ) . element ( ) as HTMLElement
267+ expect ( el . getAttribute ( 'class' ) ) . toBe ( 'initial' )
268+ className . set ( undefined )
269+ mockRaf . step ( )
270+ expect ( el . hasAttribute ( 'class' ) ) . toBe ( false )
271+ } )
272+ it ( 'clears textContent when children becomes undefined' , ( ) => {
273+ const children = new TestFluid < string | undefined > ( 'hello' )
274+ const { getByTestId } = render (
275+ < a . div data-testid = "wrapper" > { children as any } </ a . div >
276+ )
277+ const el = getByTestId ( 'wrapper' ) . element ( ) as HTMLElement
278+ expect ( el . textContent ) . toBe ( 'hello' )
279+ children . set ( undefined )
280+ mockRaf . step ( )
281+ expect ( el . textContent ) . toBe ( '' )
282+ } )
283+ it ( 'still applies defined falsy attribute values rather than removing them' , ( ) => {
284+ const value = new TestFluid < string > ( '1' )
285+ const { getByTestId } = render (
286+ < a . div tabIndex = { value as any } data-testid = "wrapper" />
287+ )
288+ const el = getByTestId ( 'wrapper' ) . element ( ) as HTMLElement
289+ expect ( el . getAttribute ( 'tabindex' ) ) . toBe ( '1' )
290+ value . set ( '0' )
291+ mockRaf . step ( )
292+ expect ( el . getAttribute ( 'tabindex' ) ) . toBe ( '0' )
293+ expect ( el . hasAttribute ( 'tabindex' ) ) . toBe ( true )
294+ } )
295+ } )
296+
227297function spring < T > ( value : T ) : SpringValue < Animatable < T > > {
228298 return new SpringValue ( value ! )
229299}
300+
301+ class TestFluid < T > extends FluidValue < T > {
302+ constructor ( private _value : T ) {
303+ super ( )
304+ }
305+ protected get ( ) : T {
306+ return this . _value
307+ }
308+ set ( next : T ) : void {
309+ this . _value = next
310+ callFluidObservers ( this , { type : 'change' , parent : this } )
311+ }
312+ }
0 commit comments