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}>