diff --git a/packages/leaner/src/web/classes.js b/packages/leaner/src/web/classes.js index e62976b..9eb99ed 100644 --- a/packages/leaner/src/web/classes.js +++ b/packages/leaner/src/web/classes.js @@ -7,15 +7,17 @@ export function setClasses( element, classes ) { if ( Array.isArray( value ) ) { element.className = ''; element.classList.add( ...value ); - } else { + } else if ( value != null ) { element.className = value; + } else { + element.removeAttribute( 'class' ); } } ); } else if ( isPlainObject( classes ) ) { setClassesObject( element, classes ); } else if ( Array.isArray( classes ) ) { setClassesArray( element, classes ); - } else { + } else if ( classes != null ) { element.className = classes; } } @@ -54,7 +56,7 @@ function setClassesArray( element, classes ) { } ); } else if ( isPlainObject( item ) ) { setClassesObject( element, item ); - } else { + } else if ( item != null ) { element.classList.add( item ); } } diff --git a/packages/leaner/src/web/make.js b/packages/leaner/src/web/make.js index f9148c7..ee21b60 100644 --- a/packages/leaner/src/web/make.js +++ b/packages/leaner/src/web/make.js @@ -97,9 +97,9 @@ function setElementProperty( element, key, value ) { element.addEventListener( key.substring( 2 ), value ); } else if ( Properties.has( key ) ) { if ( typeof value == 'function' ) - reactive( value, value => element[ key ] = value ); + reactive( value, value => element[ key ] = value != null ? value : '' ); else - element[ key ] = value; + element[ key ] = value != null ? value : ''; } else { if ( typeof value == 'function' ) reactive( value, value => setElementAttribute( element, key, value ) ); diff --git a/packages/leaner/src/web/styles.js b/packages/leaner/src/web/styles.js index 37f1dcc..ec7e107 100644 --- a/packages/leaner/src/web/styles.js +++ b/packages/leaner/src/web/styles.js @@ -8,13 +8,15 @@ export function setStyles( element, styles ) { element.style = ''; for ( const [ key, value ] of Object.entries( value ) ) setStyleProperty( element, key, value ); - } else { + } else if ( value != null ) { element.style = value; + } else { + element.removeAttribute( 'style' ); } } ); } else if ( isPlainObject( styles ) ) { setStylesObject( element, styles ); - } else { + } else if ( styles != null ) { element.style = styles; } } @@ -22,13 +24,15 @@ export function setStyles( element, styles ) { function setStylesObject( element, styles ) { for ( const [ key, value ] of Object.entries( styles ) ) { if ( typeof value == 'function' ) - reactive( value, value => element.style[ key ] = value ); + reactive( value, value => setStyleProperty( element, key, value ) ); else setStyleProperty( element, key, value ); } } function setStyleProperty( element, key, value ) { + if ( value == null ) + value = ''; if ( key.startsWith( '--' ) ) element.style.setProperty( key, value ); else diff --git a/packages/leaner/test/web/classes.spec.js b/packages/leaner/test/web/classes.spec.js index f460d4e..7068de6 100644 --- a/packages/leaner/test/web/classes.spec.js +++ b/packages/leaner/test/web/classes.spec.js @@ -10,12 +10,24 @@ describe( 'classes', () => { expect( element.className ).toBe( 'btn-primary is-large' ); } ); + test( 'undefined', () => { + const element = make( [ 'button', { type: 'button', class: undefined } ] ); + + expect( element.outerHTML ).toBe( '' ); + } ); + test( 'static array', () => { const element = make( [ 'button', { type: 'button', class: [ 'btn-primary', 'is-large' ] } ] ); expect( element.className ).toBe( 'btn-primary is-large' ); } ); + test( 'static array with undefined', () => { + const element = make( [ 'button', { type: 'button', class: [ 'btn-primary', 'is-large', undefined ] } ] ); + + expect( element.className ).toBe( 'btn-primary is-large' ); + } ); + test( 'dynamic value', () => { const [ value, setValue ] = state( 'btn-primary' ); diff --git a/packages/leaner/test/web/make.spec.js b/packages/leaner/test/web/make.spec.js index a410a05..7889233 100644 --- a/packages/leaner/test/web/make.spec.js +++ b/packages/leaner/test/web/make.spec.js @@ -47,6 +47,13 @@ describe( 'make()', () => { expect( element.value ).toBe( 'hello' ); } ); + test( 'undefined value', () => { + const element = make( [ 'input', { value: undefined } ] ); + + expect( element ).toBeInstanceOf( HTMLInputElement ); + expect( element.value ).toBe( '' ); + } ); + test( 'dynamic content', () => { const [ value, setValue ] = state( 'test' ); @@ -62,6 +69,13 @@ describe( 'make()', () => { expect( element.textContent ).toBe( 'hello' ); } ); + test( 'undefined content', () => { + const element = make( [ 'p', undefined ] ); + + expect( element ).toBeInstanceOf( HTMLElement ); + expect( element.textContent ).toBe( '' ); + } ); + test( 'dynamic attribute', () => { const [ value, setValue ] = state( 'test' ); @@ -77,6 +91,13 @@ describe( 'make()', () => { expect( element.outerHTML ).toBe( '' ); } ); + test( 'undefined attribute', () => { + const element = make( [ 'label', { for: undefined }, 'hello' ] ); + + expect( element ).toBeInstanceOf( HTMLLabelElement ); + expect( element.outerHTML ).toBe( '' ); + } ); + test( 'remove attribute', () => { const [ value, setValue ] = state( 'test' ); diff --git a/packages/leaner/test/web/styles.spec.js b/packages/leaner/test/web/styles.spec.js index 0b1f677..672faa5 100644 --- a/packages/leaner/test/web/styles.spec.js +++ b/packages/leaner/test/web/styles.spec.js @@ -10,6 +10,12 @@ describe( 'styles', () => { expect( element.style.display ).toBe( 'none' ); } ); + test( 'undefined', () => { + const element = make( [ 'button', { type: 'button', style: undefined } ] ); + + expect( element.outerHTML ).toBe( '' ); + } ); + test( 'static object', () => { const element = make( [ 'button', { type: 'button', style: { display: 'none' } } ] ); @@ -44,5 +50,19 @@ describe( 'styles', () => { expect( element.style.display ).toBe( 'inline' ); } ); + test( 'dynamic object with undefined', () => { + const [ value, setValue ] = state( 'none' ); + + const element = make( [ 'button', { type: 'button', style: { display: value } } ] ); + + expect( element.style.display ).toBe( 'none' ); + + setValue( undefined ); + + runSchedule(); + + expect( element.style.display ).toBe( '' ); + } ); + // NOTE: custom properties cannot be tested because of https://github.com/jsdom/jsdom/issues/1895 } );