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

Use img tag instead of picture #2067

Merged
merged 12 commits into from
Dec 4, 2020
4 changes: 2 additions & 2 deletions .storybook/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import { defaults } from './default-css';
import 'reset-css';

import { Lazy } from '@root/src/web/components/Lazy';
import { Picture } from '@root/src/web/components/Picture';
import { Img } from '@root/src/web/components/Img';

// Prevent components being lazy rendered when we're taking Chromatic snapshots
Lazy.disabled = isChromatic();
Picture.disableLazyLoading = isChromatic();
Img.disableLazyLoading = isChromatic();

// Add base css for the site
let css = `${getFontsCss()}${defaults}`;
Expand Down
107 changes: 56 additions & 51 deletions cypress/integration/e2e/consent.spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable mocha/no-setup-in-describe */
import { setLocalBaseUrl } from '../../lib/setLocalBaseUrl.js';
import { skipOn } from '@cypress/skip-test';

Expand All @@ -10,7 +11,7 @@ const secondPage =
describe('Consent tests', function () {
const cmpIframe = () => {
return cy
.get('iframe[id^="sp_message_iframe"]', { timeout: 30000 })
.get('iframe[id^="sp_message_iframe"]')
.its('0.contentDocument.body')
.should('not.be.empty')
.then(cy.wrap);
Expand All @@ -37,56 +38,60 @@ describe('Consent tests', function () {

// Skipping only on CI because, these tests work fine locally but can fail on CI is the server
// being used is in the US
// eslint-disable-next-line mocha/no-setup-in-describe
skipOn((Cypress.env('TEAMCITY') === 'true' || Cypress.env('GITHUB_ACTIONS') === 'true'), () => {
it('should make calls to Google Analytics after the reader consents', function () {
cy.visit(`Article?url=${firstPage}`);
waitForAnalyticsToInit();
// Open the Privacy setting dialogue
cmpIframe().contains("It's your choice");
cmpIframe().find("[title='Manage my cookies']").click();
// Accept tracking cookies
privacySettingsIframe().contains('Privacy settings');
privacySettingsIframe().find("[title='Accept all']").click();
// Make a second page load now that we have the CMP cookies set to accept tracking
cy.visit(`Article?url=${secondPage}`);
// Wait for a call to Google Analytics to be made - we expect this to happen
cy.intercept('POST', 'https://www.google-analytics.com/**');
});
skipOn(
Cypress.env('TEAMCITY') === 'true' ||
Cypress.env('GITHUB_ACTIONS') === 'true',
() => {
it('should make calls to Google Analytics after the reader consents', function () {
cy.visit(`Article?url=${firstPage}`);
waitForAnalyticsToInit();
cy.window().its('ga').should('not.exist');
// Open the Privacy setting dialogue
cmpIframe().contains("It's your choice");
cmpIframe().find("[title='Manage my cookies']").click();
// Accept tracking cookies
privacySettingsIframe().contains('Privacy settings');
privacySettingsIframe().find("[title='Accept all']").click();
// Make a second page load now that we have the CMP cookies set to accept tracking
cy.visit(`Article?url=${secondPage}`);
// Wait for a call to Google Analytics to be made - we expect this to happen
cy.intercept('POST', 'https://www.google-analytics.com/**');
});

it('should not add GA tracking scripts onto the window object after the reader rejects consent', function () {
cy.visit(`Article?url=${firstPage}`);
waitForAnalyticsToInit();
cy.window().its('ga').should('not.exist');
// Open the Privacy setting dialogue
cmpIframe().contains("It's your choice");
cmpIframe().find("[title='Manage my cookies']").click();
// Reject tracking cookies
privacySettingsIframe().contains('Privacy settings');
privacySettingsIframe().find("[title='Reject all']").click();
// Make a second page load now that we have the CMP cookies set to reject tracking and check
// to see if the ga property was set by Google on the window object
cy.visit(`Article?url=${secondPage}`);
waitForAnalyticsToInit();
// We force window.ga to be null on consent rejection to prevent subsequent requests
cy.window().its('ga').should('equal', null);
});
it('should not add GA tracking scripts onto the window object after the reader rejects consent', function () {
cy.visit(`Article?url=${firstPage}`);
waitForAnalyticsToInit();
cy.window().its('ga').should('not.exist');
// Open the Privacy setting dialogue
cmpIframe().contains("It's your choice");
cmpIframe().find("[title='Manage my cookies']").click();
// Reject tracking cookies
privacySettingsIframe().contains('Privacy settings');
privacySettingsIframe().find("[title='Reject all']").click();
// Make a second page load now that we have the CMP cookies set to reject tracking and check
// to see if the ga property was set by Google on the window object
cy.visit(`Article?url=${secondPage}`);
waitForAnalyticsToInit();
// We force window.ga to be null on consent rejection to prevent subsequent requests
cy.window().its('ga').should('equal', null);
});

it('should add GA tracking scripts onto the window object after the reader accepts consent', function () {
cy.visit(`Article?url=${firstPage}`);
waitForAnalyticsToInit();
cy.window().its('ga').should('not.exist');
// Open the Privacy setting dialogue
cmpIframe().contains("It's your choice");
cmpIframe().find("[title='Manage my cookies']").click();
// Reject tracking cookies
privacySettingsIframe().contains('Privacy settings');
privacySettingsIframe().find("[title='Accept all']").click();
// Make a second page load now that we have the CMP cookies set to reject tracking and check
// to see if the ga property was set by Google on the window object
cy.visit(`Article?url=${secondPage}`);
waitForAnalyticsToInit();
cy.window().its('ga').should('exist');
});
});
it('should add GA tracking scripts onto the window object after the reader accepts consent', function () {
cy.visit(`Article?url=${firstPage}`);
waitForAnalyticsToInit();
cy.window().its('ga').should('not.exist');
// Open the Privacy setting dialogue
cmpIframe().contains("It's your choice");
cmpIframe().find("[title='Manage my cookies']").click();
// Reject tracking cookies
privacySettingsIframe().contains('Privacy settings');
privacySettingsIframe().find("[title='Accept all']").click();
// Make a second page load now that we have the CMP cookies set to reject tracking and check
// to see if the ga property was set by Google on the window object
cy.visit(`Article?url=${secondPage}`);
waitForAnalyticsToInit();
cy.window().its('ga').should('exist');
});
},
);
});
107 changes: 107 additions & 0 deletions src/web/components/Img.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React from 'react';
import { css } from 'emotion';

import { breakpoints } from '@guardian/src-foundations/mq';

type Props = {
imageSources: ImageSource[];
role: RoleType;
alt: string;
height: string;
width: string;
isMainMedia?: boolean;
isLazy?: boolean;
};

const getClosestSetForWidth = (
desiredWidth: number,
inlineSrcSets: SrcSetItem[],
): SrcSetItem => {
// For a desired width, find the SrcSetItem which is the closest match
const sorted = inlineSrcSets.sort((a, b) => b.width - a.width);
return sorted.reduce((best, current) => {
if (current.width < best.width && current.width >= desiredWidth) {
return current;
}
return best;
});
};

const getFallbackSrc = (srcSets: SrcSetItem[]): string => {
// The assumption here is readers on devices that do not support srcset are likely to be on poor
// network connections so we're going to fallback to a small image
return getClosestSetForWidth(300, srcSets).src;
};

const getSrcSetsForRole = (
role: RoleType,
imageSources: ImageSource[],
): SrcSetItem[] => {
return imageSources.filter(
({ weighting }) =>
// Use toLowerCase to handle cases where we have halfWidth comparing to halfwidth
weighting.toLowerCase() === role.toLowerCase(),
)[0].srcSet;
};

const buildSourcesString = (srcSets: SrcSetItem[]): string => {
return srcSets.map((srcSet) => `${srcSet.src} ${srcSet.width}w`).join(',');
};

const buildSizesString = (role: RoleType, isMainMedia: boolean): string => {
switch (role) {
case 'inline':
return `(min-width: ${breakpoints.phablet}px) 620px, 100vw`;
case 'halfWidth':
return `(min-width: ${breakpoints.phablet}px) 300px, 50vw`;
case 'thumbnail':
return '140px';
case 'immersive':
return isMainMedia
? '100vw'
: `(min-width: ${breakpoints.wide}px) 1300px, 100vw`;
case 'supporting':
return `(min-width: ${breakpoints.wide}px) 380px, 300px`;
case 'showcase':
return isMainMedia
? `(min-width: ${breakpoints.wide}px) 1020px, (min-width: ${breakpoints.leftCol}px) 940px, (min-width: ${breakpoints.tablet}px) 700px, (min-width: ${breakpoints.phablet}px) 660px, 100vw`
: `(min-width: ${breakpoints.wide}px) 860px, (min-width: ${breakpoints.leftCol}px) 780px, (min-width: ${breakpoints.phablet}px) 620px, 100vw`;
}
};

export const Img = ({
imageSources,
role,
alt,
height,
width,
isMainMedia = false,
isLazy = true,
}: Props) => {
const srcSetForRole = getSrcSetsForRole(role, imageSources);
const src = getFallbackSrc(srcSetForRole);
const sources = buildSourcesString(srcSetForRole);
const sizes = buildSizesString(role, isMainMedia);

return (
<img
itemProp="contentUrl"
alt={alt}
src={src}
srcSet={sources}
sizes={sizes}
height={height}
width={width}
loading={isLazy && !Img.disableLazyLoading ? 'lazy' : undefined}
// https://stackoverflow.com/questions/10844205/html-5-strange-img-always-adds-3px-margin-at-bottom
// why did we add the css `vertical-align: middle;` to the img tag
className={css`
vertical-align: middle;
`}
/>
);
};

// We use disableLazyLoading to decide if we want to turn off lazy loading of images site wide. We use this
// to prevent false negatives on Chromatic snapshots (see /.storybook/config)
Img.disableLazyLoading = false;
50 changes: 0 additions & 50 deletions src/web/components/Picture.tsx

This file was deleted.

Loading