Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Commit

Permalink
Add preferred browser language fallback strategy (#13)
Browse files Browse the repository at this point in the history
* Add initial logic for retrieving the browser locale

* Add tests for browser utilities

* Cleanup

* Add support for browser language

* Update changelog

* Update prop description

Co-Authored-By: Tyler Biethman <[email protected]>

* Move files into private directory

* Update snapshots and private directory

* Add private directory disclaimer

* update dependencies

* revert update to dependencies. i only want to update dev dependencies

* update snapshots, update docker dev environment, update npm lock strategy

* :/

* Update snapshots and tests with action header, maybe this will work?

* keep the selector for slide-panel

* data attribute

* 25% confidence this will pass regularly

* Apply suggestions from code review

Co-Authored-By: Emily Rohrbough  <[email protected]>

* Update test selector

* Update WDIO test selector

Co-authored-by: Tyler Biethman <[email protected]>
Co-authored-by: Matt Henkes <[email protected]>
Co-authored-by: Emily Rohrbough  <[email protected]>
  • Loading branch information
4 people authored Mar 23, 2020
1 parent de2ec99 commit 2c25dd8
Show file tree
Hide file tree
Showing 53 changed files with 328 additions and 68 deletions.
1 change: 0 additions & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
package-lock=false
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
| xargs -n 2 -t sh -c 'test -e $HOME/docker/$1.tar.gz || docker save $0 | gzip -2 > $HOME/docker/$1.tar.gz'
- stage: lint and jest
script:
- "travis_wait docker-compose run -e HAS_JOSH_K_SEAL_OF_APPROVAL=$HAS_JOSH_K_SEAL_OF_APPROVAL -e TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST -e TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG test-ci bash -c 'npm run lint && npm run jest'"
- "travis_wait docker-compose run -e HAS_JOSH_K_SEAL_OF_APPROVAL=$HAS_JOSH_K_SEAL_OF_APPROVAL -e TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST -e TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG test-ci sh -c 'npm run lint && npm run jest'"
- stage: wdio
script:
- "travis_wait docker-compose run test-ci npm run wdio"
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Changelog
Unreleased
----------

### Changed
* Updated locale to be an optional prop for application base

### Added
* Added a fallback strategy for obtaining the preferred language from the browser if no locale is provided in application base

1.17.0 - (February 18, 2020)
------------------
### Changed
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
FROM cerner/terra-node
FROM cerner/terra-node-ci:1
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ terra-application is considered to be stable and will follow [SemVer](https://se

Consult the component CHANGELOGs, related issues, and PRs for more information.

## Private Directories

Javascript in `private` directories is not part of Terra's public API and should not be consumed directly.

## Contributing

Please read through our [contributing guidelines](CONTRIBUTING.md). Included are directions for issue reporting and pull requests.
Expand Down
30 changes: 9 additions & 21 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
version: '3'

services:
dev: &dev
image: "terra-application:dev"
build:
context: ./
args:
- npm_install=false
ports:
- 8080:8080
environment:
- CI=true
depends_on:
- standalone-chrome
volumes:
- .:/opt/module

standalone-chrome:
image: 'selenium/standalone-chrome:3.14.0-helium'
ports:
Expand All @@ -27,14 +12,17 @@ services:
environment:
TZ: 'America/Chicago'

term:
<<: *dev
command: "/bin/zsh"
dev:
image: cerner/terra-node-dev:1
ports:
- 8080:8080
depends_on:
- standalone-chrome
volumes:
- .:/opt/module

test-ci:
build: ./
image: "${DOCKER_IMAGE}-test:${TAG:-latest}"
environment:
- CI=true
depends_on:
- standalone-chrome
- standalone-chrome
56 changes: 28 additions & 28 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"tests/wdio/**/*-spec.js"
],
"scripts": {
"clean": "rm -rf node_modules",
"clean": "rm package-lock.json; rm -rf node_modules",
"clean:install": "npm run clean && npm install",
"clean:obsolete-snapshots": "npm test -- -u",
"compile": "babel src --out-dir lib --copy-files",
Expand Down Expand Up @@ -120,41 +120,41 @@
"react-intl": "^2.9.0"
},
"devDependencies": {
"@babel/cli": "^7.5.0",
"@babel/core": "^7.5.0",
"@babel/plugin-proposal-class-properties": "^7.4.0",
"@babel/plugin-proposal-object-rest-spread": "^7.5.0",
"@babel/plugin-transform-async-to-generator": "^7.5.0",
"@babel/plugin-transform-object-assign": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.5.0",
"@babel/preset-env": "^7.5.0",
"@babel/preset-react": "^7.0.0",
"babel-eslint": "^10.0.1",
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.0",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-object-rest-spread": "^7.9.0",
"@babel/plugin-transform-async-to-generator": "^7.8.3",
"@babel/plugin-transform-object-assign": "^7.8.3",
"@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-env": "^7.9.0",
"@babel/preset-react": "^7.9.1",
"babel-eslint": "^10.1.0",
"babel-jest": "^24.8.0",
"browserslist-config-terra": "^1.2.0",
"check-installed-dependencies": "^1.0.0",
"core-js": "^3.1.3",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"enzyme-to-json": "^3.2.2",
"eslint": "^6.1.0",
"eslint-config-terra": "^3.0.0",
"gh-pages": "^2.0.0",
"core-js": "^3.6.4",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"enzyme-to-json": "^3.4.4",
"eslint": "^6.8.0",
"eslint-config-terra": "^3.3.0",
"gh-pages": "^2.2.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.8.0",
"raf": "^3.4.1",
"react": "^16.8.5",
"react-dom": "^16.8.5",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-intl": "^2.9.0",
"regenerator-runtime": "^0.13.2",
"regenerator-runtime": "^0.13.5",
"stylelint": "^10.0.1",
"stylelint-config-terra": "^3.0.0",
"terra-collapsible-menu-view": "^6.19.0",
"terra-dev-site": "^6.5.0",
"terra-enzyme-intl": "^3.2.0",
"stylelint-config-terra": "^3.3.0",
"terra-collapsible-menu-view": "^6.29.0",
"terra-dev-site": "^6.15.0",
"terra-enzyme-intl": "^3.3.0",
"terra-toolkit": "^5.18.0",
"webpack": "^4.28.1",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.3.1"
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
}
}
8 changes: 6 additions & 2 deletions src/application-base/ApplicationBase.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ import ApplicationErrorBoundary from '../application-error-boundary';
import ApplicationLoadingOverlay, { ApplicationLoadingOverlayProvider } from '../application-loading-overlay';
import { NavigationPromptCheckpoint } from '../navigation-prompt';
import { ApplicationIntlProvider } from '../application-intl';
import getBrowserLocale from './private/getBrowserLocale';

import styles from './ApplicationBase.module.scss';

const cx = classNames.bind(styles);

const browserLocale = getBrowserLocale();

const propTypes = {
/**
* The components to render within ApplicationBase.
*/
children: PropTypes.node.isRequired,
/**
* The locale name to be used to load translated messages.
* If the `locale` prop is not provided, the preferred language from the browser will be used.
*/
locale: PropTypes.string.isRequired,
locale: PropTypes.string,
/**
* Custom translations for the current locale.
*/
Expand Down Expand Up @@ -105,7 +109,7 @@ const ApplicationBase = ({
<Base
customMessages={customTranslatedMessages}
translationsLoadingPlaceholder={translationsLoadingPlaceholder}
locale={locale}
locale={locale || browserLocale}
>
<ApplicationErrorBoundary>
<ApplicationIntlProvider>
Expand Down
64 changes: 64 additions & 0 deletions src/application-base/private/getBrowserLocale.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* global TERRA_AGGREGATED_LOCALES */

// The fallback locale that will be used if a supported locale is unable to be retrieved from the browser.
const DEFAULT_LOCALE = 'en';

/**
* Returns a boolean indicating if the locale is supported.
* @param {string} locale - The locale.
* @returns {bool} - A boolean indicating the locale is supported.
*/
const isSupported = (locale) => {
if (typeof TERRA_AGGREGATED_LOCALES === 'object' && Array.isArray(TERRA_AGGREGATED_LOCALES) && locale) {
return TERRA_AGGREGATED_LOCALES.indexOf(locale) > -1;
}

return false;
};

/**
* Filters the user's preferred languages from the browser and returns the first language found that is included in the supported locale list.
* @returns {string|null} - A supported locale. Null if not found.
*/
const filterLanguages = () => {
if (navigator.languages && navigator.languages.length > 0) {
for (let index = 0; index < navigator.languages.length; index += 1) {
const locale = navigator.languages[index];

if (isSupported(locale)) {
return locale;
}
}
}

return null;
};

/**
* Retrieves the preferred browser locale.
* @returns {string} - A supported browser locale. Falls back to en.
*/
const getBrowserLocale = () => {
const preferredLocale = filterLanguages();

if (preferredLocale) {
return preferredLocale;
}

if (isSupported(navigator.language)) {
return navigator.language;
}

if (isSupported(navigator.userLanguage)) {
return navigator.userLanguage;
}

if (isSupported(navigator.browserLanguage)) {
return navigator.browserLanguage;
}

return DEFAULT_LOCALE;
};

export default getBrowserLocale;
export { isSupported, filterLanguages };
4 changes: 1 addition & 3 deletions src/terra-dev-site/doc/application/demo/DemoAppIndex.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ const DemoAppIndex = () => {
const applicationIntl = useContext(ApplicationIntlContext);

return (
<ApplicationBase
locale={applicationIntl.locale || 'en-US'}
>
<ApplicationBase locale={applicationIntl.locale}>
<ModalManager>
<DemoAppNavigation />
</ModalManager>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const ApplicationContentTest = () => {
};

const ApplicationBaseTest = () => (
<ApplicationBase locale="en-US">
<ApplicationBase>
<ApplicationContentTest />
</ApplicationBase>
);
Expand Down
9 changes: 9 additions & 0 deletions tests/jest/application-base/ApplicationBase.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,13 @@ describe('ApplicationBase', () => {
));
expect(wrapper).toMatchSnapshot();
});

it('should render with the preferred browser local', () => {
const wrapper = shallow((
<ApplicationBase>
<div>content</div>
</ApplicationBase>
));
expect(wrapper).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,41 @@ exports[`ApplicationBase should render with minimal props 1`] = `
</Base>
</ThemeProvider>
`;

exports[`ApplicationBase should render with the preferred browser local 1`] = `
<ThemeProvider
className="application-theme-provider fill"
isGlobalTheme={false}
>
<Base
customMessages={Object {}}
locale="en"
strictMode={false}
>
<ApplicationErrorBoundary>
<InjectIntl(Component)>
<ActiveBreakpointProvider>
<withPromptRegistration(NavigationPromptCheckpoint)
onPromptChange={[Function]}
>
<ApplicationLoadingOverlayProvider>
<Suspense
fallback={
<ApplicationLoadingOverlay
backgroundStyle="clear"
isOpen={true}
/>
}
>
<div>
content
</div>
</Suspense>
</ApplicationLoadingOverlayProvider>
</withPromptRegistration(NavigationPromptCheckpoint)>
</ActiveBreakpointProvider>
</InjectIntl(Component)>
</ApplicationErrorBoundary>
</Base>
</ThemeProvider>
`;
Loading

0 comments on commit 2c25dd8

Please sign in to comment.