Skip to content

Commit 89de698

Browse files
authored
Merge pull request #78 from raycohen/optional-window-prop
provide optional `targetWindow` prop
2 parents eb3e401 + 75b0005 commit 89de698

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ Keys of media query objects are camel-cased and numeric values automatically get
129129
See the [json2mq docs](https://github.com/akiran/json2mq/blob/master/README.md#usage) for more
130130
examples of queries you can construct using objects.
131131

132+
An optional `targetWindow` prop can be specified if you want the `query` to be
133+
evaluated against a different window object than the one the code is running in.
134+
This can be useful for example if you are rendering part of your component tree
135+
to an iframe or [a popup window](https://hackernoon.com/using-a-react-16-portal-to-do-something-cool-2a2d627b0202).
136+
132137
If you're curious about how react-media differs from
133138
[react-responsive](https://github.com/contra/react-responsive), please see
134139
[this comment](https://github.com/ReactTraining/react-media/issues/70#issuecomment-347774260).

modules/Media.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class Media extends React.Component {
1414
PropTypes.arrayOf(PropTypes.object.isRequired)
1515
]).isRequired,
1616
render: PropTypes.func,
17-
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func])
17+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
18+
targetWindow: PropTypes.object
1819
};
1920

2021
static defaultProps = {
@@ -31,10 +32,17 @@ class Media extends React.Component {
3132
if (typeof window !== "object") return;
3233

3334
let { query } = this.props;
35+
const targetWindow = this.props.targetWindow || window;
36+
37+
if (!targetWindow.matchMedia) {
38+
throw new Error(
39+
'You passed a `targetWindow` prop to `Media` that does not have a `matchMedia` function.'
40+
);
41+
}
3442

3543
if (typeof query !== "string") query = json2mq(query);
3644

37-
this.mediaQueryList = window.matchMedia(query);
45+
this.mediaQueryList = targetWindow.matchMedia(query);
3846
this.mediaQueryList.addListener(this.updateMatches);
3947
this.updateMatches();
4048
}

modules/__tests__/Media-test.js

+38
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,44 @@ describe("A <Media>", () => {
122122
});
123123
});
124124

125+
describe("when a custom targetWindow prop is passed", () => {
126+
beforeEach(() => {
127+
window.matchMedia = createMockMediaMatcher(true);
128+
});
129+
130+
it("renders its child", () => {
131+
const testWindow = {
132+
matchMedia: createMockMediaMatcher(false)
133+
};
134+
135+
const element = (
136+
<Media query="" targetWindow={testWindow}>
137+
{matches => (matches ? <div>hello</div> : <div>goodbye</div>)}
138+
</Media>
139+
);
140+
141+
ReactDOM.render(element, node, () => {
142+
expect(node.firstChild.innerHTML).toMatch(/goodbye/);
143+
});
144+
});
145+
146+
describe("when a non-window prop is passed for targetWindow", () => {
147+
it("errors with a useful message", () => {
148+
const notAWindow = {};
149+
150+
const element = (
151+
<Media query="" targetWindow={notAWindow}>
152+
{matches => (matches ? <div>hello</div> : <div>goodbye</div>)}
153+
</Media>
154+
);
155+
156+
expect(() => {
157+
ReactDOM.render(element, node, () => {});
158+
}).toThrow("does not have a `matchMedia` function");
159+
});
160+
})
161+
});
162+
125163
describe("rendered on the server", () => {
126164
beforeEach(() => {
127165
window.matchMedia = createMockMediaMatcher(true);

0 commit comments

Comments
 (0)