diff --git a/docs/generate-metadata.js b/docs/generate-metadata.js index 4cbef95619..299f702335 100644 --- a/docs/generate-metadata.js +++ b/docs/generate-metadata.js @@ -26,7 +26,7 @@ let isLiteral = str => (/^('|")/).test(str.trim()); * @param {ComponentMetadata|PropMetadata} obj */ function parseDoclets(obj){ - obj.doclets = metadata.parseDoclets(obj.desc || ''); + obj.doclets = metadata.parseDoclets(obj.desc || '') || {}; obj.desc = cleanDoclets(obj.desc || ''); obj.descHtml = marked(obj.desc || ''); } diff --git a/docs/src/PropTable.js b/docs/src/PropTable.js index 9d585145c0..18259b034b 100644 --- a/docs/src/PropTable.js +++ b/docs/src/PropTable.js @@ -5,7 +5,29 @@ import Label from '../../src/Label'; import Table from '../../src/Table'; -let cleanDocletValue = str => str.replace(/^\{|\}$/g, ''); +let cleanDocletValue = str => str.trim().replace(/^\{/, '').replace(/\}$/, ''); + +function getPropsData(componentData, metadata){ + + let props = componentData.props || {}; + + if (componentData.composes) { + componentData.composes.forEach( other => { + props = merge({}, getPropsData(metadata[other] || {}, metadata), props); + + }); + } + + if (componentData.mixins) { + componentData.mixins.forEach( other => { + if ( componentData.composes.indexOf(other) === -1) { + props = merge({}, getPropsData(metadata[other] || {}, metadata), props); + } + }); + } + + return props; +} const PropTable = React.createClass({ @@ -13,10 +35,16 @@ const PropTable = React.createClass({ metadata: React.PropTypes.object }, + componentWillMount(){ + let componentData = this.context.metadata[this.props.component] || {}; + + this.propsData = getPropsData(componentData, this.context.metadata); + }, + render(){ - let metadata = this.context.metadata[this.props.component] || {}; + let propsData = this.propsData; - if ( !Object.keys(metadata.props || {}).length){ + if ( !Object.keys(propsData).length){ return ; } @@ -31,46 +59,36 @@ const PropTable = React.createClass({ - { this._renderRows() } + { this._renderRows(propsData) } ); }, - _renderRows(){ - let metadata = this.context.metadata[this.props.component] || {}; - let props = metadata.props || {}; + _renderRows(propsData){ - if (metadata.composes) { - metadata.composes.forEach( other => { - props = merge(props, (this.context.metadata[other] || {}).props); - }); - } - - if (metadata.mixins) { - metadata.mixins.forEach( other => { - if ( metadata.composes.indexOf(other) === -1) { - props = merge(props, (this.context.metadata[other] || {}).props); - } - }); - } - - return Object.keys(props) + return Object.keys(propsData) .sort() - .filter(propName => props[propName].type && !props[propName].doclets.private ) + .filter(propName => propsData[propName].type && !propsData[propName].doclets.private ) .map(propName => { - let prop = props[propName]; + let propData = propsData[propName]; return ( - {propName} {this.renderRequiredLabel(prop)} + {propName} {this.renderRequiredLabel(propData)} -
{this.getType(prop)}
+
{this.getType(propData)}
+ + {propData.defaultValue} + + + { propData.doclets.deprecated + &&
{'Deprecated: ' + propData.doclets.deprecated + ' '}
+ } +
- {prop.defaultValue} - ); }); @@ -87,7 +105,7 @@ const PropTable = React.createClass({ }, getType(prop) { - let type = prop.type; + let type = prop.type || {}; let name = this.getDisplayTypeName(type.name); let doclets = prop.doclets || {}; @@ -95,9 +113,21 @@ const PropTable = React.createClass({ case 'object': return name; case 'union': - return type.value.map(val => this.getType({ type: val })).join(' | '); + return type.value.reduce((current, val, i, list) => { + let item = this.getType({ type: val }); + + if (React.isValidElement(item)) { + item = React.cloneElement(item, {key: i}); + } + + current = current.concat(item); + + return i === (list.length - 1) ? current : current.concat(' | '); + }, []); case 'array': - return `array<${this.getDisplayTypeName(type.value.name)}>`; + let child = this.getType({ type: type.value }); + + return {'array<'}{ child }{'>'}; case 'enum': return this.renderEnum(type); case 'custom': diff --git a/package.json b/package.json index bf7b11898e..a9c9dc9d62 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "phantomjs": "^1.9.17", "portfinder": "^0.4.0", "react": "^0.13.1", - "react-component-metadata": "^1.1.1", + "react-component-metadata": "^1.2.1", "react-hot-loader": "^1.2.7", "react-router": "^0.13.1", "rimraf": "^2.3.2", diff --git a/src/ModalTrigger.js b/src/ModalTrigger.js index 7b1eb73255..9e385e4d69 100644 --- a/src/ModalTrigger.js +++ b/src/ModalTrigger.js @@ -1,11 +1,20 @@ import React, { cloneElement } from 'react'; -import OverlayMixin from './OverlayMixin'; + +import deprecationWarning from './utils/deprecationWarning'; import createChainedFunction from './utils/createChainedFunction'; import createContextWrapper from './utils/createContextWrapper'; +function createHideDepreciationWrapper(hide){ + return function(...args){ + deprecationWarning( + 'The Modal prop `onRequestHide`', 'the `onHide` prop'); + + return hide(...args); + }; +} + const ModalTrigger = React.createClass({ - mixins: [OverlayMixin], propTypes: { modal: React.PropTypes.node.isRequired, @@ -39,15 +48,31 @@ const ModalTrigger = React.createClass({ }); }, - renderOverlay() { - if (!this.state.isOverlayShown) { - return ; - } + componentDidMount(){ + this._overlay = document.createElement('div'); + React.render(this.getOverlay(), this._overlay); + }, + + componentWillUnmount() { + React.unmountComponentAtNode(this._overlay); + this._overlay = null; + clearTimeout(this._hoverDelay); + }, + + componentDidUpdate(){ + React.render(this.getOverlay(), this._overlay); + }, + + getOverlay() { + let modal = this.props.modal; return cloneElement( - this.props.modal, + modal, { - onRequestHide: this.hide + show: this.state.isOverlayShown, + onHide: this.hide, + onRequestHide: createHideDepreciationWrapper(this.hide), + container: modal.props.container || this.props.container } ); }, @@ -82,4 +107,19 @@ const ModalTrigger = React.createClass({ */ ModalTrigger.withContext = createContextWrapper(ModalTrigger, 'modal'); -export default ModalTrigger; +let DepreciatedModalTrigger = React.createClass({ + componentWillMount(){ + deprecationWarning( + 'The `ModalTrigger` component', 'the `Modal` component directly' + , 'http://react-bootstrap.github.io/components.html#modals'); + }, + + render(){ + return (); + } +}); + +DepreciatedModalTrigger.withContext = ModalTrigger.withContext; +DepreciatedModalTrigger.ModalTrigger = ModalTrigger; + +export default DepreciatedModalTrigger; diff --git a/src/index.js b/src/index.js index a15ce04b7b..327c383768 100644 --- a/src/index.js +++ b/src/index.js @@ -39,6 +39,7 @@ import NavItem from './NavItem'; import ModalTrigger from './ModalTrigger'; import OverlayTrigger from './OverlayTrigger'; import OverlayMixin from './OverlayMixin'; +import Overlay from './Overlay'; import PageHeader from './PageHeader'; import Pagination from './Pagination'; import Panel from './Panel'; @@ -100,6 +101,7 @@ export default { Navbar, NavItem, ModalTrigger, + Overlay, OverlayTrigger, OverlayMixin, PageHeader, diff --git a/test/ModalTriggerSpec.js b/test/ModalTriggerSpec.js index dbdcc8af46..9485f402b8 100644 --- a/test/ModalTriggerSpec.js +++ b/test/ModalTriggerSpec.js @@ -1,8 +1,27 @@ import React from 'react'; import ReactTestUtils from 'react/lib/ReactTestUtils'; import ModalTrigger from '../src/ModalTrigger'; +import { shouldWarn } from './helpers'; + describe('ModalTrigger', function() { + + afterEach(()=> { + if ( console.warn.called ) { + shouldWarn('The `ModalTrigger` component is deprecated'); + } + }); + + it('Should warn about deprecated Component', function() { + ReactTestUtils.renderIntoDocument( + test
}> + + + ); + + shouldWarn('The `ModalTrigger` component is deprecated'); + }); + it('Should create ModalTrigger element', function() { const instance = ReactTestUtils.renderIntoDocument( test}>