diff --git a/package.json b/package.json index 036ca99..fd1dda9 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "rules": { "brace-style": "off", "jest/expect-expect": "off", + "lines-around-comment": "off", "comma-dangle": "off", "no-unused-vars": [ 2, diff --git a/src/index.test.jsx b/src/index.test.jsx index 00606eb..77b5b2d 100644 --- a/src/index.test.jsx +++ b/src/index.test.jsx @@ -4,248 +4,230 @@ import { useContext } from 'preact/hooks'; import { act } from 'preact/test-utils'; import registerElement from './index'; -function Clock({ time }) { - return {time}; -} +describe('web components', () => { + /** @type {HTMLDivElement} */ + let root; -registerElement(Clock, 'x-clock', ['time', 'custom-date']); - -it('renders ok, updates on attr change', () => { - const root = document.createElement('div'); - const el = document.createElement('x-clock'); - el.setAttribute('time', '10:28:57 PM'); - - root.appendChild(el); - document.body.appendChild(root); - - assert.equal( - root.innerHTML, - '10:28:57 PM' - ); - - el.setAttribute('time', '11:01:10 AM'); - - assert.equal( - root.innerHTML, - '11:01:10 AM' - ); - - document.body.removeChild(root); -}); - -it('passes property changes to props', () => { - const root = document.createElement('div'); - const el = document.createElement('x-clock'); - - el.time = '10:28:57 PM'; - assert.equal(el.time, '10:28:57 PM'); - - root.appendChild(el); - document.body.appendChild(root); - assert.equal( - root.innerHTML, - '10:28:57 PM' - ); - - el.time = '11:01:10 AM'; - assert.equal(el.time, '11:01:10 AM'); - - assert.equal( - root.innerHTML, - '11:01:10 AM' - ); - - document.body.removeChild(root); -}); - -function DummyButton({ onClick, text = 'click' }) { - return ; -} - -registerElement(DummyButton, 'x-dummy-button', ['onClick', 'text']); - -it('passes simple properties changes to props', () => { - const root = document.createElement('div'); - const el = document.createElement('x-dummy-button'); - - el.text = 'foo'; - assert.equal(el.text, 'foo'); - - root.appendChild(el); - document.body.appendChild(root); - assert.equal( - root.innerHTML, - '' - ); + beforeEach(() => { + root = document.createElement('div'); + document.body.appendChild(root); + }); - // Update - el.text = 'bar'; - assert.equal( - root.innerHTML, - '' - ); + afterEach(() => { + document.body.removeChild(root); + }); - document.body.removeChild(root); -}); + function Clock({ time }) { + return {time}; + } -it('passes complex properties changes to props', () => { - const root = document.createElement('div'); - const el = document.createElement('x-dummy-button'); + registerElement(Clock, 'x-clock', ['time', 'custom-date']); - let clicks = 0; - const onClick = () => clicks++; - el.onClick = onClick; - assert.equal(el.onClick, onClick); + it('renders ok, updates on attr change', () => { + const el = document.createElement('x-clock'); + el.setAttribute('time', '10:28:57 PM'); - root.appendChild(el); - document.body.appendChild(root); - assert.equal( - root.innerHTML, - '' - ); + root.appendChild(el); + assert.equal( + root.innerHTML, + '10:28:57 PM' + ); - act(() => { - el.querySelector('button').click(); + el.setAttribute('time', '11:01:10 AM'); + assert.equal( + root.innerHTML, + '11:01:10 AM' + ); }); - assert.equal(clicks, 1); - // Update - let other = 0; - el.onClick = () => other++; - act(() => { - el.querySelector('button').click(); + describe('DOM properties', () => { + it('passes property changes to props', () => { + const el = document.createElement('x-clock'); + + el.time = '10:28:57 PM'; + assert.equal(el.time, '10:28:57 PM'); + + root.appendChild(el); + assert.equal( + root.innerHTML, + '10:28:57 PM' + ); + + el.time = '11:01:10 AM'; + assert.equal(el.time, '11:01:10 AM'); + + assert.equal( + root.innerHTML, + '11:01:10 AM' + ); + }); + + function DummyButton({ onClick, text = 'click' }) { + return ; + } + + registerElement(DummyButton, 'x-dummy-button', ['onClick', 'text']); + + it('passes simple properties changes to props', () => { + const el = document.createElement('x-dummy-button'); + + el.text = 'foo'; + assert.equal(el.text, 'foo'); + + root.appendChild(el); + assert.equal( + root.innerHTML, + '' + ); + + // Update + el.text = 'bar'; + assert.equal( + root.innerHTML, + '' + ); + }); + + it('passes complex properties changes to props', () => { + const el = document.createElement('x-dummy-button'); + + let clicks = 0; + const onClick = () => clicks++; + el.onClick = onClick; + assert.equal(el.onClick, onClick); + + root.appendChild(el); + assert.equal( + root.innerHTML, + '' + ); + + act(() => { + el.querySelector('button').click(); + }); + assert.equal(clicks, 1); + + // Update + let other = 0; + el.onClick = () => other++; + act(() => { + el.querySelector('button').click(); + }); + assert.equal(other, 1); + }); }); - assert.equal(other, 1); - document.body.removeChild(root); -}); - -function Foo({ text, children }) { - return ( - -
{children}
-
{text}
-
- ); -} - -registerElement(Foo, 'x-foo', [], { shadow: true }); - -it('renders slots as props with shadow DOM', () => { - const root = document.createElement('div'); - const el = document.createElement('x-foo'); - - // here is a slot - const slot = document.createElement('span'); - slot.textContent = 'here is a slot'; - slot.slot = 'text'; - el.appendChild(slot); - - //
no slot
- const noSlot = document.createElement('div'); - noSlot.textContent = 'no slot'; - el.appendChild(noSlot); - el.appendChild(slot); - - root.appendChild(el); - document.body.appendChild(root); - - assert.equal( - root.innerHTML, - '
no slot
here is a slot
' - ); - - const shadowHTML = document.querySelector('x-foo').shadowRoot.innerHTML; - assert.equal( - shadowHTML, - '
no slot
here is a slot
' - ); - - document.body.removeChild(root); -}); - -const kebabName = 'custom-date-long-name'; -const camelName = 'customDateLongName'; -const lowerName = camelName.toLowerCase(); -function PropNameTransform(props) { - return ( - - {props[kebabName]} {props[lowerName]} {props[camelName]} - - ); -} -registerElement(PropNameTransform, 'x-prop-name-transform', [ - kebabName, - camelName, -]); - -it('handles kebab-case attributes with passthrough', () => { - const root = document.createElement('div'); - const el = document.createElement('x-prop-name-transform'); - el.setAttribute(kebabName, '11/11/2011'); - el.setAttribute(camelName, 'pretended to be camel'); - - root.appendChild(el); - document.body.appendChild(root); - - assert.equal( - root.innerHTML, - `11/11/2011 pretended to be camel 11/11/2011` - ); - - el.setAttribute(kebabName, '01/01/2001'); - - assert.equal( - root.innerHTML, - `01/01/2001 pretended to be camel 01/01/2001` - ); - - document.body.removeChild(root); -}); + function Foo({ text, children }) { + return ( + +
{children}
+
{text}
+
+ ); + } + + registerElement(Foo, 'x-foo', [], { shadow: true }); + + it('renders slots as props with shadow DOM', () => { + const el = document.createElement('x-foo'); + + // here is a slot + const slot = document.createElement('span'); + slot.textContent = 'here is a slot'; + slot.slot = 'text'; + el.appendChild(slot); + + //
no slot
+ const noSlot = document.createElement('div'); + noSlot.textContent = 'no slot'; + el.appendChild(noSlot); + el.appendChild(slot); + + root.appendChild(el); + assert.equal( + root.innerHTML, + '
no slot
here is a slot
' + ); + + const shadowHTML = document.querySelector('x-foo').shadowRoot.innerHTML; + assert.equal( + shadowHTML, + '
no slot
here is a slot
' + ); + }); -const Theme = createContext('light'); + const kebabName = 'custom-date-long-name'; + const camelName = 'customDateLongName'; + const lowerName = camelName.toLowerCase(); + function PropNameTransform(props) { + return ( + + {props[kebabName]} {props[lowerName]} {props[camelName]} + + ); + } + registerElement(PropNameTransform, 'x-prop-name-transform', [ + kebabName, + camelName, + ]); + + it('handles kebab-case attributes with passthrough', () => { + const el = document.createElement('x-prop-name-transform'); + el.setAttribute(kebabName, '11/11/2011'); + el.setAttribute(camelName, 'pretended to be camel'); + + root.appendChild(el); + assert.equal( + root.innerHTML, + `11/11/2011 pretended to be camel 11/11/2011` + ); + + el.setAttribute(kebabName, '01/01/2001'); + assert.equal( + root.innerHTML, + `01/01/2001 pretended to be camel 01/01/2001` + ); + }); -function DisplayTheme() { - const theme = useContext(Theme); - return

Active theme: {theme}

; -} + const Theme = createContext('light'); -registerElement(DisplayTheme, 'x-display-theme', [], { shadow: true }); + function DisplayTheme() { + const theme = useContext(Theme); + return

Active theme: {theme}

; + } -function Parent({ children, theme = 'dark' }) { - return ( - -
{children}
-
- ); -} + registerElement(DisplayTheme, 'x-display-theme', [], { shadow: true }); -registerElement(Parent, 'x-parent', ['theme'], { shadow: true }); + function Parent({ children, theme = 'dark' }) { + return ( + +
{children}
+
+ ); + } -it('passes context over custom element boundaries', async () => { - const root = document.createElement('div'); - const el = document.createElement('x-parent'); + registerElement(Parent, 'x-parent', ['theme'], { shadow: true }); - const noSlot = document.createElement('x-display-theme'); - el.appendChild(noSlot); + it('passes context over custom element boundaries', async () => { + const el = document.createElement('x-parent'); - root.appendChild(el); - document.body.appendChild(root); + const noSlot = document.createElement('x-display-theme'); + el.appendChild(noSlot); - assert.equal( - root.innerHTML, - '' - ); + root.appendChild(el); + assert.equal( + root.innerHTML, + '' + ); - const getShadowHTML = () => - document.querySelector('x-display-theme').shadowRoot.innerHTML; - assert.equal(getShadowHTML(), '

Active theme: dark

'); + const getShadowHTML = () => + document.querySelector('x-display-theme').shadowRoot.innerHTML; + assert.equal(getShadowHTML(), '

Active theme: dark

'); - // Trigger context update - act(() => { - el.setAttribute('theme', 'sunny'); + // Trigger context update + act(() => { + el.setAttribute('theme', 'sunny'); + }); + assert.equal(getShadowHTML(), '

Active theme: sunny

'); }); - assert.equal(getShadowHTML(), '

Active theme: sunny

'); - - document.body.removeChild(root); });