diff --git a/src/app/contexts/RequestContext/index.test.tsx b/src/app/contexts/RequestContext/index.test.tsx index 4f17b26f049..1e1cb95c8fc 100644 --- a/src/app/contexts/RequestContext/index.test.tsx +++ b/src/app/contexts/RequestContext/index.test.tsx @@ -68,6 +68,7 @@ const expectedOutput = { isNextJs: false, isApp: false, isLite: false, + isOffline: false, platform: 'amp', variant: 'simp', timeOnServer: null, diff --git a/src/app/contexts/RequestContext/index.tsx b/src/app/contexts/RequestContext/index.tsx index 8600dc740d2..2cf978db631 100644 --- a/src/app/contexts/RequestContext/index.tsx +++ b/src/app/contexts/RequestContext/index.tsx @@ -24,6 +24,7 @@ export type RequestContextProps = { isAmp: boolean; isApp: boolean; isLite: boolean; + isOffline: boolean; isNextJs: boolean; isUK: boolean; serverSideExperiments?: ServerSideExperiment[] | null; @@ -55,6 +56,7 @@ type RequestProviderProps = { isAmp?: boolean; isApp?: boolean; isLite?: boolean; + isOffline?: boolean; isNextJs?: boolean; pageType: PageTypes; pathname: string; @@ -79,6 +81,7 @@ export const RequestContextProvider = ({ isAmp = false, isApp = false, isLite = false, + isOffline = false, isNextJs = false, serverSideExperiments = null, pageType, @@ -107,6 +110,8 @@ export const RequestContextProvider = ({ return 'amp'; case isLite: return 'lite'; + case isOffline: + return 'canonical'; default: return 'canonical'; } @@ -130,6 +135,7 @@ export const RequestContextProvider = ({ isAmp, isApp, isLite, + isOffline, isNextJs, platform, statsDestination, @@ -154,6 +160,7 @@ export const RequestContextProvider = ({ isAmp, isApp, isLite, + isOffline, isNextJs, serverSideExperiments, origin, diff --git a/src/app/legacy/containers/PageHandlers/__snapshots__/withContexts.test.jsx.snap b/src/app/legacy/containers/PageHandlers/__snapshots__/withContexts.test.jsx.snap index c8bb49b19af..643eabddc56 100644 --- a/src/app/legacy/containers/PageHandlers/__snapshots__/withContexts.test.jsx.snap +++ b/src/app/legacy/containers/PageHandlers/__snapshots__/withContexts.test.jsx.snap @@ -6,7 +6,7 @@ exports[`withContexts HOC should return all context providers 1`] = ` {"lang":"en-GB","articleAuthor":"https://www.facebook.com/bbcnews","articleTimestampPrefix":"Updated","articleTimestampSuffix":"","atiAnalyticsAppName":"news","atiAnalyticsProducerId":"64","atiAnalyticsProducerName":"NEWS","chartbeatDomain":"bbc.co.uk","brandName":"BBC News","product":"BBC News","defaultImage":"https://static.files.bbci.co.uk/ws/simorgh-assets/public/news/images/metadata/poster-1024x576.png","defaultImageAltText":"BBC News","dir":"ltr","externalLinkText":", external","imageCaptionOffscreenText":"Image caption, ","videoCaptionOffscreenText":"Video caption, ","audioCaptionOffscreenText":"Audio caption","defaultCaptionOffscreenText":"Caption, ","imageCopyrightOffscreenText":"Image source, ","locale":"en_GB","datetimeLocale":"en-gb","service":"news","serviceName":"News","languageName":"English","twitterCreator":"@BBCNews","twitterSite":"@BBCNews","noBylinesPolicy":"https://www.bbc.com/news/help-41670342#authorexpertise","publishingPrinciples":"https://www.bbc.com/news/help-41670342","isTrustProjectParticipant":true,"script":{"atlas":{"groupA":{"fontSize":78,"lineHeight":84},"groupB":{"fontSize":96,"lineHeight":104},"groupD":{"fontSize":140,"lineHeight":148}},"elephant":{"groupA":{"fontSize":60,"lineHeight":64},"groupB":{"fontSize":78,"lineHeight":84},"groupD":{"fontSize":116,"lineHeight":124}},"imperial":{"groupA":{"fontSize":50,"lineHeight":54},"groupB":{"fontSize":64,"lineHeight":72},"groupD":{"fontSize":96,"lineHeight":104}},"royal":{"groupA":{"fontSize":40,"lineHeight":44},"groupB":{"fontSize":52,"lineHeight":60},"groupD":{"fontSize":76,"lineHeight":84}},"foolscap":{"groupA":{"fontSize":32,"lineHeight":36},"groupB":{"fontSize":40,"lineHeight":44},"groupD":{"fontSize":56,"lineHeight":60}},"canon":{"groupA":{"fontSize":28,"lineHeight":32},"groupB":{"fontSize":32,"lineHeight":36},"groupD":{"fontSize":44,"lineHeight":48}},"trafalgar":{"groupA":{"fontSize":20,"lineHeight":24},"groupB":{"fontSize":24,"lineHeight":28},"groupD":{"fontSize":32,"lineHeight":36}},"paragon":{"groupA":{"fontSize":20,"lineHeight":24},"groupB":{"fontSize":22,"lineHeight":26},"groupD":{"fontSize":28,"lineHeight":32}},"doublePica":{"groupA":{"fontSize":18,"lineHeight":22},"groupB":{"fontSize":20,"lineHeight":24},"groupD":{"fontSize":24,"lineHeight":28}},"greatPrimer":{"groupA":{"fontSize":18,"lineHeight":22},"groupB":{"fontSize":18,"lineHeight":22},"groupD":{"fontSize":20,"lineHeight":24}},"bodyCopy":{"groupA":{"fontSize":15,"lineHeight":20},"groupB":{"fontSize":16,"lineHeight":22},"groupD":{"fontSize":16,"lineHeight":22}},"pica":{"groupA":{"fontSize":15,"lineHeight":20},"groupB":{"fontSize":16,"lineHeight":20},"groupD":{"fontSize":16,"lineHeight":20}},"longPrimer":{"groupA":{"fontSize":15,"lineHeight":18},"groupB":{"fontSize":15,"lineHeight":18},"groupD":{"fontSize":14,"lineHeight":18}},"brevier":{"groupA":{"fontSize":14,"lineHeight":18},"groupB":{"fontSize":14,"lineHeight":18},"groupD":{"fontSize":13,"lineHeight":16}},"minion":{"groupA":{"fontSize":12,"lineHeight":16},"groupB":{"fontSize":12,"lineHeight":16},"groupD":{"fontSize":12,"lineHeight":16}}},"manifestPath":"https://static.files.bbci.co.uk/core/manifest.1ccdbcfd4cc3bf889128a50903c2b22c81758637.json","homePageTitle":"Home","showAdPlaceholder":false,"showRelatedTopics":true,"translations":{"ads":{"advertisementLabel":"Advertisement"},"home":"Home","currentPage":"Current page","skipLinkText":"Skip to content","relatedContent":"Related content","relatedTopics":"Related topics","moreOnThis":"","navMenuText":"Sections","mediaAssetPage":{"mediaPlayer":"Media player","audioPlayer":"Audio player","videoPlayer":"Video player"},"liveExperiencePage":{"liveLabel":"Live","liveCoverage":"Live Coverage","breaking":"Breaking","postedAt":"Posted at","summary":"Summary","shareButtonText":"Share"},"downloads":{"instructions":"You can download and view today’s news.","title":"File Download"},"gist":"At a glance","error":{"404":{"statusCode":"404","title":"Page cannot be found","message":"Sorry, we're unable to bring you the page you're looking for. Please try:","solutions":["Double checking the url","Hitting the refresh button in your browser","Searching for this page using the BBC search bar"],"callToActionFirst":"Alternatively, please visit the ","callToActionLinkText":"BBC News homepage.","callToActionLast":"","callToActionLinkUrl":"https://www.bbc.com/news"},"500":{"statusCode":"500","title":"Internal server error","message":"Sorry, we're currently unable to bring you the page you're looking for. Please try:","solutions":["Hitting the refresh button in your browser","Coming back again later"],"callToActionFirst":"Alternatively, please visit the ","callToActionLinkText":"BBC News homepage.","callToActionLast":"","callToActionLinkUrl":"https://www.bbc.com/news"}},"consentBanner":{"privacy":{"title":"We've updated our Privacy and Cookies Policy","description":{"uk":{"first":"We've made some important changes to our Privacy and Cookies Policy and we want you to know what this means for you and your data.","linkText":null,"last":null,"linkUrl":null},"international":{"first":"We've made some important changes to our Privacy and Cookies Policy and we want you to know what this means for you and your data.","linkText":null,"last":null,"linkUrl":null}},"accept":"OK","reject":"Find out what's changed","rejectUrl":"https://www.bbc.co.uk/usingthebbc/privacy-policy/"},"cookie":{"amp":{"accept":"Accept data collection and continue","reject":"Reject data collection and continue","initial":{"title":"Let us know you agree to data collection on AMP","description":{"first":"We and our partners use technologies, such as ","linkText":"cookies","last":", and collect browsing data to give you the best online experience and to personalise the content and advertising shown to you. Please let us know if you agree.","linkUrl":"https://www.bbc.co.uk/usingthebbc/cookies/what-do-i-need-to-know-about-cookies/"},"manage":"Manage my settings"},"manage":{"title":"Manage consent settings on AMP pages","description":{"para1":"These settings apply to AMP pages only. You may be asked to set these preferences again when you visit non-AMP BBC pages.","para2":"The lightweight mobile page you have visited has been built using Google AMP technology.","heading2":"Strictly necessary data collection","para3":"To make our web pages work, we store some limited information on your device without your consent.","para4":{"text":"Read more about the essential information we store on your device to make our web pages work.","url":"https://www.bbc.co.uk/usingthebbc/strictly-necessary-cookies/"},"para5":"We use local storage to store your consent preferences on your device.","heading3":"Optional data collection","para6":"When you consent to data collection on AMP pages you are consenting to allow us to display personalised ads that are relevant to you when you are outside of the UK.","para7":{"text":"Read more about how we personalise ads in the BBC and our advertising partners.","url":"https://www.bbc.com/usingthebbc/cookies/how-does-the-bbc-use-cookies-for-advertising/"},"para8":"You can choose not to receive personalised ads by clicking “Reject data collection and continue” below. Please note that you will still see advertising, but it will not be personalised to you.","para9":"You can change these settings by clicking “Ad Choices / Do not sell my info” in the footer at any time."}}},"canonical":{"title":"Let us know you agree to cookies","description":{"uk":{"first":"We use ","linkText":"cookies","last":" to give you the best online experience. Please let us know if you agree to all of these cookies.","linkUrl":"https://www.bbc.co.uk/usingthebbc/cookies/what-do-i-need-to-know-about-cookies/"},"international":{"first":"We use ","linkText":"cookies","last":" to give you the best online experience. Please let us know if you agree to all of these cookies.","linkUrl":"https://www.bbc.co.uk/usingthebbc/cookies/what-do-i-need-to-know-about-cookies/"}},"accept":"Yes, I agree","reject":"No, take me to settings","rejectUrl":"https://www.bbc.co.uk/usingthebbc/cookies/how-can-i-change-my-bbc-cookie-settings/"}}},"media":{"noJs":"To play this content, please enable JavaScript, or try a different browser","contentExpired":"This content is no longer available","audio":"Audio","photogallery":"Image gallery","video":"Video","listen":"Listen","watch":"Watch","liveLabel":"LIVE","nextLabel":"NEXT","previousRadioShow":"Previous radio show","nextRadioShow":"Next radio show","duration":"Duration"},"socialEmbed":{},"featuresAnalysisTitle":"More to explore"},"mostRead":{"header":"Popular Reads","lastUpdated":"Last updated:","numberOfItems":10,"hasMostRead":true},"radioSchedule":{"hasRadioSchedule":false},"footer":{"trustProjectLink":{"href":"https://www.bbc.com/news/help-41670342","text":"Why you can trust the BBC"},"externalLink":{"href":"https://www.bbc.co.uk/editorialguidelines/guidance/feeds-and-links","text":"Read about our approach to external linking."},"links":[{"href":"https://www.bbc.com/terms","text":"Terms of Use"},{"href":"https://www.bbc.co.uk/aboutthebbc/","text":"About the BBC"},{"href":"https://www.bbc.com/privacy/","text":"Privacy Policy"},{"href":"https://www.bbc.com/usingthebbc/cookies/","text":"Cookies"},{"href":"https://www.bbc.com/accessibility/","text":"Accessibility Help"},{"href":"https://www.bbc.com/contact/","text":"Contact the BBC"},{"id":"COOKIE_SETTINGS","href":"#","text":"Do not share or sell my info","lang":"en-GB"}],"copyrightText":"BBC. The BBC is not responsible for the content of external sites."},"timezone":"Europe/London","navigation":[{"title":"Home","url":"/news"},{"title":"UK","url":"/news/uk"},{"title":"World","url":"/news/world"},{"title":"Business","url":"/news/business"},{"title":"Politics","url":"/news/politics"},{"title":"Tech","url":"/news/technology"},{"title":"Science","url":"/news/science_and_environment"},{"title":"Health","url":"/news/health"},{"title":"Family & Education","url":"/news/education"},{"title":"Entertainment & Arts","url":"/news/entertainment_and_arts"},{"title":"Stories","url":"/news/stories"}]} - {"env":"live","id":"c0000000000o","isUK":true,"origin":"https://www.bbc.com","pageType":"article","derivedPageType":null,"isAmp":true,"isApp":false,"isLite":false,"isNextJs":false,"platform":"amp","statsDestination":"NEWS_PS_TEST","statusCode":200,"variant":null,"timeOnServer":null,"showAdsBasedOnLocation":true,"showCookieBannerBasedOnCountry":true,"service":"news","pathname":"/pathname","canonicalLink":"https://www.bbc.com/pathname","ampLink":"https://www.bbc.com/pathname.amp","canonicalUkLink":"https://www.bbc.co.uk/pathname","ampUkLink":"https://www.bbc.co.uk/pathname.amp","canonicalNonUkLink":"https://www.bbc.com/pathname","ampNonUkLink":"https://www.bbc.com/pathname.amp","serverSideExperiments":[{"experimentName":"foo","variation":"bar"}],"country":null,"nonce":null,"cspHeader":null} + {"env":"live","id":"c0000000000o","isUK":true,"origin":"https://www.bbc.com","pageType":"article","derivedPageType":null,"isAmp":true,"isApp":false,"isLite":false,"isOffline":false,"isNextJs":false,"platform":"amp","statsDestination":"NEWS_PS_TEST","statusCode":200,"variant":null,"timeOnServer":null,"showAdsBasedOnLocation":true,"showCookieBannerBasedOnCountry":true,"service":"news","pathname":"/pathname","canonicalLink":"https://www.bbc.com/pathname","ampLink":"https://www.bbc.com/pathname.amp","canonicalUkLink":"https://www.bbc.co.uk/pathname","ampUkLink":"https://www.bbc.co.uk/pathname.amp","canonicalNonUkLink":"https://www.bbc.com/pathname","ampNonUkLink":"https://www.bbc.com/pathname.amp","serverSideExperiments":[{"experimentName":"foo","variation":"bar"}],"country":null,"nonce":null,"cspHeader":null} {"toggleState":{"testToggle":{"enabled":false}},"toggleDispatch":"*function - bound dispatchReducerAction*"} diff --git a/src/app/lib/regex.const.js b/src/app/lib/regex.const.js index 53411412fb8..346485f7c84 100644 --- a/src/app/lib/regex.const.js +++ b/src/app/lib/regex.const.js @@ -2,6 +2,7 @@ const regexes = { AMP_REGEX: /\.amp$/, APP_REGEX: /\.app$/, LITE_REGEX: /\.lite$/, + OFFLINE_REGEX: /\.offline$/, TLD_REGEX: /(\.com|\.co\.uk)/g, }; diff --git a/src/app/pages/ArticlePage/ArticlePage.tsx b/src/app/pages/ArticlePage/ArticlePage.tsx index 9610c828c45..7f1a219ffe5 100644 --- a/src/app/pages/ArticlePage/ArticlePage.tsx +++ b/src/app/pages/ArticlePage/ArticlePage.tsx @@ -177,7 +177,7 @@ const getContinueReadingButton = const ArticlePage = ({ pageData }: { pageData: Article }) => { const [showAllContent, setShowAllContent] = useState(false); - const { isApp, isAmp, isLite } = use(RequestContext); + const { isApp, isAmp, isLite, isOffline } = use(RequestContext); const { articleAuthor, isTrustProjectParticipant, @@ -329,15 +329,19 @@ const ArticlePage = ({ pageData }: { pageData: Article }) => { return (
{/* EXPERIMENT: PWA Promotional Banner */} - {shouldRenderPWAPromotionalBanner && } - - - - + {!isOffline && shouldRenderPWAPromotionalBanner && ( + + )} + {!isOffline && } + {!isOffline && ( + + )} + {!isOffline && } + {!isOffline && } { aboutTags={aboutTags} imageLocator={promoImage} /> - {allowAdvertising && ( + {!isOffline && allowAdvertising && ( )} @@ -399,9 +403,11 @@ const ArticlePage = ({ pageData }: { pageData: Article }) => {
- {!isApp && !isPGL && } + {!isApp && !isPGL && !isOffline && ( + + )} - {!isApp && !isPGL && ( + {!isApp && !isPGL && !isOffline && ( { }); }); }); + + describe('isOfflinePath', () => { + [ + { + description: 'should return true if path ends in ".offline"', + path: '/news/foobar.offline', + expectedIsOffline: true, + }, + { + description: + 'should return false if path contains ".offline" but does not end in it', + path: '/news/foobar.offlinefoo', + expectedIsOffline: false, + }, + { + description: + 'should return false if path only contains ".offline" as part of the trailing text', + path: '/news/foobar.fooofflinebar', + expectedIsOffline: false, + }, + { + description: + 'should return true when path ends with .offline and has renderer_env override specified', + path: '/news/foobar.offline?renderer_env=live', + expectedIsOffline: true, + }, + { + description: + 'should return true when path ends with .offline and has any query params specified', + path: '/news/foobar.offline?blah=1', + expectedIsOffline: true, + }, + { + description: 'should return false if path ends in just "offline"', + path: '/news/foobar/offline', + expectedIsOffline: false, + }, + ].forEach(({ description, path, expectedIsOffline }) => { + it(description, () => { + expect(getPathExtension(path).isOffline).toEqual(expectedIsOffline); + }); + }); + }); }); diff --git a/src/app/utilities/getPathExtension/index.ts b/src/app/utilities/getPathExtension/index.ts index a836317b137..abd7e30eb37 100644 --- a/src/app/utilities/getPathExtension/index.ts +++ b/src/app/utilities/getPathExtension/index.ts @@ -1,5 +1,10 @@ import Url from 'url-parse'; -import { APP_REGEX, AMP_REGEX, LITE_REGEX } from '#app/lib/regex.const'; +import { + APP_REGEX, + AMP_REGEX, + LITE_REGEX, + OFFLINE_REGEX, +} from '#app/lib/regex.const'; export default (url: string) => { const { pathname } = new Url(url, true); @@ -8,5 +13,6 @@ export default (url: string) => { isAmp: AMP_REGEX.test(pathname), isApp: APP_REGEX.test(pathname), isLite: LITE_REGEX.test(pathname), + isOffline: OFFLINE_REGEX.test(pathname), }; }; diff --git a/ws-nextjs-app/pages/_app.page.tsx b/ws-nextjs-app/pages/_app.page.tsx index d5c00c2c1d3..c190c134811 100644 --- a/ws-nextjs-app/pages/_app.page.tsx +++ b/ws-nextjs-app/pages/_app.page.tsx @@ -34,6 +34,7 @@ interface Props { isAmp: boolean; isApp?: boolean; isLite?: boolean; + isOffline?: boolean; isNextJs: boolean; isAvEmbeds?: boolean; serverSideExperiments: ServerSideExperiment[] | null; @@ -64,7 +65,7 @@ export default class CustomApp extends App { static async getInitialProps({ ctx }: AppContext) { const { asPath = '' } = ctx; - const { isApp, isAmp, isLite } = getPathExtension(asPath); + const { isApp, isAmp, isLite, isOffline } = getPathExtension(asPath); const { service } = parseRoute(asPath) as { service: Services }; @@ -91,6 +92,7 @@ export default class CustomApp extends App { isApp, isAmp, isLite, + isOffline, isNextJs: true, serverSideExperiments, toggles, @@ -107,6 +109,7 @@ export default class CustomApp extends App { isAmp, isApp = false, isLite = false, + isOffline = false, isNextJs = true, isAvEmbeds = false, serverSideExperiments = null, @@ -147,6 +150,7 @@ export default class CustomApp extends App { isAmp={isAmp} isApp={isApp} isLite={isLite} + isOffline={isOffline} pageType={pageType} service={service} statusCode={status} diff --git a/ws-nextjs-app/pages/_document.page.tsx b/ws-nextjs-app/pages/_document.page.tsx index 77a2d859cbc..b50e6a21c76 100644 --- a/ws-nextjs-app/pages/_document.page.tsx +++ b/ws-nextjs-app/pages/_document.page.tsx @@ -43,6 +43,7 @@ type DocProps = { isAmp: boolean; isApp: boolean; isLite: boolean; + isOffline: boolean; title: ReactElement; }; @@ -51,7 +52,7 @@ export default class AppDocument extends Document { const url = ctx.asPath || ''; const pageType = derivePageType(url); - const { isApp, isAmp, isLite } = getPathExtension(url); + const { isApp, isAmp, isLite, isOffline } = getPathExtension(url); const cache = createCache({ key: 'css' }); const { extractCritical } = createEmotionServer(cache); @@ -89,6 +90,7 @@ export default class AppDocument extends Document { isAmp, isApp, isLite, + isOffline, }; } @@ -102,6 +104,7 @@ export default class AppDocument extends Document { isAmp, isApp, isLite, + isOffline, } = this.props; const htmlAttrs = helmet.htmlAttributes.toComponent(); @@ -153,6 +156,7 @@ export default class AppDocument extends Document { {`window.SIMORGH_ENV_VARS=${JSON.stringify(clientSideEnvVariables)}`} {isApp && } + {isOffline && } {title} {helmetMetaTags} {helmetLinkTags}