Skip to content

Commit

Permalink
fix: Modal SSR
Browse files Browse the repository at this point in the history
Resolve Issue Semantic-Org#2259
  • Loading branch information
adam-26 committed Oct 26, 2017
1 parent 01b02f7 commit 003ad54
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/modules/Modal/Modal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ export interface ModalProps extends PortalProps {

/** Custom styles. */
style?: React.CSSProperties;

/** Element to be rendered in-place where the portal is defined. */
trigger?: React.ReactNode;
}

interface ModalComponent extends React.ComponentClass<ModalProps> {
Expand Down
17 changes: 13 additions & 4 deletions src/modules/Modal/Modal.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import cx from 'classnames'
import _ from 'lodash'
import PropTypes from 'prop-types'
import React from 'react'
import React, { isValidElement } from 'react'

import {
AutoControlledComponent as Component,
Expand Down Expand Up @@ -128,6 +128,9 @@ class Modal extends Component {
/** Custom styles. */
style: PropTypes.object,

/** Element to be rendered in-place where the portal is defined. */
trigger: PropTypes.node,

/**
* NOTE: Any unhandled props that are defined in Portal are passed-through
* to the wrapping Portal.
Expand Down Expand Up @@ -155,13 +158,16 @@ class Modal extends Component {
static Description = ModalDescription
static Actions = ModalActions

// expose for testing
static isBrowser = isBrowser

componentWillUnmount() {
debug('componentWillUnmount()')
this.handlePortalUnmount()
}

// Do not access document when server side rendering
getMountNode = () => (isBrowser ? this.props.mountNode || document.body : null)
getMountNode = () => (Modal.isBrowser ? this.props.mountNode || document.body : null)

handleActionsOverrides = predefinedProps => ({
onActionClick: (e, actionProps) => {
Expand Down Expand Up @@ -317,11 +323,13 @@ class Modal extends Component {

render() {
const { open } = this.state
const { closeOnDimmerClick, closeOnDocumentClick, dimmer, eventPool } = this.props
const { closeOnDimmerClick, closeOnDocumentClick, dimmer, eventPool, trigger } = this.props
const mountNode = this.getMountNode()

// Short circuit when server side rendering
if (!isBrowser) return null
if (!Modal.isBrowser) {
return isValidElement(trigger) ? trigger : null
}

const unhandled = getUnhandledProps(Modal, this.props)
const portalPropNames = Portal.handledProps
Expand Down Expand Up @@ -358,6 +366,7 @@ class Modal extends Component {
closeOnDocumentClick={closeOnDocumentClick}
closeOnRootNodeClick={closeOnDimmerClick}
{...portalProps}
trigger={trigger}
className={dimmerClasses}
eventPool={eventPool}
mountNode={mountNode}
Expand Down
25 changes: 25 additions & 0 deletions test/specs/modules/Modal/Modal-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react'
import ReactDOMServer from 'react-dom/server'

import Modal from 'src/modules/Modal/Modal'
import ModalHeader from 'src/modules/Modal/ModalHeader'
Expand Down Expand Up @@ -538,4 +539,28 @@ describe('Modal', () => {
})
})
})

describe('server-side', () => {
let modalIsBrowser

before(() => {
modalIsBrowser = Modal.isBrowser
Modal.isBrowser = false
})

after(() => {
Modal.isBrowser = modalIsBrowser
modalIsBrowser = null
})

it('renders empty content when trigger is not a valid component', () => {
const markup = ReactDOMServer.renderToStaticMarkup(<Modal />)
markup.should.equal('')
})

it('renders a valid trigger component', () => {
const markup = ReactDOMServer.renderToStaticMarkup(<Modal trigger={<div id='trigger' />} />)
markup.should.equal('<div id="trigger"></div>')
})
})
})

0 comments on commit 003ad54

Please sign in to comment.