Skip to content

Commit

Permalink
Allow any Node as container (#2728)
Browse files Browse the repository at this point in the history
* Allow any Node, including ShadowRoot, as container

* Add changeset

* Update container types in documentation to `Node`

* Update packages/sheet/src/index.js

Co-authored-by: Sam Magura <srmagura@gmail.com>

* Update .changeset/five-balloons-sneeze.md

Co-authored-by: Sam Magura <srmagura@gmail.com>

* remove a trailing whitespace

* `before` should be a `ChildNode`

Since `Node['firstChild']` is `ChildNode`, not `Element`.

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>

* Add `.d.ts` test

* tweak changeset

Co-authored-by: Sam Magura <srmagura@gmail.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
  • Loading branch information
3 people committed Apr 28, 2022
1 parent bf0a262 commit 6c2d7a6
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 11 deletions.
6 changes: 6 additions & 0 deletions .changeset/five-balloons-sneeze.md
@@ -0,0 +1,6 @@
---
'@emotion/cache': patch
'@emotion/sheet': patch
---

TypeScript type for the `container` option has been adjusted. It will now accept a `ShadowRoot`, or any other kind of `Node`.
2 changes: 1 addition & 1 deletion packages/cache/README.md
Expand Up @@ -51,7 +51,7 @@ The prefix before class names. It will also be set as the value of the `data-emo

### `container`

`HTMLElement`
`Node`

A DOM node that emotion will insert all of its style tags into. This is useful for inserting styles into iframes or windows.

Expand Down
33 changes: 33 additions & 0 deletions packages/cache/__tests__/__snapshots__/index.js.snap
@@ -1,5 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should accept container option 1`] = `
.emotion-0 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
color: blue;
}
<body>
<div
id="container"
>
<style
data-emotion="test-container"
data-s=""
>
.emotion-0{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;color:blue;}
</style>
</div>
<div>
<div
class="emotion-0"
/>
</div>
</body>
`;

exports[`should accept insertionPoint option 1`] = `
<head>
Expand Down
21 changes: 21 additions & 0 deletions packages/cache/__tests__/index.js
Expand Up @@ -34,3 +34,24 @@ it('should accept insertionPoint option', () => {

expect(document.head).toMatchSnapshot()
})

it('should accept container option', () => {
const body = safeQuerySelector('body')

body.innerHTML = `
<div id="container" />
`

const cache = createCache({
key: 'test-container',
container: safeQuerySelector('#container')
})

render(
<CacheProvider value={cache}>
<div css={{ display: 'flex', color: 'blue' }} />
</CacheProvider>
)

expect(document.body).toMatchSnapshot()
})
5 changes: 2 additions & 3 deletions packages/cache/src/index.js
Expand Up @@ -92,8 +92,7 @@ let createCache = (options: Options): EmotionCache => {
}
}
let inserted = {}
// $FlowFixMe
let container: HTMLElement
let container: Node
const nodesToHydrate = []
if (isBrowser) {
container = options.container || ((document.head: any): HTMLHeadElement)
Expand Down Expand Up @@ -250,7 +249,7 @@ let createCache = (options: Options): EmotionCache => {
key,
sheet: new StyleSheet({
key,
container: ((container: any): HTMLElement),
container: ((container: any): Node),
nonce: options.nonce,
speedy: options.speedy,
prepend: options.prepend,
Expand Down
2 changes: 1 addition & 1 deletion packages/cache/types/index.d.ts
Expand Up @@ -34,7 +34,7 @@ export interface Options {
nonce?: string
stylisPlugins?: Array<StylisPlugin>
key: string
container?: HTMLElement
container?: Node
speedy?: boolean
/** @deprecate use `insertionPoint` instead */
prepend?: boolean
Expand Down
2 changes: 1 addition & 1 deletion packages/sheet/README.md
Expand Up @@ -25,7 +25,7 @@ sheet.insert('html { color: hotpink; }')
type Options = {
nonce?: string
key: string
container: HTMLElement
container: Node
speedy?: boolean
prepend?: boolean
}
Expand Down
21 changes: 21 additions & 0 deletions packages/sheet/__tests__/__snapshots__/index.js.snap
Expand Up @@ -271,3 +271,24 @@ exports[`StyleSheet should work if insertionPoint is last element 1`] = `
<body />
</html>
`;

exports[`StyleSheet should work with a ShadowRoot container 1`] = `
<html>
<head />
<body>
<div />
</body>
</html>
`;

exports[`StyleSheet should work with a ShadowRoot container 2`] = `
HTMLCollection [
<style
data-emotion=""
data-s=""
>
html { color: hotpink; }
</style>,
]
`;
17 changes: 17 additions & 0 deletions packages/sheet/__tests__/index.js
Expand Up @@ -105,6 +105,23 @@ describe('StyleSheet', () => {
sheet.flush()
})

it('should work with a ShadowRoot container', () => {
const div = document.createElement('div')
// $FlowFixMe
document.body.appendChild(div)
const container = div.attachShadow({ mode: 'open' })
const sheet = new StyleSheet({ ...defaultOptions, container })
expect(sheet.container).toBe(container)
sheet.insert(rule)
expect(document.documentElement).toMatchSnapshot()
// The shadowRoot is not serialized in the snapshot, so we need to take a
// separate snapshot of the shadowRoot's children.
expect(container.children).toMatchSnapshot()
expect(sheet.tags).toHaveLength(1)
expect(sheet.tags[0].parentNode).toBe(container)
sheet.flush()
})

it('should accept prepend option', () => {
const head = safeQuerySelector('head')
const otherStyle = document.createElement('style')
Expand Down
5 changes: 3 additions & 2 deletions packages/sheet/src/index.js
Expand Up @@ -42,7 +42,7 @@ function sheetForTag(tag: HTMLStyleElement): CSSStyleSheet {
export type Options = {
nonce?: string,
key: string,
container: HTMLElement,
container: Node,
speedy?: boolean,
prepend?: boolean,
insertionPoint?: HTMLElement
Expand All @@ -66,7 +66,8 @@ export class StyleSheet {
isSpeedy: boolean
ctr: number
tags: HTMLStyleElement[]
container: HTMLElement
// Using Node instead of HTMLElement since container may be a ShadowRoot
container: Node
key: string
nonce: string | void
prepend: boolean | void
Expand Down
6 changes: 3 additions & 3 deletions packages/sheet/types/index.d.ts
Expand Up @@ -4,7 +4,7 @@
export interface Options {
nonce?: string
key: string
container: HTMLElement
container: Node
speedy?: boolean
/** @deprecate use `insertionPoint` instead */
prepend?: boolean
Expand All @@ -15,10 +15,10 @@ export class StyleSheet {
isSpeedy: boolean
ctr: number
tags: Array<HTMLStyleElement>
container: HTMLElement
container: Node
key: string
nonce?: string
before?: Element | null
before?: ChildNode | null
constructor(options?: Options)
insert(rule: string): void
flush(): void
Expand Down
6 changes: 6 additions & 0 deletions packages/sheet/types/tests.ts
Expand Up @@ -51,3 +51,9 @@ styleSheet.flush()
styleSheet.flush(undefined as any)
// $ExpectError
styleSheet.flush(...(undefined as any as Array<any>))

const shadowRoot = document.createElement('div').attachShadow({ mode: 'open' })
const shadowStyleSheet = new StyleSheet({
key: 'abc',
container: shadowRoot
})

0 comments on commit 6c2d7a6

Please sign in to comment.