Skip to content

Commit

Permalink
feat: support textComponent="", fixes #1330 (#1354)
Browse files Browse the repository at this point in the history
  • Loading branch information
longlho authored Jul 7, 2019
1 parent 01eef5c commit 3f27902
Show file tree
Hide file tree
Showing 12 changed files with 91 additions and 55 deletions.
7 changes: 5 additions & 2 deletions src/components/date.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ const FormattedDate: React.FC<Props> = props => {
if (typeof children === 'function') {
return children(formattedDate);
}

return <Text>{formattedDate}</Text>;
if (Text) {
return <Text>{formattedDate}</Text>;
}
// Work around @types/react where React.FC cannot return string
return formattedDate as any;
};

FormattedDate.displayName = 'FormattedDate';
Expand Down
17 changes: 10 additions & 7 deletions src/components/message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export interface Props extends MessageDescriptor {
intl: IntlShape;
values?: any;
tagName?: React.ElementType<any>;
children?(...nodes: Array<React.ReactNode>): React.ReactNode;
children?(...nodes: React.ReactNodeArray): React.ReactNode;
}

export class BaseFormattedMessage extends React.Component<Props> {
Expand Down Expand Up @@ -92,7 +92,7 @@ export class BaseFormattedMessage extends React.Component<Props> {

let tokenDelimiter: string = '';
let tokenizedValues: Record<string, string> = {};
let elements: Record<string, string | React.ReactChild> = {};
let elements: Record<string, React.ReactChild> = {};

let hasValues = values && Object.keys(values).length > 0;
if (hasValues) {
Expand Down Expand Up @@ -131,7 +131,7 @@ export class BaseFormattedMessage extends React.Component<Props> {
let descriptor = {id, description, defaultMessage};
let formattedMessage = formatMessage(descriptor, tokenizedValues || values);

let nodes: Array<string | React.ReactChild>;
let nodes: React.ReactNodeArray;

let hasElements = elements && Object.keys(elements).length > 0;
if (hasElements) {
Expand All @@ -141,7 +141,7 @@ export class BaseFormattedMessage extends React.Component<Props> {
// keeping React's virtual diffing working properly.
nodes = formattedMessage
.split(tokenDelimiter)
.filter(part => !!part)
.filter(Boolean)
.map(part => elements[part] || part);
} else {
nodes = [formattedMessage];
Expand All @@ -151,9 +151,12 @@ export class BaseFormattedMessage extends React.Component<Props> {
return children(...nodes);
}

// Needs to use `createElement()` instead of JSX, otherwise React will
// warn about a missing `key` prop with rich-text message formatting.
return React.createElement(Component, null, ...nodes);
if (Component) {
// Needs to use `createElement()` instead of JSX, otherwise React will
// warn about a missing `key` prop with rich-text message formatting.
return <Component>{nodes}</Component>;
}
return nodes;
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/components/number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ const FormattedNumber: React.FC<Props> = props => {
if (typeof children === 'function') {
return children(formattedNumber);
}

return <Text>{formattedNumber}</Text>;
if (Text) {
return <Text>{formattedNumber}</Text>;
}
// Work around @types/react where React.FC cannot return string
return formattedNumber as any;
};

FormattedNumber.displayName = 'FormattedNumber';
Expand Down
7 changes: 5 additions & 2 deletions src/components/plural.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ const FormattedPlural: React.FC<Props> = props => {
if (typeof children === 'function') {
return children(formattedPlural);
}

return <Text>{formattedPlural}</Text>;
if (Text) {
return <Text>{formattedPlural}</Text>;
}
// Work around @types/react where React.FC cannot return string
return formattedPlural as any;
};

FormattedPlural.defaultProps = {
Expand Down
5 changes: 4 additions & 1 deletion src/components/relative.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ class FormattedRelativeTime extends React.PureComponent<Props, State> {
if (typeof children === 'function') {
return children(formattedRelativeTime);
}
return <Text>{formattedRelativeTime}</Text>;
if (Text) {
return <Text>{formattedRelativeTime}</Text>;
}
return formattedRelativeTime;
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/components/time.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ const FormattedTime: React.FC<Props> = props => {
if (typeof children === 'function') {
return children(formattedTime);
}

return <Text>{formattedTime}</Text>;
if (Text) {
return <Text>{formattedTime}</Text>;
}
// Work around @types/react where React.FC cannot return string
return formattedTime as any;
};

FormattedTime.displayName = 'FormattedTime';
Expand Down
10 changes: 10 additions & 0 deletions test/unit/components/date.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ describe('<FormattedDate>', () => {

expect(rendered.text()).toBe(intl.formatDate(date));
});
it('renders a formatted date w/o textComponent', () => {
const date = Date.now();

const rendered = mountWithProvider(
{value: date},
{...intl, textComponent: ''}
);

expect(rendered.text()).toBe(intl.formatDate(date));
});

it('accepts valid Intl.DateTimeFormat options as props', () => {
const date = new Date();
Expand Down
38 changes: 0 additions & 38 deletions test/unit/components/message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,44 +56,6 @@ describe('<FormattedMessage>', () => {
expect(rendered.text()).toBe(intl.formatMessage(descriptor));
});

it('should not cause a unique "key" prop warning', () => {
const descriptor = {
id: 'hello',
defaultMessage: 'Hello, {name}!',
};

mountWithProvider({...descriptor, values: {name: <b>Jest</b>}}, intl);

expect(consoleError).toHaveBeenCalledTimes(0);
});

it('should not cause a prop warning when description is a string', () => {
const descriptor = {
id: 'hello',
description: 'Greeting',
defaultMessage: 'Hello, {name}!',
};

mountWithProvider({...descriptor, values: {name: <b>Jest</b>}}, intl);

expect(consoleError).toHaveBeenCalledTimes(0);
});

it('should not cause a prop warning when description is an object', () => {
const descriptor = {
id: 'hello',
description: {
text: 'Greeting',
ticket: 'GTP-1234',
},
defaultMessage: 'Hello, {name}!',
};

mountWithProvider({...descriptor, values: {name: <b>Jest</b>}}, intl);

expect(consoleError).toHaveBeenCalledTimes(0);
});

it('accepts `values` prop', () => {
const descriptor = {
id: 'hello',
Expand Down
11 changes: 11 additions & 0 deletions test/unit/components/number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ describe('<FormattedNumber>', () => {
expect(rendered.text()).toBe(intl.formatNumber(num));
});

it('renders a formatted number w/o textComponent', () => {
const num = 1000;

const rendered = mountWithProvider(
{value: num},
{...intl, textComponent: null}
);

expect(rendered.text()).toBe(intl.formatNumber(num));
});

it('accepts valid Intl.NumberFormat options as props', () => {
const num = 0.5;
const options = {style: 'percent'};
Expand Down
12 changes: 12 additions & 0 deletions test/unit/components/plural.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ describe('<FormattedPlural>', () => {
expect(rendered.text()).toBe(num === 1 ? one : other);
});

it('renders a formatted plural w/o textComponent', () => {
const num = 1;
const one = 'foo';
const other = 'bar';

const rendered = mountWithProvider(
{value: num, one, other},
{...intl, textComponent: null}
);
expect(rendered.text()).toBe(num === 1 ? one : other);
});

it('accepts valid IntlPluralFormat options as props', () => {
const num = 22;
const props = {two: 'nd'};
Expand Down
12 changes: 12 additions & 0 deletions test/unit/components/relative.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ describe('<FormattedRelative>', () => {
);
});

it('can render in null textComponent', () => {
const options = {style: 'narrow' as 'narrow'};
const rendered = mountWithProvider(
{value: -60, ...options},
{...intl, textComponent: null}
);

expect(rendered.text()).toBe(
intl.formatRelativeTime(-60, 'second', options)
);
});

it('throws an error for invalid unit', () => {
const rendered = mountWithProvider({value: 0, unit: 'invalid' as any});
expect(rendered.text()).toBe('0');
Expand Down
13 changes: 12 additions & 1 deletion test/unit/components/time.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,25 @@ describe('<FormattedTime>', () => {
);
});

it('renders a formatted time in a <span>', () => {
it('renders a formatted time in a <>', () => {
const date = new Date();

const rendered = mountWithProvider({value: date}, intl);

expect(rendered.text()).toBe(intl.formatTime(date));
});

it('renders a formatted time w/o textComponent', () => {
const date = new Date();

const rendered = mountWithProvider(
{value: date},
{...intl, textComponent: null}
);

expect(rendered.text()).toBe(intl.formatTime(date));
});

it('accepts valid Intl.DateTimeFormat options as props', () => {
const date = Date.now();
const options = {hour: '2-digit'};
Expand Down

0 comments on commit 3f27902

Please sign in to comment.