diff --git a/src/react-imgix.jsx b/src/react-imgix.jsx index 91f3a7f5..128e57a4 100644 --- a/src/react-imgix.jsx +++ b/src/react-imgix.jsx @@ -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"; @@ -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; @@ -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 element a element ignores its src diff --git a/test/unit/helpers/shallowUntilTarget.test.jsx b/test/unit/helpers/shallowUntilTarget.test.jsx index 6ad9e9a9..14043c10 100644 --- a/test/unit/helpers/shallowUntilTarget.test.jsx +++ b/test/unit/helpers/shallowUntilTarget.test.jsx @@ -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(, ExampleBase); expect(root.text()).toEqual("Example component"); @@ -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(, ExampleBase, { diff --git a/test/unit/react-imgix.test.jsx b/test/unit/react-imgix.test.jsx index 8a400d91..8ca92b91 100644 --- a/test/unit/react-imgix.test.jsx +++ b/test/unit/react-imgix.test.jsx @@ -309,6 +309,72 @@ describe("When in 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( + + ); + + 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( + + ); + + expect(sut.props()["htmlAttributes"]["data-src"]).toEqual( + htmlAttributes["data-src"] + ); + }); + + it("attaches a ref via htmlAttributes", () => { + const callback = sinon.spy(); + + sut = mount( + + ); + + 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( + + ); + + expect(htmlAttrCallback.callCount).toEqual(1); + expect(onMountedCallback.callCount).toEqual(1); + }); + }); }); describe("When in picture mode", () => { @@ -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( - - ); - 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( + + ); + 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( - - ); + it("passes any attributes via htmlAttributes to the rendered element", () => { + const htmlAttributes = { + "data-src": "https://mysource.imgix.net/demo.png" + }; + sut = shallow( + + ); + + 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( + + ); + + 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( + + ); + + 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", () => {