Skip to content

Commit

Permalink
Merge pull request #305 from marp-team/improved-browser-interface
Browse files Browse the repository at this point in the history
Update the rendered DOMs for custom elements whenever called browser script
  • Loading branch information
yhatt authored Jun 5, 2022
2 parents abc4e2e + a7aaa19 commit 8da56f5
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 13 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

### Fixed

- Apply hydration for custom elements whenever calling browser script ([#305](https://github.com/marp-team/marp-core/pull/305))

## v3.2.0 - 2022-05-21

### Changed
Expand Down
31 changes: 25 additions & 6 deletions src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,43 @@ export { observer }

const marpCoreBrowserScript = Symbol()

export const browser = (target: ParentNode = document): (() => void) => {
export interface MarpCoreBrowser {
/** Clean-up observer for applying polyfills. */
(): void

/** Clean-up observer for applying polyfills. */
cleanup: () => void

/**
* Update DOMs to apply custom elements for Marp Core. It should call whenever
* rendered DOMs rendered by Marp.
*
* It has exactly same meaning to call `browser()` with the same arguments
* again.
*/
update: () => MarpCoreBrowser
}

export const browser = (target: ParentNode = document): MarpCoreBrowser => {
if (typeof window === 'undefined') {
throw new Error(
"Marp Core's browser script is valid only in browser context."
)
}

if (target[marpCoreBrowserScript]) return target[marpCoreBrowserScript]

// ---
applyCustomElements(target) // Should call in every update of Marp rendering

applyCustomElements(target)
if (target[marpCoreBrowserScript]) return target[marpCoreBrowserScript]

const cleanupObserver = observer({ target })
const cleanup = () => {
const cleanupFn = () => {
cleanupObserver()
delete target[marpCoreBrowserScript]
}
const cleanup = Object.assign(cleanupFn, {
cleanup: cleanupFn,
update: () => browser(target),
})

Object.defineProperty(target, marpCoreBrowserScript, {
configurable: true,
Expand Down
36 changes: 29 additions & 7 deletions test/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,56 @@ beforeEach(() => jest.clearAllMocks())
afterEach(() => jest.restoreAllMocks())

describe('Browser script', () => {
it('executes observers for polyfill and fitting', () => {
it('executes polyfill observer and set-up for custom elements', () => {
const spy = jest.spyOn(window, 'requestAnimationFrame')

const cleanup = browser()
const browserInterface = browser()
expect(spy).toHaveBeenCalledTimes(1)
expect(polyfill).toHaveBeenCalledTimes(1)
expect(applyCustomElements).toHaveBeenCalledTimes(1)
expect(browser()).toStrictEqual(cleanup)
expect(browser()).toStrictEqual(browserInterface)
expect(browserInterface).toStrictEqual(expect.any(Function))
expect(browserInterface).toStrictEqual(browserInterface.cleanup)

const rafFunc = spy.mock.calls[0][0]
rafFunc(performance.now())

expect(spy).toHaveBeenCalledTimes(2)
expect(polyfill).toHaveBeenCalledTimes(2)

cleanup()
browserInterface.cleanup()
rafFunc(performance.now())
expect(spy).toHaveBeenCalledTimes(2) // No more calling function after cleanup
})

describe('with passed shadow root', () => {
it('calls polyfills and fitting observer with specific target', () => {
it('calls polyfill observer and custom elements set-up with specific target', () => {
const root = document.createElement('div').attachShadow({ mode: 'open' })
const cleanup = browser(root)
const browserInterface = browser(root)

expect(polyfill).toHaveBeenCalledWith({ target: root })
expect(applyCustomElements).toHaveBeenCalledWith(root)
cleanup()
browserInterface.cleanup()
})
})

describe('#update', () => {
it('calls itself again to update custom element DOMs', () => {
const browserInterface = browser()

expect(applyCustomElements).toHaveBeenCalledTimes(1)
expect(browserInterface.update()).toStrictEqual(browserInterface)
expect(applyCustomElements).toHaveBeenCalledTimes(2)
browserInterface.cleanup()

// For shadow root
const root = document.createElement('div').attachShadow({ mode: 'open' })
const interfaceShadowRoot = browser(root)

expect(applyCustomElements).toHaveBeenNthCalledWith(3, root)
expect(interfaceShadowRoot.update()).toStrictEqual(interfaceShadowRoot)
expect(applyCustomElements).toHaveBeenNthCalledWith(4, root)
interfaceShadowRoot.cleanup()
})
})
})
Expand Down

0 comments on commit 8da56f5

Please sign in to comment.