Skip to content

Commit

Permalink
Add insertionPoint option in EmotionCache (#2521)
Browse files Browse the repository at this point in the history
* Add insertionPoint option in EmotionCache

* yarn changeset

* yarn lint

* Add tests as per view

* Updated changeset, improved tests

* Update packages/sheet/src/index.js

* Update packages/cache/__tests__/index.js

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

* Improve tests

* Use @testing-library

* Address comments from review

* Fix some flow issues

* fix selectors

* small tweaks

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
  • Loading branch information
mnajdova and Andarist committed Nov 3, 2021
1 parent 01e4e0f commit 516fe45
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 8 deletions.
31 changes: 31 additions & 0 deletions .changeset/sixty-balloons-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
'@emotion/cache': minor
'@emotion/sheet': minor
---

Add insertionPoint option to the EmotionCache, to insert rules after the specified element.

```jsx
const head = document.querySelector('head')

// <meta name="emotion-insertion-point" content="">
const emotionInsertionPoint = document.createElement('meta')
emotionInsertionPoint.setAttribute('name', 'emotion-insertion-point')
emotionInsertionPoint.setAttribute('content', '')

head.appendChild(emotionInsertionPoint)

// the emotion sheets should be inserted right after the meta tag
const cache = createCache({
key: 'my-app',
insertionPoint: emotionInsertionPoint
})

function App() {
return (
<CacheProvider value={cache}>
<Main />
</CacheProvider>
)
}
```
24 changes: 24 additions & 0 deletions packages/cache/__tests__/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should accept insertionPoint option 1`] = `
<head>
<style
id="first"
/>
<style
data-emotion="test-insertion-point"
data-s=""
>
.test-insertion-point-83n355{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;color:blue;}
</style>
<style
id="last"
/>
</head>
`;

exports[`throws correct error with invalid key 1`] = `"Emotion key must only contain lower case alphabetical characters and - but \\".\\" was passed"`;
28 changes: 28 additions & 0 deletions packages/cache/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
// @flow
/** @jsx jsx */
import 'test-utils/next-env'
import { safeQuerySelector } from 'test-utils'
import createCache from '@emotion/cache'
import { jsx, CacheProvider } from '@emotion/react'
import { render } from '@testing-library/react'

test('throws correct error with invalid key', () => {
expect(() => {
createCache({ key: '.' })
}).toThrowErrorMatchingSnapshot()
})

it('should accept insertionPoint option', () => {
const head = safeQuerySelector('head')

head.innerHTML = `
<style id="first"></style>
<style id="last"></style>
`

// the sheet should be inserted between the first and last style nodes
const cache = createCache({
key: 'test-insertion-point',
insertionPoint: safeQuerySelector('#first')
})

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

expect(document.head).toMatchSnapshot()
})
6 changes: 4 additions & 2 deletions packages/cache/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export type Options = {
key: string,
container?: HTMLElement,
speedy?: boolean,
prepend?: boolean
prepend?: boolean,
insertionPoint?: HTMLElement
}

let getServerStylisCache = isBrowser
Expand Down Expand Up @@ -252,7 +253,8 @@ let createCache = (options: Options): EmotionCache => {
container: ((container: any): HTMLElement),
nonce: options.nonce,
speedy: options.speedy,
prepend: options.prepend
prepend: options.prepend,
insertionPoint: options.insertionPoint
}),
nonce: options.nonce,
inserted,
Expand Down
2 changes: 2 additions & 0 deletions packages/cache/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ export interface Options {
key: string
container?: HTMLElement
speedy?: boolean
/** @deprecate use `insertionPoint` instead */
prepend?: boolean
insertionPoint?: HTMLElement
}

export default function createCache(options: Options): EmotionCache
31 changes: 31 additions & 0 deletions packages/sheet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,39 @@ This defines how rules are inserted. If it is true, rules will be inserted with
#### prepend
**Deprecated:** Please use `insertionPoint` option instead.
This defines where rules are inserted into the `container`. By default they are appended but this can be changed by using `prepend: true` option.
#### insertionPoint
This defines specific dom node after which the rules are inserted into the `container`. You can use a `meta` tag to specify the specific location:
```jsx
const head = document.querySelector('head')

// <meta name="emotion-insertion-point" content="">
const emotionInsertionPoint = document.createElement('meta')
emotionInsertionPoint.setAttribute('name', 'emotion-insertion-point')
emotionInsertionPoint.setAttribute('content', '')

head.appendChild(emotionInsertionPoint)

// the emotion sheets should be inserted right after the meta tag
const cache = createCache({
key: 'my-app',
insertionPoint: emotionInsertionPoint
})

function App() {
return (
<CacheProvider value={cache}>
<Main />
</CacheProvider>
)
}
```

### Methods

#### insert
Expand Down
59 changes: 59 additions & 0 deletions packages/sheet/__tests__/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`StyleSheet should accept insertionPoint option 1`] = `
<html>
<head>
<style
id="first"
/>
<style
data-emotion=""
data-s=""
>
html { color: hotpink; }
</style>
<style
data-emotion=""
data-s=""
>
* { box-sizing: border-box; }
</style>
<style
id="last"
/>
</head>
<body />
</html>
`;

exports[`StyleSheet should accept prepend option 1`] = `
<html>
<head>
Expand Down Expand Up @@ -212,3 +246,28 @@ exports[`StyleSheet should use the container option instead of document.head to
</body>
</html>
`;

exports[`StyleSheet should work if insertionPoint is last element 1`] = `
<html>
<head>
<style
id="last"
/>
<style
data-emotion=""
data-s=""
>
html { color: hotpink; }
</style>
<style
data-emotion=""
data-s=""
>
* { box-sizing: border-box; }
</style>
</head>
<body />
</html>
`;
47 changes: 43 additions & 4 deletions packages/sheet/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ afterEach(() => {
jest.clearAllMocks()
})

beforeEach(() => {
safeQuerySelector('head').innerHTML = ''
safeQuerySelector('body').innerHTML = ''
})

describe('StyleSheet', () => {
it('should be speedy by default in production', () => {
process.env.NODE_ENV = 'production'
Expand Down Expand Up @@ -98,8 +103,6 @@ describe('StyleSheet', () => {
expect(sheet.tags).toHaveLength(1)
expect(sheet.tags[0].parentNode).toBe(container)
sheet.flush()
// $FlowFixMe
document.body.removeChild(container)
})

it('should accept prepend option', () => {
Expand All @@ -114,7 +117,44 @@ describe('StyleSheet', () => {
expect(document.documentElement).toMatchSnapshot()

sheet.flush()
head.removeChild(otherStyle)
})

it('should accept insertionPoint option', () => {
const head = safeQuerySelector('head')

head.innerHTML = `
<style id="first"></style>
<style id="last"></style>
`

// the sheet should be inserted between the first and last style nodes
const sheet = new StyleSheet({
...defaultOptions,
insertionPoint: safeQuerySelector('#first')
})
sheet.insert(rule)
sheet.insert(rule2)
expect(document.documentElement).toMatchSnapshot()

sheet.flush()
})

it('should work if insertionPoint is last element', () => {
const head = safeQuerySelector('head')
const lastStyle = document.createElement('style')
lastStyle.setAttribute('id', 'last')
head.appendChild(lastStyle)

// the sheet should be inserted after the first node
const sheet = new StyleSheet({
...defaultOptions,
insertionPoint: lastStyle
})
sheet.insert(rule)
sheet.insert(rule2)
expect(document.documentElement).toMatchSnapshot()

sheet.flush()
})

it('should be able to hydrate styles', () => {
Expand Down Expand Up @@ -179,7 +219,6 @@ describe('StyleSheet', () => {
expect(document.documentElement).toMatchSnapshot()

sheet.flush()
head.removeChild(otherStyle)
})

it('should not crash when flushing when styles are already detached', () => {
Expand Down
13 changes: 11 additions & 2 deletions packages/sheet/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export type Options = {
key: string,
container: HTMLElement,
speedy?: boolean,
prepend?: boolean
prepend?: boolean,
insertionPoint?: HTMLElement
}

function createStyleElement(options: {
Expand All @@ -70,6 +71,7 @@ export class StyleSheet {
nonce: string | void
prepend: boolean | void
before: Element | null
insertionPoint: HTMLElement | void
constructor(options: Options) {
this.isSpeedy =
options.speedy === undefined
Expand All @@ -82,13 +84,20 @@ export class StyleSheet {
this.key = options.key
this.container = options.container
this.prepend = options.prepend
this.insertionPoint = options.insertionPoint
this.before = null
}

_insertTag = (tag: HTMLStyleElement) => {
let before
if (this.tags.length === 0) {
before = this.prepend ? this.container.firstChild : this.before
if (this.insertionPoint) {
before = this.insertionPoint.nextSibling
} else if (this.prepend) {
before = this.container.firstChild
} else {
before = this.before
}
} else {
before = this.tags[this.tags.length - 1].nextSibling
}
Expand Down
2 changes: 2 additions & 0 deletions packages/sheet/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export interface Options {
key: string
container: HTMLElement
speedy?: boolean
/** @deprecate use `insertionPoint` instead */
prepend?: boolean
insertionPoint?: HTMLElement
}

export class StyleSheet {
Expand Down

0 comments on commit 516fe45

Please sign in to comment.