diff --git a/README.md b/README.md index d524bc3..e075182 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,11 @@ Keys of media query objects are camel-cased and numeric values automatically get See the [json2mq docs](https://github.com/akiran/json2mq/blob/master/README.md#usage) for more examples of queries you can construct using objects. +An optional `targetWindow` prop can be specified if you want the `query` to be +evaluated against a different window object than the one the code is running in. +This can be useful for example if you are rendering part of your component tree +to an iframe or [a popup window](https://hackernoon.com/using-a-react-16-portal-to-do-something-cool-2a2d627b0202). + If you're curious about how react-media differs from [react-responsive](https://github.com/contra/react-responsive), please see [this comment](https://github.com/ReactTraining/react-media/issues/70#issuecomment-347774260). diff --git a/modules/Media.js b/modules/Media.js index 2a57892..fc7aaad 100644 --- a/modules/Media.js +++ b/modules/Media.js @@ -14,7 +14,8 @@ class Media extends React.Component { PropTypes.arrayOf(PropTypes.object.isRequired) ]).isRequired, render: PropTypes.func, - children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]) + children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]), + targetWindow: PropTypes.object }; static defaultProps = { @@ -31,10 +32,17 @@ class Media extends React.Component { if (typeof window !== "object") return; let { query } = this.props; + const targetWindow = this.props.targetWindow || window; + + if (!targetWindow.matchMedia) { + throw new Error( + 'You passed a `targetWindow` prop to `Media` that does not have a `matchMedia` function.' + ); + } if (typeof query !== "string") query = json2mq(query); - this.mediaQueryList = window.matchMedia(query); + this.mediaQueryList = targetWindow.matchMedia(query); this.mediaQueryList.addListener(this.updateMatches); this.updateMatches(); } diff --git a/modules/__tests__/Media-test.js b/modules/__tests__/Media-test.js index 312cef0..b3b38aa 100644 --- a/modules/__tests__/Media-test.js +++ b/modules/__tests__/Media-test.js @@ -122,6 +122,44 @@ describe("A ", () => { }); }); + describe("when a custom targetWindow prop is passed", () => { + beforeEach(() => { + window.matchMedia = createMockMediaMatcher(true); + }); + + it("renders its child", () => { + const testWindow = { + matchMedia: createMockMediaMatcher(false) + }; + + const element = ( + + {matches => (matches ?
hello
:
goodbye
)} +
+ ); + + ReactDOM.render(element, node, () => { + expect(node.firstChild.innerHTML).toMatch(/goodbye/); + }); + }); + + describe("when a non-window prop is passed for targetWindow", () => { + it("errors with a useful message", () => { + const notAWindow = {}; + + const element = ( + + {matches => (matches ?
hello
:
goodbye
)} +
+ ); + + expect(() => { + ReactDOM.render(element, node, () => {}); + }).toThrow("does not have a `matchMedia` function"); + }); + }) + }); + describe("rendered on the server", () => { beforeEach(() => { window.matchMedia = createMockMediaMatcher(true);