Skip to content

Commit f1564ee

Browse files
committed
Create CollapsibleMixin.
Move src/CollapsableMixin => src/CollapsibleMixin Create src/CollapsableMixin extended from src/CollapsibleMixin Refactor CollapsibleMixin.dimension() method, because it is the easiest way to get this method working. The cause is that function used from component is optional. I had tried `get` and `set`, but without success. Copy tests for the new src/CollapsibleMixin. Add assertions for deprecation warnings. DRY tests.
1 parent 779365c commit f1564ee

7 files changed

Lines changed: 501 additions & 166 deletions

File tree

src/CollapsableMixin.js

Lines changed: 28 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,168 +1,33 @@
1-
import React from 'react';
2-
import TransitionEvents from 'react/lib/ReactTransitionEvents';
3-
4-
const CollapsableMixin = {
5-
6-
propTypes: {
7-
defaultExpanded: React.PropTypes.bool,
8-
expanded: React.PropTypes.bool
9-
},
10-
11-
getInitialState(){
12-
let defaultExpanded = this.props.defaultExpanded != null ?
13-
this.props.defaultExpanded :
14-
this.props.expanded != null ?
15-
this.props.expanded :
16-
false;
17-
18-
return {
19-
expanded: defaultExpanded,
20-
collapsing: false
21-
};
22-
},
23-
24-
componentWillUpdate(nextProps, nextState){
25-
let willExpanded = nextProps.expanded != null ? nextProps.expanded : nextState.expanded;
26-
if (willExpanded === this.isExpanded()) {
27-
return;
28-
}
29-
30-
// if the expanded state is being toggled, ensure node has a dimension value
31-
// this is needed for the animation to work and needs to be set before
32-
// the collapsing class is applied (after collapsing is applied the in class
33-
// is removed and the node's dimension will be wrong)
34-
35-
let node = this.getCollapsableDOMNode();
36-
let dimension = this.dimension();
37-
let value = '0';
38-
39-
if(!willExpanded){
40-
value = this.getCollapsableDimensionValue();
41-
}
42-
43-
node.style[dimension] = value + 'px';
44-
45-
this._afterWillUpdate();
46-
},
47-
48-
componentDidUpdate(prevProps, prevState){
49-
// check if expanded is being toggled; if so, set collapsing
50-
this._checkToggleCollapsing(prevProps, prevState);
51-
52-
// check if collapsing was turned on; if so, start animation
53-
this._checkStartAnimation();
54-
},
55-
56-
// helps enable test stubs
57-
_afterWillUpdate(){
58-
},
59-
60-
_checkStartAnimation(){
61-
if(!this.state.collapsing) {
62-
return;
63-
}
64-
65-
let node = this.getCollapsableDOMNode();
66-
let dimension = this.dimension();
67-
let value = this.getCollapsableDimensionValue();
68-
69-
// setting the dimension here starts the transition animation
70-
let result;
71-
if(this.isExpanded()) {
72-
result = value + 'px';
73-
} else {
74-
result = '0px';
75-
}
76-
node.style[dimension] = result;
77-
},
78-
79-
_checkToggleCollapsing(prevProps, prevState){
80-
let wasExpanded = prevProps.expanded != null ? prevProps.expanded : prevState.expanded;
81-
let isExpanded = this.isExpanded();
82-
if(wasExpanded !== isExpanded){
83-
if(wasExpanded) {
84-
this._handleCollapse();
85-
} else {
86-
this._handleExpand();
87-
}
88-
}
89-
},
90-
91-
_handleExpand(){
92-
let node = this.getCollapsableDOMNode();
93-
let dimension = this.dimension();
94-
95-
let complete = () => {
96-
this._removeEndEventListener(node, complete);
97-
// remove dimension value - this ensures the collapsable item can grow
98-
// in dimension after initial display (such as an image loading)
99-
node.style[dimension] = '';
100-
this.setState({
101-
collapsing:false
102-
});
103-
};
104-
105-
this._addEndEventListener(node, complete);
106-
107-
this.setState({
108-
collapsing: true
109-
});
110-
},
111-
112-
_handleCollapse(){
113-
let node = this.getCollapsableDOMNode();
114-
115-
let complete = () => {
116-
this._removeEndEventListener(node, complete);
117-
this.setState({
118-
collapsing: false
119-
});
120-
};
121-
122-
this._addEndEventListener(node, complete);
123-
124-
this.setState({
125-
collapsing: true
126-
});
127-
},
128-
129-
// helps enable test stubs
130-
_addEndEventListener(node, complete){
131-
TransitionEvents.addEndEventListener(node, complete);
132-
},
133-
134-
// helps enable test stubs
135-
_removeEndEventListener(node, complete){
136-
TransitionEvents.removeEndEventListener(node, complete);
137-
},
138-
139-
dimension(){
140-
return (typeof this.getCollapsableDimension === 'function') ?
141-
this.getCollapsableDimension() :
142-
'height';
143-
},
144-
145-
isExpanded(){
146-
return this.props.expanded != null ? this.props.expanded : this.state.expanded;
147-
},
1+
import assign from './utils/Object.assign';
2+
import deprecationWarning from './utils/deprecationWarning';
3+
import CollapsibleMixin from './CollapsibleMixin';
1484

5+
const CollapsableMixin = assign({}, CollapsibleMixin, {
1496
getCollapsableClassSet(className) {
150-
let classes = {};
151-
152-
if (typeof className === 'string') {
153-
className.split(' ').forEach(subClasses => {
154-
if (subClasses) {
155-
classes[subClasses] = true;
156-
}
157-
});
158-
}
159-
160-
classes.collapsing = this.state.collapsing;
161-
classes.collapse = !this.state.collapsing;
162-
classes.in = this.isExpanded() && !this.state.collapsing;
163-
164-
return classes;
7+
deprecationWarning(
8+
'CollapsableMixin.getCollapsableClassSet()',
9+
'CollapsibleMixin.getCollapsibleClassSet()'
10+
);
11+
return CollapsibleMixin.getCollapsibleClassSet.call(this, className);
12+
},
13+
14+
getCollapsibleDOMNode() {
15+
deprecationWarning(
16+
'CollapsableMixin.getCollapsableDOMNode()',
17+
'CollapsibleMixin.getCollapsibleDOMNode()'
18+
);
19+
return this.getCollapsableDOMNode();
20+
},
21+
22+
getCollapsibleDimensionValue() {
23+
deprecationWarning(
24+
'CollapsableMixin.getCollapsableDimensionValue()',
25+
'CollapsibleMixin.getCollapsibleDimensionValue()'
26+
);
27+
return this.getCollapsableDimensionValue();
16528
}
166-
};
29+
});
30+
31+
deprecationWarning('CollapsableMixin', 'CollapsibleMixin');
16732

16833
export default CollapsableMixin;

src/CollapsibleMixin.js

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import React from 'react';
2+
import TransitionEvents from 'react/lib/ReactTransitionEvents';
3+
import deprecationWarning from './utils/deprecationWarning';
4+
5+
const CollapsibleMixin = {
6+
7+
propTypes: {
8+
defaultExpanded: React.PropTypes.bool,
9+
expanded: React.PropTypes.bool
10+
},
11+
12+
getInitialState(){
13+
let defaultExpanded = this.props.defaultExpanded != null ?
14+
this.props.defaultExpanded :
15+
this.props.expanded != null ?
16+
this.props.expanded :
17+
false;
18+
19+
return {
20+
expanded: defaultExpanded,
21+
collapsing: false
22+
};
23+
},
24+
25+
componentWillUpdate(nextProps, nextState){
26+
let willExpanded = nextProps.expanded != null ? nextProps.expanded : nextState.expanded;
27+
if (willExpanded === this.isExpanded()) {
28+
return;
29+
}
30+
31+
// if the expanded state is being toggled, ensure node has a dimension value
32+
// this is needed for the animation to work and needs to be set before
33+
// the collapsing class is applied (after collapsing is applied the in class
34+
// is removed and the node's dimension will be wrong)
35+
36+
let node = this.getCollapsibleDOMNode();
37+
let dimension = this.dimension();
38+
let value = '0';
39+
40+
if(!willExpanded){
41+
value = this.getCollapsibleDimensionValue();
42+
}
43+
44+
node.style[dimension] = value + 'px';
45+
46+
this._afterWillUpdate();
47+
},
48+
49+
componentDidUpdate(prevProps, prevState){
50+
// check if expanded is being toggled; if so, set collapsing
51+
this._checkToggleCollapsing(prevProps, prevState);
52+
53+
// check if collapsing was turned on; if so, start animation
54+
this._checkStartAnimation();
55+
},
56+
57+
// helps enable test stubs
58+
_afterWillUpdate(){
59+
},
60+
61+
_checkStartAnimation(){
62+
if(!this.state.collapsing) {
63+
return;
64+
}
65+
66+
let node = this.getCollapsibleDOMNode();
67+
let dimension = this.dimension();
68+
let value = this.getCollapsibleDimensionValue();
69+
70+
// setting the dimension here starts the transition animation
71+
let result;
72+
if(this.isExpanded()) {
73+
result = value + 'px';
74+
} else {
75+
result = '0px';
76+
}
77+
node.style[dimension] = result;
78+
},
79+
80+
_checkToggleCollapsing(prevProps, prevState){
81+
let wasExpanded = prevProps.expanded != null ? prevProps.expanded : prevState.expanded;
82+
let isExpanded = this.isExpanded();
83+
if(wasExpanded !== isExpanded){
84+
if(wasExpanded) {
85+
this._handleCollapse();
86+
} else {
87+
this._handleExpand();
88+
}
89+
}
90+
},
91+
92+
_handleExpand(){
93+
let node = this.getCollapsibleDOMNode();
94+
let dimension = this.dimension();
95+
96+
let complete = () => {
97+
this._removeEndEventListener(node, complete);
98+
// remove dimension value - this ensures the collapsable item can grow
99+
// in dimension after initial display (such as an image loading)
100+
node.style[dimension] = '';
101+
this.setState({
102+
collapsing:false
103+
});
104+
};
105+
106+
this._addEndEventListener(node, complete);
107+
108+
this.setState({
109+
collapsing: true
110+
});
111+
},
112+
113+
_handleCollapse(){
114+
let node = this.getCollapsibleDOMNode();
115+
116+
let complete = () => {
117+
this._removeEndEventListener(node, complete);
118+
this.setState({
119+
collapsing: false
120+
});
121+
};
122+
123+
this._addEndEventListener(node, complete);
124+
125+
this.setState({
126+
collapsing: true
127+
});
128+
},
129+
130+
// helps enable test stubs
131+
_addEndEventListener(node, complete){
132+
TransitionEvents.addEndEventListener(node, complete);
133+
},
134+
135+
// helps enable test stubs
136+
_removeEndEventListener(node, complete){
137+
TransitionEvents.removeEndEventListener(node, complete);
138+
},
139+
140+
dimension(){
141+
if (typeof this.getCollapsableDimension === 'function') {
142+
deprecationWarning(
143+
'CollapsableMixin.getCollapsableDimension()',
144+
'CollapsibleMixin.getCollapsibleDimension()'
145+
);
146+
return this.getCollapsableDimension();
147+
}
148+
149+
return (typeof this.getCollapsibleDimension === 'function') ?
150+
this.getCollapsibleDimension() :
151+
'height';
152+
},
153+
154+
isExpanded(){
155+
return this.props.expanded != null ? this.props.expanded : this.state.expanded;
156+
},
157+
158+
getCollapsibleClassSet(className) {
159+
let classes = {};
160+
161+
if (typeof className === 'string') {
162+
className.split(' ').forEach(subClasses => {
163+
if (subClasses) {
164+
classes[subClasses] = true;
165+
}
166+
});
167+
}
168+
169+
classes.collapsing = this.state.collapsing;
170+
classes.collapse = !this.state.collapsing;
171+
classes.in = this.isExpanded() && !this.state.collapsing;
172+
173+
return classes;
174+
}
175+
};
176+
177+
export default CollapsibleMixin;

src/utils/deprecationWarning.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default function deprecationWarning(oldname, newname) {
2+
if (process.env.NODE_ENV !== 'production') {
3+
let message = `${oldname} is deprecated. Use ${newname} instead.`;
4+
console.warn(message);
5+
let link = 'https://github.com/react-bootstrap/react-bootstrap/issues/425#issuecomment-97110963';
6+
console.warn(`You can read more about it here ${link}`);
7+
}
8+
}

0 commit comments

Comments
 (0)