Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(package): do not access window/document in ssr #806

Merged
merged 7 commits into from
Nov 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/addons/Portal/Portal.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
AutoControlledComponent as Component,
customPropTypes,
keyboardKey,
isBrowser,
makeDebugger,
META,
} from '../../lib'
Expand Down Expand Up @@ -112,6 +113,7 @@ class Portal extends Component {
closeOnDocumentClick: true,
closeOnEscape: true,
openOnTriggerClick: true,
mountNode: isBrowser ? document.body : null,
}

static autoControlledProps = [
Expand Down Expand Up @@ -322,6 +324,9 @@ class Portal extends Component {

this.mountPortal()

// Server side rendering
if (!isBrowser) return null

this.node.className = className || ''

ReactDOM.unstable_renderSubtreeIntoContainer(
Expand All @@ -337,11 +342,11 @@ class Portal extends Component {
}

mountPortal = () => {
if (this.node) return
if (!isBrowser || this.node) return

debug('mountPortal()')

const { mountNode = document.body, prepend } = this.props
const { mountNode, prepend } = this.props

this.node = document.createElement('div')

Expand All @@ -359,7 +364,7 @@ class Portal extends Component {
}

unmountPortal = () => {
if (!this.node) return
if (!isBrowser || !this.node) return

debug('unmountPortal()')

Expand Down
1 change: 1 addition & 0 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
export * from './factories'
export { default as getUnhandledProps } from './getUnhandledProps'
export { default as getElementType } from './getElementType'
export { default as isBrowser } from './isBrowser'
export * as META from './META'
export * as SUI from './SUI'

Expand Down
4 changes: 4 additions & 0 deletions src/lib/isBrowser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const hasDocument = typeof document === 'object'
const hasWindow = typeof window === 'object' || window.self === window

export default hasDocument && hasWindow
19 changes: 12 additions & 7 deletions src/modules/Dimmer/Dimmer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
customPropTypes,
getElementType,
getUnhandledProps,
isBrowser,
META,
useKeyOnly,
} from '../../lib'
Expand Down Expand Up @@ -60,9 +61,13 @@ export default class Dimmer extends Component {

static Dimmable = DimmerDimmable

handlePortalMount = () => document.body.classList.add('dimmed', 'dimmable')
handlePortalMount = () => {
if (isBrowser) document.body.classList.add('dimmed', 'dimmable')
}

handlePortalUnmount = () => document.body.classList.remove('dimmed', 'dimmable')
handlePortalUnmount = () => {
if (isBrowser) document.body.classList.remove('dimmed', 'dimmable')
}

handleClick = (e) => {
const { onClick, onClickOutside } = this.props
Expand Down Expand Up @@ -98,12 +103,12 @@ export default class Dimmer extends Component {
const ElementType = getElementType(Dimmer, this.props)

const childrenJSX = (children || content) && (
<div className='content'>
<div className='center' ref={center => (this.center = center)}>
{ children || content }
<div className='content'>
<div className='center' ref={center => (this.center = center)}>
{ children || content }
</div>
</div>
</div>
)
)

if (page) {
return (
Expand Down
14 changes: 14 additions & 0 deletions src/modules/Dropdown/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
customPropTypes,
getElementType,
getUnhandledProps,
isBrowser,
keyboardKey,
makeDebugger,
META,
Expand Down Expand Up @@ -306,6 +307,9 @@ export default class Dropdown extends Component {
// TODO objectDiff still runs in prod, stop it
debug('to state:', objectDiff(prevState, this.state))

// Do not access document when server side rendering
if (!isBrowser) return

// focused / blurred
if (!prevState.focus && this.state.focus) {
debug('dropdown focused')
Expand Down Expand Up @@ -357,6 +361,10 @@ export default class Dropdown extends Component {

componentWillUnmount() {
debug('componentWillUnmount()')

// Do not access document when server side rendering
if (!isBrowser) return

document.removeEventListener('keydown', this.openOnArrow)
document.removeEventListener('keydown', this.openOnSpace)
document.removeEventListener('keydown', this.moveSelectionOnKeyDown)
Expand Down Expand Up @@ -495,12 +503,16 @@ export default class Dropdown extends Component {
const { onMouseDown } = this.props
if (onMouseDown) onMouseDown(e)
this.isMouseDown = true
// Do not access document when server side rendering
if (!isBrowser) return
document.addEventListener('mouseup', this.handleDocumentMouseUp)
}

handleDocumentMouseUp = () => {
debug('handleDocumentMouseUp()')
this.isMouseDown = false
// Do not access document when server side rendering
if (!isBrowser) return
document.removeEventListener('mouseup', this.handleDocumentMouseUp)
}

Expand Down Expand Up @@ -757,6 +769,8 @@ export default class Dropdown extends Component {

scrollSelectedItemIntoView = () => {
debug('scrollSelectedItemIntoView()')
// Do not access document when server side rendering
if (!isBrowser) return
const menu = document.querySelector('.ui.dropdown.active.visible .menu.visible')
const item = menu.querySelector('.item.selected')
debug(`menu: ${menu}`)
Expand Down
22 changes: 12 additions & 10 deletions src/modules/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
customPropTypes,
getElementType,
getUnhandledProps,
isBrowser,
makeDebugger,
META,
useKeyOnly,
Expand Down Expand Up @@ -79,6 +80,8 @@ class Modal extends Component {

static defaultProps = {
dimmer: true,
// Do not access document when server side rendering
mountNode: isBrowser ? document.body : null,
}

static _meta = _meta
Expand Down Expand Up @@ -106,8 +109,7 @@ class Modal extends Component {

handlePortalMount = (e) => {
debug('handlePortalMount()')
const { dimmer } = this.props
const mountNode = this.getMountNode()
const { dimmer, mountNode } = this.props

if (dimmer) {
debug('adding dimmer')
Expand All @@ -131,7 +133,7 @@ class Modal extends Component {
// Always remove all dimmer classes.
// If the dimmer value changes while the modal is open, then removing its
// current value could leave cruft classes previously added.
const mountNode = this.getMountNode()
const { mountNode } = this.props
mountNode.classList.remove('blurring', 'dimmable', 'dimmed', 'scrollable')

cancelAnimationFrame(this.animationRequestId)
Expand All @@ -140,13 +142,9 @@ class Modal extends Component {
if (onUnmount) onUnmount(e, this.props)
}

getMountNode = () => {
return this.props.mountNode || document.body
}

setPosition = () => {
if (this._modalNode) {
const mountNode = this.getMountNode()
const { mountNode } = this.props
const { height } = this._modalNode.getBoundingClientRect()
const scrolling = height >= window.innerHeight

Expand All @@ -171,7 +169,11 @@ class Modal extends Component {
}

render() {
const { basic, children, className, dimmer, size } = this.props
const { basic, children, className, dimmer, mountNode, size } = this.props

// Short circuit when server side rendering
if (!isBrowser) return null

const { marginTop, scrolling } = this.state
const classes = cx(
'ui',
Expand Down Expand Up @@ -218,7 +220,7 @@ class Modal extends Component {
closeOnDocumentClick={false}
{...portalProps}
className={dimmerClasses}
mountNode={this.getMountNode()}
mountNode={mountNode}
onClose={this.handleClose}
onMount={this.handlePortalMount}
onOpen={this.handleOpen}
Expand Down
5 changes: 5 additions & 0 deletions src/modules/Popup/Popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import _ from 'lodash'
import {
getElementType,
getUnhandledProps,
isBrowser,
META,
SUI,
useKeyOnly,
Expand Down Expand Up @@ -117,6 +118,10 @@ export default class Popup extends Component {

computePopupStyle(positions) {
const style = { position: 'absolute' }

// Do not access window/document when server side rendering
if (!isBrowser) return style

const { offset } = this.props
const { pageYOffset, pageXOffset } = window
const { clientWidth, clientHeight } = document.documentElement
Expand Down
18 changes: 16 additions & 2 deletions src/modules/Search/Search.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
customPropTypes,
getElementType,
getUnhandledProps,
isBrowser,
keyboardKey,
makeDebugger,
META,
Expand Down Expand Up @@ -191,6 +192,9 @@ export default class Search extends Component {
// TODO objectDiff still runs in prod, stop it
debug('to state:', objectDiff(prevState, this.state))

// Do not access document when server side rendering
if (!isBrowser) return

// focused / blurred
if (!prevState.focus && this.state.focus) {
debug('search focused')
Expand Down Expand Up @@ -232,6 +236,10 @@ export default class Search extends Component {

componentWillUnmount() {
debug('componentWillUnmount()')

// Do not access document when server side rendering
if (!isBrowser) return

document.removeEventListener('keydown', this.moveSelectionOnKeyDown)
document.removeEventListener('keydown', this.selectItemOnEnter)
document.removeEventListener('keydown', this.closeOnEscape)
Expand Down Expand Up @@ -306,12 +314,16 @@ export default class Search extends Component {
const { onMouseDown } = this.props
if (onMouseDown) onMouseDown(e)
this.isMouseDown = true
// Do not access document when server side rendering
if (!isBrowser) return
document.addEventListener('mouseup', this.handleDocumentMouseUp)
}

handleDocumentMouseUp = () => {
debug('handleDocumentMouseUp()')
this.isMouseDown = false
// Do not access document when server side rendering
if (!isBrowser) return
document.removeEventListener('mouseup', this.handleDocumentMouseUp)
}

Expand Down Expand Up @@ -431,6 +443,8 @@ export default class Search extends Component {

scrollSelectedItemIntoView = () => {
debug('scrollSelectedItemIntoView()')
// Do not access document when server side rendering
if (!isBrowser) return
const menu = document.querySelector('.ui.search.active.visible .results.visible')
const item = menu.querySelector('.result.active')
debug(`menu (results): ${menu}`)
Expand Down Expand Up @@ -493,9 +507,9 @@ export default class Search extends Component {
return (
<div className='message empty'>
<div className='header'>{noResultsMessage}</div>
{noResultsDescription &&
{noResultsDescription && (
<div className='description'>{noResultsDescription}</div>
}
)}
</div>
)
}
Expand Down