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

Provide ability to configure mediaUrlPrefix in mediaApi #419

Merged
merged 5 commits into from
Jul 23, 2020
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
102 changes: 101 additions & 1 deletion packages/sitecore-jss-angular/src/components/image.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ class TestComponent {
@Component({
selector: 'test-image2',
template: `
<img height="1" width="1" *scImage="field; editable: editable; urlParams: params; attrs: imageAttrs" />
<img height="1" width="1" *scImage="field; editable: editable; urlParams: params; attrs: imageAttrs; mediaUrlPrefix: mediaUrlPrefix" />
`,
})
class AnotherTestComponent {
@Input() field: any;
@Input() editable = true;
@Input() params: any = {};
@Input() imageAttrs: any = { };
@Input() mediaUrlPrefix?: RegExp;
}

describe('<img *scImage />', () => {
Expand Down Expand Up @@ -215,6 +216,105 @@ describe('<img *scImage />', () => {
expect(url.query.w).toBe(imageParams.w);
expect(url.query.hash).toEqual('B973470AA333773341C62A76511361C88897E2D4');
});

it('should update image url using custom mediaUrlPrefix', () => {
const testImg = (expectedPrefix: string) => {
const img = de.nativeElement.getElementsByTagName('img')[0];
const url = URL(img.getAttribute('src'), null as any, true);

expect(url.pathname).toContain(expectedPrefix);
expect(url.query.h).toBe(imageParams.h);
expect(url.query.w).toBe(imageParams.w);
expect(url.query.hash).toBeUndefined();
};

comp2.mediaUrlPrefix = /\/([-~]{1})test\//i;
comp2.field = {
value: {
src: '/-test/assets/img/test0.png',
alt: 'my image',
height: '650',
width: '300',
},
};

fixture2.detectChanges();

testImg('/-/jssmedia/');

comp2.field = {
value: {
src: '/~test/assets/img/test0.png',
alt: 'my image',
height: '650',
width: '300',
},
};

fixture2.detectChanges();

testImg('/~/jssmedia/');

comp2.field = {
value: {
src: '/-invalid/assets/img/test0.png',
alt: 'my image',
height: '650',
width: '300',
},
};

fixture2.detectChanges();

testImg('/-invalid/');
});

it('should update image url using custom mediaUrlPrefix with srcSet', () => {
const testImg = (expectedPrefix: string) => {
const img = de.nativeElement.getElementsByTagName('img')[0];
const url = img.getAttribute('srcset');

expect(url).toBe(`${expectedPrefix}assets/img/test0.png?h=100&w=150&mw=100 150w, ${expectedPrefix}assets/img/test0.png?h=100&w=150&mw=300 150w`);
};

comp2.imageAttrs = {
srcSet: [{ mw: 100 }, { mw: 300 }],
};
comp2.mediaUrlPrefix = /\/([-~]{1})test\//i;

comp2.field = {
value: {
src: '/-test/assets/img/test0.png',
alt: 'my image',
},
};

fixture2.detectChanges();

testImg('/-/jssmedia/');

comp2.field = {
value: {
src: '/~test/assets/img/test0.png',
alt: 'my image',
},
};

fixture2.detectChanges();

testImg('/~/jssmedia/');

comp2.field = {
value: {
src: '/~invalid/assets/img/test0.png',
alt: 'my image',
},
};

fixture2.detectChanges();

testImg('/~invalid/');
});
});

describe('with "editable" property value but editing disabled', () => {
Expand Down
14 changes: 12 additions & 2 deletions packages/sitecore-jss-angular/src/components/image.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ export class ImageDirective implements OnChanges {
// tslint:disable-next-line:no-input-rename
@Input('scImageEditable') editable = true;

/**
* Custom regexp that finds media URL prefix that will be replaced by `/-/jssmedia` or `/~/jssmedia`.
* @example
* /\/([-~]{1})assets\//i
* /-assets/website -> /-/jssmedia/website
* /~assets/website -> /~/jssmedia/website
*/
// tslint:disable-next-line:no-input-rename
@Input('scImageMediaUrlPrefix') mediaUrlPrefix?: RegExp;

// tslint:disable-next-line:no-input-rename
@Input('scImageUrlParams') urlParams = {};

Expand Down Expand Up @@ -94,10 +104,10 @@ export class ImageDirective implements OnChanges {
...otherAttrs,
};
// update image URL for jss handler and image rendering params
src = mediaApi.updateImageUrl(src, imageParams);
src = mediaApi.updateImageUrl(src, imageParams, this.mediaUrlPrefix);
if (srcSet) {
// replace with HTML-formatted srcset, including updated image URLs
newAttrs.srcSet = mediaApi.getSrcSet(src, srcSet, imageParams);
newAttrs.srcSet = mediaApi.getSrcSet(src, srcSet, imageParams, this.mediaUrlPrefix);
} else {
newAttrs.src = src;
}
Expand Down
80 changes: 80 additions & 0 deletions packages/sitecore-jss-react/src/components/Image.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,86 @@ describe('<Image />', () => {
});
});

describe('with "mediaUrlPrefix" property', () => {
it('should transform url with "value" property value', () => {
const props = {
media: { value: { src: '/~assets/img/test0.png', alt: 'my image' } },
id: 'some-id',
style: { width: '100%' },
className: 'the-dude-abides',
mediaUrlPrefix: /\/([-~]{1})assets\//i
};
const rendered = mount(<Image {...props} />);

expect(rendered.find('img').prop('src')).to.equal('/~/jssmedia/img/test0.png');

rendered.setProps({
...props,
media: { value: { src: '/-assets/img/test0.png', alt: 'my image' } },
});

expect(rendered.find('img').prop('src')).to.equal('/-/jssmedia/img/test0.png');
});

it('should transform url with direct image object, no value/editable', () => {
const props = {
media: {
src: '/~assets/img/test0.png',
width: 8,
height: 10,
},
id: 'some-id',
style: {
width: '100%',
},
className: 'the-dude-abides',
mediaUrlPrefix: /\/([-~]{1})assets\//i
};
const rendered = mount(<Image {...props} />);

expect(rendered.find('img').prop('src')).to.equal('/~/jssmedia/img/test0.png');

rendered.setProps({
...props,
media: {
src: '/-assets/img/test0.png',
width: 8,
height: 10,
}
});

expect(rendered.find('img').prop('src')).to.equal('/-/jssmedia/img/test0.png');
});

it('should transform url with responsive image object', () => {
const props = {
media: {
src: '/~assets/img/test0.png',
},
srcSet: [{ mw: 100 }, { mw: 300 }],
sizes: '(min-width: 960px) 300px, 100px',
id: 'some-id',
className: 'the-dude-abides',
mediaUrlPrefix: /\/([-~]{1})assets\//i
};

const rendered = mount(<Image {...props} />);

expect(rendered.find('img').prop('src')).to.equal('/~/jssmedia/img/test0.png');

rendered.setProps({
...props,
media: {
src: '/-assets/img/test0.png',
width: 8,
height: 10,
}
});

expect(rendered.find('img').prop('src')).to.equal('/-/jssmedia/img/test0.png');
});
});

it('should render no <img /> when media prop is empty', () => {
const img: any = '';
const rendered = mount(<Image media={img} />);
Expand Down
22 changes: 17 additions & 5 deletions packages/sitecore-jss-react/src/components/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ export interface ImageProps {

srcSet?: Array<ImageSizeParameters>;

/**
* Custom regexp that finds media URL prefix that will be replaced by `/-/jssmedia` or `/~/jssmedia`.
* @example
* /\/([-~]{1})assets\//i
* /-assets/website -> /-/jssmedia/website
* /~assets/website -> /~/jssmedia/website
*/
mediaUrlPrefix?: RegExp;

/** HTML attributes that will be appended to the rendered <img /> tag. */
[attributeName: string]: any;
}
Expand All @@ -82,7 +91,8 @@ const getImageAttrs = (
srcSet: any;
otherAttrs: any[];
},
imageParams: any
imageParams: any,
mediaUrlPrefix?: RegExp
) => {
if (!src) {
return null;
Expand All @@ -93,10 +103,10 @@ const getImageAttrs = (
};

// update image URL for jss handler and image rendering params
const resolvedSrc = mediaApi.updateImageUrl(src, imageParams);
const resolvedSrc = mediaApi.updateImageUrl(src, imageParams, mediaUrlPrefix);
if (srcSet) {
// replace with HTML-formatted srcset, including updated image URLs
newAttrs.srcSet = mediaApi.getSrcSet(resolvedSrc, srcSet, imageParams);
newAttrs.srcSet = mediaApi.getSrcSet(resolvedSrc, srcSet, imageParams, mediaUrlPrefix);
}
// always output original src as fallback for older browsers
newAttrs.src = resolvedSrc;
Expand All @@ -108,6 +118,7 @@ export const Image: React.SFC<ImageProps> = ({
editable,
imageParams,
field,
mediaUrlPrefix,
...otherProps
}) => {
// allows the mistake of using 'field' prop instead of 'media' (consistent with other helpers)
Expand All @@ -131,7 +142,7 @@ export const Image: React.SFC<ImageProps> = ({
const foundImgProps = convertAttributesToReactProps(foundImg.attrs);
// Note: otherProps may override values from foundImgProps, e.g. `style`, `className` prop
// We do not attempt to merge.
const imgAttrs = getImageAttrs({ ...foundImgProps, ...otherProps } as any, imageParams);
const imgAttrs = getImageAttrs({ ...foundImgProps, ...otherProps } as any, imageParams, mediaUrlPrefix);
if (!imgAttrs) {
return getEditableWrapper(dynamicMedia.editable);
}
Expand All @@ -147,7 +158,7 @@ export const Image: React.SFC<ImageProps> = ({
return null;
}

const attrs = getImageAttrs({ ...img, ...otherProps }, imageParams);
const attrs = getImageAttrs({ ...img, ...otherProps }, imageParams, mediaUrlPrefix);
if (attrs) {
return <img {...attrs} />;
}
Expand All @@ -166,6 +177,7 @@ Image.propTypes = {
}),
]),
editable: PropTypes.bool,
mediaUrlPrefix: PropTypes.instanceOf(RegExp),
imageParams: PropTypes.objectOf(PropTypes.oneOfType([
PropTypes.number.isRequired,
PropTypes.string.isRequired
Expand Down
Loading