Skip to content

Commit

Permalink
fix: prevent overwriting htmlAttributes.ref (#496)
Browse files Browse the repository at this point in the history
  • Loading branch information
sherwinski authored Nov 15, 2019
1 parent aca4ceb commit e15e1b2
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 36 deletions.
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", () => {
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

0 comments on commit e15e1b2

Please sign in to comment.