From 97e6727ccc00ae6988c1b45d3505f17207a70f0c Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 13 Aug 2020 17:19:54 +0200 Subject: [PATCH] feat: add support for older browsers --- package-lock.json | 18 ++++++-- package.json | 10 ++++- scripts/webpackHotDevClient.js | 2 +- src/core/client/framework/helpers/index.ts | 3 +- .../helpers/injectConditionalPolyfills.tsx | 41 ++++++++++++------- .../framework/helpers/polyfillCSSVars.tsx | 13 ++++++ .../helpers/polyfillCSSVarsForIE11.tsx | 8 ---- .../framework/helpers/polyfillIntlLocale.tsx | 16 ++++++++ .../framework/lib/bootstrap/createManaged.tsx | 2 + src/core/client/framework/lib/browserInfo.ts | 30 ++++++++++++-- src/core/client/framework/lib/relay/lookup.ts | 2 +- .../testHelpers/createTestRenderer.tsx | 7 ++++ .../stream/tabs/Configure/ConfigureQuery.tsx | 4 +- .../tabs/Discussions/DiscussionsQuery.tsx | 4 +- .../stream/tabs/Profile/ProfileQuery.tsx | 4 +- src/types/intl.d.ts | 1 + 16 files changed, 125 insertions(+), 40 deletions(-) create mode 100644 src/core/client/framework/helpers/polyfillCSSVars.tsx delete mode 100644 src/core/client/framework/helpers/polyfillCSSVarsForIE11.tsx create mode 100644 src/core/client/framework/helpers/polyfillIntlLocale.tsx create mode 100644 src/types/intl.d.ts diff --git a/package-lock.json b/package-lock.json index b893c4e9b..2348740cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16567,6 +16567,12 @@ "integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw==", "dev": true }, + "date-time-format-timezone": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/date-time-format-timezone/-/date-time-format-timezone-1.0.22.tgz", + "integrity": "sha512-4hEeKPpNlbFO05ldht9FwJEy2g1xL7kU3dTPY5hNSd1AyMjrrIeUS54kSWgt/KdttYshhjDMIonU+vCmL4NjVw==", + "dev": true + }, "dateformat": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", @@ -32159,6 +32165,12 @@ "integrity": "sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg==", "dev": true }, + "intl": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/intl/-/intl-1.2.5.tgz", + "integrity": "sha1-giRKIZDE5Bn4Nx9ao02qNCDiq94=", + "dev": true + }, "into-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", @@ -55354,9 +55366,9 @@ } }, "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz", + "integrity": "sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ==", "dev": true }, "whatwg-mimetype": { diff --git a/package.json b/package.json index 5cc9a596a..95289bbc8 100644 --- a/package.json +++ b/package.json @@ -264,6 +264,7 @@ "cross-spawn": "^7.0.1", "css-loader": "^3.4.2", "css-vars-ponyfill": "^2.2.1", + "date-time-format-timezone": "^1.0.22", "del": "^5.1.0", "doctoc": "^1.4.0", "docz": "^v2.3.0-alpha.14", @@ -296,6 +297,7 @@ "html-webpack-plugin": "^4.0.4", "husky": "^4.2.3", "intersection-observer": "^0.7.0", + "intl": "^1.2.5", "jest": "^26.1.0", "jest-axe": "^3.4.0", "jest-junit": "^11.0.1", @@ -381,7 +383,7 @@ "webpack-bundle-analyzer": "^3.8.0", "webpack-cli": "^3.3.12", "webpack-dev-server": "^3.11.0", - "whatwg-fetch": "^3.0.0" + "whatwg-fetch": "^3.4.0" }, "dependencies-pins-documentation": { "ts-node-dev@1.0.0-pre.44": [ @@ -456,8 +458,12 @@ "browsers": [ ">1%", "last 4 versions", - "Firefox ESR", "IE 11", + "iOS >= 9", + "Android >= 4.4.4", + "Edge >= 17", + "Firefox >= 68", + "Chrome >= 49", "not dead" ] } diff --git a/scripts/webpackHotDevClient.js b/scripts/webpackHotDevClient.js index 55448850a..174f62822 100644 --- a/scripts/webpackHotDevClient.js +++ b/scripts/webpackHotDevClient.js @@ -199,7 +199,7 @@ function handleAvailableHash(hash) { mostRecentCompilationHash = hash; } -const debouncedReload = debounce(function() { +var debouncedReload = debounce(function() { window.location.reload(); }, 1000); diff --git a/src/core/client/framework/helpers/index.ts b/src/core/client/framework/helpers/index.ts index a7f010f25..714861efe 100644 --- a/src/core/client/framework/helpers/index.ts +++ b/src/core/client/framework/helpers/index.ts @@ -12,5 +12,6 @@ export { default as resolveStoryURL } from "./resolveStoryURL"; export { default as detectCountScript } from "./detectCountScript"; export { default as potentiallyInjectAxe } from "./potentiallyInjectAxe"; export { default as injectConditionalPolyfills } from "./injectConditionalPolyfills"; -export { default as polyfillCSSVarsForIE11 } from "./polyfillCSSVarsForIE11"; +export { default as polyfillCSSVars } from "./polyfillCSSVars"; +export { default as polyfillIntlLocale } from "./polyfillIntlLocale"; export { default as getModerationLink, QUEUE_NAME } from "./getModerationLink"; diff --git a/src/core/client/framework/helpers/injectConditionalPolyfills.tsx b/src/core/client/framework/helpers/injectConditionalPolyfills.tsx index 245a151ff..b64c2a898 100644 --- a/src/core/client/framework/helpers/injectConditionalPolyfills.tsx +++ b/src/core/client/framework/helpers/injectConditionalPolyfills.tsx @@ -1,30 +1,41 @@ import { getBrowserInfo } from "../lib/browserInfo"; -import polyfillCSSVarsForIE11 from "./polyfillCSSVarsForIE11"; +import polyfillCSSVars from "./polyfillCSSVars"; export default async function injectConditionalPolyfills() { + const browser = getBrowserInfo(); const pending: Promise[] = []; // Polyfill Intl. - if (typeof Intl === "undefined" || !(Intl as any).PluralRules) { - pending.push(import("fluent-intl-polyfill")); + let intlPromise = Promise.resolve(); + if (!browser.supportsIntl) { + intlPromise = (async () => { + const IntlPolyfill = (await import("intl")).default; + Intl.NumberFormat = IntlPolyfill.NumberFormat; + Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat; + return; + })(); } - + pending.push( + intlPromise.then(() => { + if (!browser.supportsIntlPluralRules) { + return import("fluent-intl-polyfill"); + } + return; + }) + ); // Polyfill Intersection Observer. - if ( - !("IntersectionObserver" in window) || - !("IntersectionObserverEntry" in window) || - !( - "intersectionRatio" in (window as any).IntersectionObserverEntry.prototype - ) - ) { + if (!browser.supportsIntersectionObserver) { pending.push(import("intersection-observer")); } - // CSS Vars Polyfill for IE11. - if (getBrowserInfo().msie) { - pending.push(import("whatwg-fetch")); + if (!browser.supportsProxyObject) { pending.push(import("proxy-polyfill")); - pending.push(polyfillCSSVarsForIE11()); + } + if (!browser.supportsFetch) { + pending.push(import("whatwg-fetch")); + } + if (!browser.supportsCSSVariables) { + pending.push(polyfillCSSVars()); } await Promise.all(pending); } diff --git a/src/core/client/framework/helpers/polyfillCSSVars.tsx b/src/core/client/framework/helpers/polyfillCSSVars.tsx new file mode 100644 index 000000000..fc9cd2e88 --- /dev/null +++ b/src/core/client/framework/helpers/polyfillCSSVars.tsx @@ -0,0 +1,13 @@ +import { getBrowserInfo } from "../lib/browserInfo"; + +/** + * Polyfills CSS Variables. + * This needs to be called whenenver new css variables are introduced + * through new CSS. + */ +export default function polyfillCSSVars() { + if (!getBrowserInfo().supportsCSSVariables) { + return import("css-vars-ponyfill").then((module) => module.default()); + } + return Promise.resolve(); +} diff --git a/src/core/client/framework/helpers/polyfillCSSVarsForIE11.tsx b/src/core/client/framework/helpers/polyfillCSSVarsForIE11.tsx deleted file mode 100644 index af6413f28..000000000 --- a/src/core/client/framework/helpers/polyfillCSSVarsForIE11.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { getBrowserInfo } from "../lib/browserInfo"; - -export default function polyfillCSSVarsForIE11() { - if (getBrowserInfo().msie) { - return import("css-vars-ponyfill").then((module) => module.default()); - } - return Promise.resolve(); -} diff --git a/src/core/client/framework/helpers/polyfillIntlLocale.tsx b/src/core/client/framework/helpers/polyfillIntlLocale.tsx new file mode 100644 index 000000000..5eeffe740 --- /dev/null +++ b/src/core/client/framework/helpers/polyfillIntlLocale.tsx @@ -0,0 +1,16 @@ +import { getBrowserInfo } from "../lib/browserInfo"; + +/** + * Polyfills Intl Locale Data. + * This is only needed when we use the Intl Polyfill. + */ +export default async function polyfillIntlLocale(locales: string[]) { + if (!getBrowserInfo().supportsIntl) { + await Promise.all( + locales.map((locale) => + import("intl/locale-data/jsonp/" + locale + ".js") + ) + ); + } + return Promise.resolve(); +} diff --git a/src/core/client/framework/lib/bootstrap/createManaged.tsx b/src/core/client/framework/lib/bootstrap/createManaged.tsx index 3eedf4f9f..253c2cee9 100644 --- a/src/core/client/framework/lib/bootstrap/createManaged.tsx +++ b/src/core/client/framework/lib/bootstrap/createManaged.tsx @@ -8,6 +8,7 @@ import { Environment, RecordSource, Store } from "relay-runtime"; import { v1 as uuid } from "uuid"; import { LanguageCode } from "coral-common/helpers/i18n"; +import polyfillIntlLocale from "coral-framework/helpers/polyfillIntlLocale"; import { getBrowserInfo } from "coral-framework/lib/browserInfo"; import { RestClient } from "coral-framework/lib/rest"; import { @@ -309,6 +310,7 @@ export default async function createManaged({ } const localeBundles = await generateBundles(locales, localesData); + await polyfillIntlLocale(locales); // Get the access token from storage. const auth = retrieveAccessToken(); diff --git a/src/core/client/framework/lib/browserInfo.ts b/src/core/client/framework/lib/browserInfo.ts index 067c2d614..d3e22fa2d 100644 --- a/src/core/client/framework/lib/browserInfo.ts +++ b/src/core/client/framework/lib/browserInfo.ts @@ -1,9 +1,16 @@ import Bowser from "bowser"; export interface BrowserInfo { + version: number; ios: boolean; msie: boolean; mobile: boolean; + supportsCSSVariables: boolean; + supportsFetch: boolean; + supportsProxyObject: boolean; + supportsIntl: boolean; + supportsIntlPluralRules: boolean; + supportsIntersectionObserver: boolean; } let browserInfo: BrowserInfo | null = null; @@ -11,10 +18,27 @@ let browserInfo: BrowserInfo | null = null; export function getBrowserInfo(): BrowserInfo { if (!browserInfo) { const browser = Bowser.getParser(window.navigator.userAgent); + const ios = browser.is(Bowser.OS_MAP.iOS); + const msie = browser.is(Bowser.BROWSER_MAP.ie); + const mobile = browser.is(Bowser.PLATFORMS_MAP.mobile); + const version = Number.parseFloat(browser.getBrowserVersion()); browserInfo = { - ios: browser.is(Bowser.OS_MAP.iOS), - msie: browser.is(Bowser.BROWSER_MAP.ie), - mobile: browser.is(Bowser.PLATFORMS_MAP.mobile), + version, + supportsIntl: typeof Intl !== "undefined", + supportsIntlPluralRules: + typeof Intl !== "undefined" && Boolean(Intl.PluralRules), + supportsProxyObject: Boolean(window.Proxy), + supportsCSSVariables: + window.CSS && CSS.supports("color", "var(--fake-var)"), + supportsFetch: Boolean(window.fetch), + supportsIntersectionObserver: + "IntersectionObserver" in window && + "IntersectionObserverEntry" in window && + "intersectionRatio" in + (window as any).IntersectionObserverEntry.prototype, + ios, + msie, + mobile, }; } return browserInfo; diff --git a/src/core/client/framework/lib/relay/lookup.ts b/src/core/client/framework/lib/relay/lookup.ts index 9c4ddf397..b62a44fad 100644 --- a/src/core/client/framework/lib/relay/lookup.ts +++ b/src/core/client/framework/lib/relay/lookup.ts @@ -56,7 +56,7 @@ const createProxy = ( // IE11 does not have Proxy support and the polyfill only supports // a subset of features under special circumstances. // https://github.com/GoogleChrome/proxy-polyfill - if (getBrowserInfo().msie) { + if (!getBrowserInfo().supportsProxyObject) { target = recordSource; delete proxy.ownKeys; delete proxy.getOwnPropertyDescriptor; diff --git a/src/core/client/framework/testHelpers/createTestRenderer.tsx b/src/core/client/framework/testHelpers/createTestRenderer.tsx index 1f5fd7dfb..ca1f8ae89 100644 --- a/src/core/client/framework/testHelpers/createTestRenderer.tsx +++ b/src/core/client/framework/testHelpers/createTestRenderer.tsx @@ -96,6 +96,13 @@ export default function createTestRenderer< rest: new RestClient("http://localhost/api"), postMessage: new PostMessageService(), browserInfo: params.browserInfo || { + supportsCSSVariables: true, + supportsIntersectionObserver: true, + supportsFetch: true, + supportsIntl: true, + supportsIntlPluralRules: true, + supportsProxyObject: true, + version: "10.0", ios: false, mobile: false, msie: false, diff --git a/src/core/client/stream/tabs/Configure/ConfigureQuery.tsx b/src/core/client/stream/tabs/Configure/ConfigureQuery.tsx index 2b2083c00..0f528a171 100644 --- a/src/core/client/stream/tabs/Configure/ConfigureQuery.tsx +++ b/src/core/client/stream/tabs/Configure/ConfigureQuery.tsx @@ -3,7 +3,7 @@ import { once } from "lodash"; import React, { FunctionComponent, Suspense } from "react"; import { graphql } from "react-relay"; -import { polyfillCSSVarsForIE11 } from "coral-framework/helpers"; +import { polyfillCSSVars } from "coral-framework/helpers"; import { QueryRenderData, QueryRenderer, @@ -18,7 +18,7 @@ const loadConfigureContainer = () => import("./ConfigureContainer" /* webpackChunkName: "configure" */).then( (x) => { // New css is loaded, take care of polyfilling those css vars for IE11. - void polyfillCSSVarsForIE11(); + void polyfillCSSVars(); return x; } ); diff --git a/src/core/client/stream/tabs/Discussions/DiscussionsQuery.tsx b/src/core/client/stream/tabs/Discussions/DiscussionsQuery.tsx index e2959f1d4..670f382fa 100644 --- a/src/core/client/stream/tabs/Discussions/DiscussionsQuery.tsx +++ b/src/core/client/stream/tabs/Discussions/DiscussionsQuery.tsx @@ -3,7 +3,7 @@ import { once } from "lodash"; import React, { FunctionComponent, Suspense } from "react"; import { graphql } from "react-relay"; -import { polyfillCSSVarsForIE11 } from "coral-framework/helpers"; +import { polyfillCSSVars } from "coral-framework/helpers"; import { QueryRenderData, QueryRenderer, @@ -19,7 +19,7 @@ const loadDiscussionsContainer = () => import("./DiscussionsContainer" /* webpackChunkName: "profile" */).then( (x) => { // New css is loaded, take care of polyfilling those css vars for IE11. - void polyfillCSSVarsForIE11(); + void polyfillCSSVars(); return x; } ); diff --git a/src/core/client/stream/tabs/Profile/ProfileQuery.tsx b/src/core/client/stream/tabs/Profile/ProfileQuery.tsx index 3b36dc183..233a327f9 100644 --- a/src/core/client/stream/tabs/Profile/ProfileQuery.tsx +++ b/src/core/client/stream/tabs/Profile/ProfileQuery.tsx @@ -3,7 +3,7 @@ import { once } from "lodash"; import React, { FunctionComponent, Suspense } from "react"; import { graphql } from "react-relay"; -import { polyfillCSSVarsForIE11 } from "coral-framework/helpers"; +import { polyfillCSSVars } from "coral-framework/helpers"; import { QueryRenderData, QueryRenderer, @@ -18,7 +18,7 @@ import { ProfileQueryLocal as Local } from "coral-stream/__generated__/ProfileQu const loadProfileContainer = () => import("./ProfileContainer" /* webpackChunkName: "profile" */).then((x) => { // New css is loaded, take care of polyfilling those css vars for IE11. - void polyfillCSSVarsForIE11(); + void polyfillCSSVars(); return x; }); // (cvle) For some reason without `setTimeout` this request will block other requests. diff --git a/src/types/intl.d.ts b/src/types/intl.d.ts new file mode 100644 index 000000000..567af93d5 --- /dev/null +++ b/src/types/intl.d.ts @@ -0,0 +1 @@ +declare module "intl";