Skip to content

Commit 6192285

Browse files
committed
[added] <Route ignoreScrollBehavior /> to opt out of scroll behavior for itself and descendants
1 parent b9416bc commit 6192285

File tree

3 files changed

+108
-5
lines changed

3 files changed

+108
-5
lines changed

modules/components/Route.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ var Route = React.createClass({
5353
handler: React.PropTypes.any.isRequired,
5454
getAsyncProps: React.PropTypes.func,
5555
path: React.PropTypes.string,
56-
name: React.PropTypes.string
56+
name: React.PropTypes.string,
57+
ignoreScrollBehavior: React.PropTypes.bool
5758
},
5859

5960
render: function () {

modules/components/Routes.js

+17-4
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ function updateMatchComponents(matches, refs) {
163163
}
164164
}
165165

166+
function shouldUpdateScroll(currentMatches, previousMatches) {
167+
var commonMatches = currentMatches.filter(function (match) {
168+
return previousMatches.indexOf(match) !== -1;
169+
});
170+
171+
return !commonMatches.some(function (match) {
172+
return match.route.props.ignoreScrollBehavior;
173+
});
174+
}
175+
166176
function returnNull() {
167177
return null;
168178
}
@@ -285,16 +295,19 @@ var Routes = React.createClass({
285295
} else if (abortReason) {
286296
this.goBack();
287297
} else {
288-
this._handleStateChange = this.handleStateChange.bind(this, path, actionType);
298+
this._handleStateChange = this.handleStateChange.bind(this, path, actionType, this.state.matches);
289299
this.setState(nextState);
290300
}
291301
});
292302
},
293303

294-
handleStateChange: function (path, actionType) {
295-
updateMatchComponents(this.state.matches, this.refs);
304+
handleStateChange: function (path, actionType, previousMatches) {
305+
var currentMatches = this.state.matches;
306+
updateMatchComponents(currentMatches, this.refs);
296307

297-
this.updateScroll(path, actionType);
308+
if (shouldUpdateScroll(currentMatches, previousMatches)) {
309+
this.updateScroll(path, actionType);
310+
}
298311

299312
if (this.props.onChange)
300313
this.props.onChange.call(this);

modules/components/__tests__/Routes-test.js

+89
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,93 @@ describe('A Routes', function () {
6969
});
7070
});
7171

72+
describe('that considers ignoreScrollBehavior when calling updateScroll', function () {
73+
var component;
74+
beforeEach(function () {
75+
component = ReactTestUtils.renderIntoDocument(
76+
Routes({ location: 'none' },
77+
Route({ handler: NullHandler, ignoreScrollBehavior: true },
78+
Route({ path: '/feed', handler: NullHandler }),
79+
Route({ path: '/discover', handler: NullHandler })
80+
),
81+
Route({ path: '/search', handler: NullHandler, ignoreScrollBehavior: true }),
82+
Route({ path: '/about', handler: NullHandler })
83+
)
84+
);
85+
});
86+
87+
function spyOnUpdateScroll(action) {
88+
var didCall = false;
89+
90+
var realUpdateScroll = component.updateScroll;
91+
component.updateScroll = function mockUpdateScroll() {
92+
didCall = true;
93+
realUpdateScroll.apply(component, arguments);
94+
};
95+
96+
try {
97+
action();
98+
} finally {
99+
component.updateScroll = realUpdateScroll;
100+
}
101+
102+
return didCall;
103+
}
104+
105+
afterEach(function () {
106+
React.unmountComponentAtNode(component.getDOMNode());
107+
});
108+
109+
it('calls updateScroll when no ancestors ignore scroll', function () {
110+
component.updateLocation('/feed');
111+
112+
var calledUpdateScroll = spyOnUpdateScroll(function () {
113+
component.updateLocation('/about');
114+
});
115+
116+
expect(calledUpdateScroll).toEqual(true);
117+
});
118+
119+
it('calls updateScroll when no ancestors ignore scroll even though source and target do', function () {
120+
component.updateLocation('/feed');
121+
122+
var calledUpdateScroll = spyOnUpdateScroll(function () {
123+
component.updateLocation('/search');
124+
});
125+
126+
expect(calledUpdateScroll).toEqual(true);
127+
});
128+
129+
it('calls updateScroll when source is same as target and does not ignore scroll', function () {
130+
component.updateLocation('/about');
131+
132+
var calledUpdateScroll = spyOnUpdateScroll(function () {
133+
component.updateLocation('/about?page=2');
134+
});
135+
136+
expect(calledUpdateScroll).toEqual(true);
137+
});
138+
139+
it('does not call updateScroll when common ancestor ignores scroll', function () {
140+
component.updateLocation('/feed');
141+
142+
var calledUpdateScroll = spyOnUpdateScroll(function () {
143+
component.updateLocation('/discover');
144+
});
145+
146+
expect(calledUpdateScroll).toEqual(false);
147+
});
148+
149+
it('does not call updateScroll when source is same as target and ignores scroll', function () {
150+
component.updateLocation('/search');
151+
152+
var calledUpdateScroll = spyOnUpdateScroll(function () {
153+
component.updateLocation('/search?q=test');
154+
});
155+
156+
expect(calledUpdateScroll).toEqual(false);
157+
});
158+
159+
});
160+
72161
});

0 commit comments

Comments
 (0)