Skip to content

Commit

Permalink
Merge pull request #78 from raycohen/optional-window-prop
Browse files Browse the repository at this point in the history
provide optional `targetWindow` prop
  • Loading branch information
mjackson authored Jan 30, 2018
2 parents eb3e401 + 75b0005 commit 89de698
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 2 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
12 changes: 10 additions & 2 deletions modules/Media.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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();
}
Expand Down
38 changes: 38 additions & 0 deletions modules/__tests__/Media-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,44 @@ describe("A <Media>", () => {
});
});

describe("when a custom targetWindow prop is passed", () => {
beforeEach(() => {
window.matchMedia = createMockMediaMatcher(true);
});

it("renders its child", () => {
const testWindow = {
matchMedia: createMockMediaMatcher(false)
};

const element = (
<Media query="" targetWindow={testWindow}>
{matches => (matches ? <div>hello</div> : <div>goodbye</div>)}
</Media>
);

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 = (
<Media query="" targetWindow={notAWindow}>
{matches => (matches ? <div>hello</div> : <div>goodbye</div>)}
</Media>
);

expect(() => {
ReactDOM.render(element, node, () => {});
}).toThrow("does not have a `matchMedia` function");
});
})
});

describe("rendered on the server", () => {
beforeEach(() => {
window.matchMedia = createMockMediaMatcher(true);
Expand Down

0 comments on commit 89de698

Please sign in to comment.