Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: prevent overwriting htmlAttributes.ref #496

Merged
merged 4 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions src/react-imgix.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ function aspectRatioIsValid(aspectRatio) {
return /^\d+(\.\d+)?:\d+(\.\d+)?$/.test(aspectRatio);
}

const setParentRef = (parentRef, el) =>{
if (!parentRef) {
return;
}

// assign ref based on if it's a callback vs object
if (typeof parentRef === "function") {
parentRef(el);
} else {
parentRef.current = el;
}
}

const buildSrcSetPairWithFixedHeight = (url, targetWidth, fixedHeight, _) =>
url + "&h=" + fixedHeight + "&w=" + targetWidth + " " + targetWidth + "w";

Expand Down Expand Up @@ -226,7 +239,15 @@ class ReactImgix extends Component {
width: width <= 1 ? null : width,
height: height <= 1 ? null : height,
[attributeConfig.src]: src,
ref: el => (this.imgRef = el)
ref: el => {
this.imgRef = el;
if (
this.props.htmlAttributes !== undefined &&
"ref" in this.props.htmlAttributes
) {
setParentRef(this.props.htmlAttributes.ref, this.imgRef);
}
}
});
if (!disableSrcSet) {
childProps[attributeConfig.srcSet] = srcSet;
Expand Down Expand Up @@ -336,7 +357,15 @@ class SourceImpl extends Component {
className: this.props.className,
width: width <= 1 ? null : width,
height: height <= 1 ? null : height,
ref: el => (this.sourceRef = el)
ref: el => {
this.sourceRef = el;
if (
this.props.htmlAttributes !== undefined &&
"ref" in this.props.htmlAttributes
) {
setParentRef(this.props.htmlAttributes.ref, this.sourceRef);
}
}
});

// inside of a <picture> element a <source> element ignores its src
Expand Down
11 changes: 2 additions & 9 deletions test/unit/helpers/shallowUntilTarget.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,7 @@ describe("helpers", () => {
});

it("lets you unwrap a component two levels", () => {
const Example = compose(
wrapper(),
wrapper()
)(ExampleBase);
const Example = compose(wrapper(), wrapper())(ExampleBase);

const root = shallowUntilTarget(<Example />, ExampleBase);
expect(root.text()).toEqual("Example component");
Expand All @@ -84,11 +81,7 @@ describe("helpers", () => {
});

it("gives up trying to unwrap component after maxTries", () => {
const Example = compose(
wrapper(),
wrapper(),
wrapper()
)(ExampleBase);
const Example = compose(wrapper(), wrapper(), wrapper())(ExampleBase);

expect(() => {
shallowUntilTarget(<Example />, ExampleBase, {
Expand Down
149 changes: 124 additions & 25 deletions test/unit/react-imgix.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,72 @@ describe("When in <source> mode", () => {
const onMountArg = onMountedSpy.lastCall.args[0];
expect(onMountArg).toBeInstanceOf(HTMLSourceElement);
});

describe("using the htmlAttributes prop", () => {
it("assigns an alt attribute given htmlAttributes.alt", async () => {
const htmlAttributes = {
alt: "Example alt attribute"
};

sut = mount(
<Source
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
htmlAttributes={htmlAttributes}
/>
);

expect(sut.props()["htmlAttributes"].alt).toEqual(htmlAttributes.alt);
});

it("passes any attributes via htmlAttributes to the rendered element", () => {
const htmlAttributes = {
"data-src": "https://mysource.imgix.net/demo.png"
};
sut = mount(
<Source
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
htmlAttributes={htmlAttributes}
/>
);

expect(sut.props()["htmlAttributes"]["data-src"]).toEqual(
htmlAttributes["data-src"]
);
});

it("attaches a ref via htmlAttributes", () => {
sherwinski marked this conversation as resolved.
Show resolved Hide resolved
const callback = sinon.spy();

sut = mount(
<Source
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
htmlAttributes={{ ref: callback }}
/>
);

expect(callback.callCount).toEqual(1);
});

it("stills calls onMounted if a ref is passed via htmlAttributes", () => {
const htmlAttrCallback = sinon.spy();
const onMountedCallback = sinon.spy();

sut = mount(
<Source
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
onMounted={onMountedCallback}
htmlAttributes={{ ref: htmlAttrCallback }}
/>
);

expect(htmlAttrCallback.callCount).toEqual(1);
expect(onMountedCallback.callCount).toEqual(1);
});
});
});

describe("When in picture mode", () => {
Expand Down Expand Up @@ -706,33 +772,66 @@ describe("When using the component", () => {
});
});

it("an alt attribute should be set given htmlAttributes.alt", async () => {
const htmlAttributes = {
alt: "Example alt attribute"
};
sut = shallow(
<Imgix
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
htmlAttributes={htmlAttributes}
/>
);
expect(sut.props().alt).toEqual(htmlAttributes.alt);
});
describe("using the htmlAttributes prop", () => {
it("assigns an alt attribute given htmlAttributes.alt", async () => {
const htmlAttributes = {
alt: "Example alt attribute"
};
sut = shallow(
<Imgix
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
htmlAttributes={htmlAttributes}
/>
);
expect(sut.props().alt).toEqual(htmlAttributes.alt);
});

it("any attributes passed via htmlAttributes should be added to the rendered element", () => {
const htmlAttributes = {
"data-src": "https://mysource.imgix.net/demo.png"
};
sut = shallow(
<Imgix
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
htmlAttributes={htmlAttributes}
/>
);
it("passes any attributes via htmlAttributes to the rendered element", () => {
const htmlAttributes = {
"data-src": "https://mysource.imgix.net/demo.png"
};
sut = shallow(
<Imgix
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
htmlAttributes={htmlAttributes}
/>
);

expect(sut.props()["data-src"]).toEqual(htmlAttributes["data-src"]);
});

it("attaches a ref via htmlAttributes", () => {
const callback = sinon.spy();

expect(sut.props()["data-src"]).toEqual(htmlAttributes["data-src"]);
sut = mount(
<Imgix
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
htmlAttributes={{ ref: callback }}
/>
);

expect(callback.callCount).toEqual(1);
});

it("stills calls onMounted if a ref is passed via htmlAttributes", () => {
const htmlAttrCallback = sinon.spy();
const onMountedCallback = sinon.spy();

sut = mount(
<Imgix
src={"https://mysource.imgix.net/demo.png"}
sizes="100vw"
onMounted={onMountedCallback}
htmlAttributes={{ ref: htmlAttrCallback }}
/>
);

expect(htmlAttrCallback.callCount).toEqual(1);
expect(onMountedCallback.callCount).toEqual(1);
});
});

it("an ixlib parameter should be included by default in the computed src and srcSet", () => {
Expand Down