Skip to content

Commit

Permalink
feat(fbt): Add ability to provide your own ViewerContext dynamically
Browse files Browse the repository at this point in the history
Reviewed By: jrwats

Differential Revision: D21593501

fbshipit-source-id: 030470e650836e9e7f570190a993d82ad2ad9dd5
  • Loading branch information
kayhadrin authored and facebook-github-bot committed May 28, 2020
1 parent f58d7c2 commit df2414a
Show file tree
Hide file tree
Showing 22 changed files with 225 additions and 136 deletions.
2 changes: 1 addition & 1 deletion .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module.name_mapper='^\(cx\|FBLogger\|FBLogMessage\|FbtQTOverrides\|FbtResultGK\|
module.name_mapper='^\(DisplayGenderConst\)$' -> '<PROJECT_ROOT>/runtime/shared_deps/\1'

# Redirect public JS modules to a dedicated folder
module.name_mapper='^\(escapeRegex\|fbs\|fbt\|FbtHooks\|FbtPureStringResult\|FbtReactUtil\|FbtResultBase\|FbtResult\|FbtTable\|FbtTableAccessor\|formatNumber\|getFbsResult\|IntlGender\|intlList\|intlNumUtils\|IntlPunctuation\|IntlVariationResolverImpl\|substituteTokens\)$' -> '<PROJECT_ROOT>/runtime/shared/\1'
module.name_mapper='^\(escapeRegex\|fbs\|fbt\|FbtHooks\|FbtHooksImpl\|FbtPureStringResult\|FbtReactUtil\|FbtResultBase\|FbtResult\|FbtTable\|FbtTableAccessor\|formatNumber\|getFbsResult\|IntlGender\|intlList\|intlNumUtils\|IntlPunctuation\|IntlVariationResolverImpl\|substituteTokens\)$' -> '<PROJECT_ROOT>/runtime/shared/\1'
module.name_mapper='^\(IntlCLDRNumberType[0-9][0-9]\)$' -> '<PROJECT_ROOT>/runtime/shared/FbtNumber/\1'
# This Jest mock file is only used for testing, we don't need it on the OSS side
module.name_mapper='^\(FbtNumberType\)$' -> '<PROJECT_ROOT>/runtime/shared/__mocks__/\1'
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ We haven't had the best track record of code/feature changes before this date, b
<summary>
Unreleased changes that have landed in master. Click to see more.
</summary>
- [major feat] Add ability to provide your own ViewerContext dynamically
- [fix] Render optional catch binding syntax to ES5 to fix [IE11 bug](https://github.com/facebook/fbt/pull/139)
- [feat] Convert `fbt.isFbtInstance()` to a predicate function for Flow
- [fix] Avoid generating unnecessary empty strings in fbt result contents
Expand Down
23 changes: 15 additions & 8 deletions demo-app/src/example/Example.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@

import './css/Example.css';
import classNames from 'classnames';
import fbt, {GenderConst, IntlVariations, init} from 'fbt';
import * as React from 'react';

const ExampleEnum = require('./Example$FbtEnum');

import fbt, {GenderConst, IntlVariations, IntlViewerContext, init} from 'fbt';
init({translations: require('../translatedFbts.json')});
const viewerContext = {
GENDER: IntlVariations.GENDER_UNKNOWN,
locale: 'en_US',
};

init({
translations: require('../translatedFbts.json'),
hooks: {
getViewerContext: () => viewerContext,
},
});

const LOCALES = Object.freeze({
en_US: Object.freeze({
Expand Down Expand Up @@ -85,7 +95,7 @@ export default class Example extends React.Component<Props, State> {
};

setLocale(locale: Locale) {
IntlViewerContext.locale = locale;
viewerContext.locale = locale;
this.setState({locale});
const html = document.getElementsByTagName('html')[0];
if (html != null) {
Expand Down Expand Up @@ -124,7 +134,7 @@ export default class Example extends React.Component<Props, State> {
className="neatoSelect"
onChange={(event: SyntheticUIEvent<>) => {
const vcGender = parseInt(event.target.value, 10);
IntlViewerContext.GENDER = vcGender;
viewerContext.GENDER = vcGender;
this.forceUpdate();
}}>
<option value={IntlVariations.GENDER_UNKNOWN}>
Expand Down Expand Up @@ -307,10 +317,7 @@ export default class Example extends React.Component<Props, State> {
className="bottom"
type="submit"
onClick={e => {
window.open(
'https://github.com/facebook/fbt',
'_blank',
);
window.open('https://github.com/facebook/fbt', '_blank');
}}>
{fbt('Try it out!', 'Sign up button')}
</button>
Expand Down
2 changes: 1 addition & 1 deletion flow-types/nonfb/libdef/flow-typed/jest_v24.x.x.js
Original file line number Diff line number Diff line change
Expand Up @@ -820,7 +820,7 @@ type JestObjectType = {
* Returns the actual module instead of a mock, bypassing all checks on
* whether the module should receive a mock implementation or not.
*/
requireActual(moduleName: string): any,
requireActual<T>(m: $Flow$ModuleRef<T> | string): T,
/**
* Returns a mock module instead of the actual module, bypassing all checks
* on whether the module should be required normally or not.
Expand Down
2 changes: 1 addition & 1 deletion runtime/nonfb/FbtEnv.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* An empty no-op for now.
*/
const FbtEnv = {
setup() {},
setupOnce() {},
};

module.exports = FbtEnv;
2 changes: 0 additions & 2 deletions runtime/nonfb/FbtPublic.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const FbtResult = require('FbtResult');
const FbtTranslations = require('FbtTranslations');
const GenderConst = require('GenderConst');
const IntlVariations = require('IntlVariations');
const IntlViewerContext = require('IntlViewerContext');

const fbt = require('fbt');
const init = require('fbtInit');
Expand All @@ -27,6 +26,5 @@ const FbtPublic = {
GenderConst,
init,
IntlVariations,
IntlViewerContext,
};
module.exports = FbtPublic;
12 changes: 6 additions & 6 deletions runtime/nonfb/FbtTranslations.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import type {FbtRuntimeCallInput, FbtTranslatedInput} from 'FbtHooks';

const IntlViewerContext = require('IntlViewerContext');
const FbtHooks = require('FbtHooks');

let translatedFbts = null;

Expand All @@ -29,20 +29,20 @@ const FbtTranslations = {
getTranslatedInput(input: FbtRuntimeCallInput): ?FbtTranslatedInput {
const {args, options} = input;
const hashKey = options?.hk;
const table =
translatedFbts != null && translatedFbts[IntlViewerContext.locale];
const {locale} = FbtHooks.getViewerContext();
const table = translatedFbts?.[locale];
if (__DEV__) {
if (!table && IntlViewerContext.locale !== DEFAULT_SRC_LOCALE) {
if (!table && locale !== DEFAULT_SRC_LOCALE) {
console.warn('Translations have not been provided');
}
}

if (!table || hashKey == null || table[hashKey] == null) {
if (hashKey == null || table?.[hashKey] == null) {
return null;
}
return {
table: table[hashKey],
args: args,
args,
};
},

Expand Down
6 changes: 6 additions & 0 deletions runtime/nonfb/fbtInit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
* @flow strict-local
* @format
*/

import type {FbtHookRegistrations} from 'FbtHooks';
import type {TranslationDict} from 'FbtTranslations';

const FbtHooks = require('FbtHooks');
const FbtResult = require('FbtResult');
const FbtTranslations = require('FbtTranslations');
const IntlViewerContext = require('IntlViewerContext'); // default VC

const getFbsResult = require('getFbsResult');

Expand All @@ -35,6 +37,10 @@ function fbtInit(input: FbtInitInput): void {
if (hooks.getTranslatedInput == null) {
hooks.getTranslatedInput = FbtTranslations.getTranslatedInput;
}
if (hooks.getViewerContext == null) {
hooks.getViewerContext = () => IntlViewerContext;
}

FbtHooks.register(hooks);
}

Expand Down
5 changes: 5 additions & 0 deletions runtime/nonfb/mocks/IntlViewerContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
* @flow strict
*/

// flowlint ambiguous-object-type:error

const IntlVariations = require('IntlVariations');

// Keep this in sync with IntlViewerContext.js.flow
// It's almost the same except that the `locale` field is optional on www
// and required in the OSS version
const IntlViewerContext = {
GENDER: IntlVariations.GENDER_UNKNOWN,
locale: 'en_US',
Expand Down
47 changes: 11 additions & 36 deletions runtime/shared/FbtHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @emails oncall+internationalization
* @flow strict
* @flow strict-local
* @format
*/

/* eslint-disable fb-www/flow-exact-by-default-object-types */

import type {FbtTableKey, PatternHash, PatternString} from 'FbtTable';
import type {FbtTableArg} from 'FbtTableAccessor';
import typeof IntlViewerContext from 'IntlViewerContext';

// TODO T61557741: Move these types to fbt.js when it's flow strict
export type FbtResolvedPayload = {|
Expand Down Expand Up @@ -72,47 +75,19 @@ export type FbtRuntimeCallInput = {
};

// TODO: T61015960 - getFb[st]Result should return types that are locked down
export type FbtHookRegistrations = $Shape<{
export type FbtHookRegistrations = $Shape<{|
errorListener: (context: FbtErrorContext) => IFbtErrorListener,
getFbsResult: (input: FbtResolvedPayload) => mixed,
getFbtResult: (input: FbtResolvedPayload) => mixed,
getTranslatedInput: (input: FbtRuntimeCallInput) => ?FbtTranslatedInput,
getViewerContext: () => IntlViewerContext,
logImpression: (hash: string) => void,
onTranslationOverride: (hash: string) => void,
...
}>;

const _registrations: FbtHookRegistrations = {};
const FbtHooks = {
getErrorListener(context: FbtErrorContext): ?IFbtErrorListener {
return _registrations.errorListener?.(context);
},

logImpression(hash: string): void {
_registrations.logImpression?.(hash);
},
|}>;

onTranslationOverride(hash: string): void {
_registrations.onTranslationOverride?.(hash);
},
const FbtEnv = require('FbtEnv');
const FbtHooksImpl = require('FbtHooksImpl');

// TODO: T61015960 - get off `mixed` and onto something more locked down (Fbs)
getFbsResult(input: FbtResolvedPayload): mixed {
return _registrations.getFbsResult(input);
},

// TODO: T61015960 - get off `mixed` and onto something more locked down (Fbt)
getFbtResult(input: FbtResolvedPayload): mixed {
return _registrations.getFbtResult(input);
},

getTranslatedInput(input: FbtRuntimeCallInput): FbtTranslatedInput {
return _registrations.getTranslatedInput?.(input) ?? input;
},

register(registrations: FbtHookRegistrations): void {
Object.assign(_registrations, registrations);
},
};
module.exports = FbtHooksImpl;

module.exports = FbtHooks;
FbtEnv.setupOnce();
56 changes: 56 additions & 0 deletions runtime/shared/FbtHooksImpl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @emails oncall+internationalization
* @flow strict-local
* @format
*/

/* eslint-disable fb-www/flow-exact-by-default-object-types */

import type {
FbtHookRegistrations,
FbtResolvedPayload,
FbtRuntimeCallInput,
FbtTranslatedInput,
} from 'FbtHooks';
import typeof IntlViewerContext from 'IntlViewerContext';

const _registrations: FbtHookRegistrations = {};
const FbtHooksImpl = {
getErrorListener(context: FbtErrorContext): ?IFbtErrorListener {
return _registrations.errorListener?.(context);
},

logImpression(hash: string): void {
_registrations.logImpression?.(hash);
},

onTranslationOverride(hash: string): void {
_registrations.onTranslationOverride?.(hash);
},

// TODO: T61015960 - get off `mixed` and onto something more locked down (Fbs)
getFbsResult(input: FbtResolvedPayload): mixed {
return _registrations.getFbsResult(input);
},

// TODO: T61015960 - get off `mixed` and onto something more locked down (Fbt)
getFbtResult(input: FbtResolvedPayload): mixed {
return _registrations.getFbtResult(input);
},

getTranslatedInput(input: FbtRuntimeCallInput): FbtTranslatedInput {
return _registrations.getTranslatedInput?.(input) ?? input;
},

getViewerContext(): IntlViewerContext {
return _registrations.getViewerContext();
},

register(registrations: FbtHookRegistrations): void {
Object.assign(_registrations, registrations);
},
};

module.exports = FbtHooksImpl;
2 changes: 1 addition & 1 deletion runtime/shared/FbtTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* recursively accessing the table entries and returning the leaves
*
* @emails oncall+internationalization
* @flow strict
* @flow strict-local
* @format
*/

Expand Down
2 changes: 1 addition & 1 deletion runtime/shared/FbtTableAccessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* specific implementation.
*
* @emails oncall+internationalization
* @flow strict
* @flow strict-local
* @format
*/

Expand Down
Loading

0 comments on commit df2414a

Please sign in to comment.