Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(i18n): add i18n support #951

Merged
merged 34 commits into from
Feb 28, 2019
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ca53310
feat(i18n): add i18n support
Feb 25, 2019
f4cdec4
feat(i18n): extending language files
Feb 25, 2019
d3d039b
feat(i18n): calendar-header
Feb 25, 2019
36aada3
Merge branch 'master' into feat/i18n
gergelyke Feb 25, 2019
42ad35f
docs(i18n): add cta
Feb 25, 2019
4c0d09e
feat(i18n): co-locate locale files
Feb 25, 2019
d8ef6b2
Merge branch 'master' into feat/i18n
gergelyke Feb 25, 2019
bdb6726
fix(i18n): remove unused import
Feb 25, 2019
f7ac2f3
feat(i18n): breadcrumb and accordion
Feb 25, 2019
03d1973
feat(i18n): buttongrup
Feb 25, 2019
2ba26bb
fix(i18n): breadcrumb prop
Feb 25, 2019
d72da0d
Merge branch 'master' into feat/i18n
gergelyke Feb 25, 2019
f541e2e
feat(i18n): file-uploader
Feb 25, 2019
635028d
feat(i18n): modal
Feb 25, 2019
b9ff5c9
feat(i18n): pagination
Feb 26, 2019
bf02490
feat(i18n): select
Feb 26, 2019
46d561c
feat(i18n): toast
Feb 26, 2019
4ef8859
Merge branch 'master' into feat/i18n
gergelyke Feb 26, 2019
41e1b78
Merge branch 'master' into feat/i18n
gergelyke Feb 26, 2019
029d327
Merge branch 'master' into feat/i18n
gergelyke Feb 26, 2019
cffbcb9
feat(i18n): fix ButtonGroup tests
tajo Feb 27, 2019
bd7fb02
Merge branch 'master' into feat/i18n
gergelyke Feb 27, 2019
f07ffcd
Merge branch 'master' into feat/i18n
gergelyke Feb 27, 2019
2f971f9
fix(i18n): breadcrumbs
Feb 27, 2019
083752e
fix(i18n): test coverage
Feb 27, 2019
6ef7e2e
fix(i18n): flow types
Feb 27, 2019
a040d5f
docs(i18n): add example
Feb 27, 2019
b2c2cff
fix(i18n): update snapshots
Feb 28, 2019
c21f011
Merge branch 'master' into feat/i18n
gergelyke Feb 28, 2019
6a0c288
Merge branch 'master' into feat/i18n
gergelyke Feb 28, 2019
c353798
fix(i18n): pagination
Feb 28, 2019
a2ab15d
docs(i18n): more docs
Feb 28, 2019
4ee5e45
Merge branch 'master' into feat/i18n
gergelyke Feb 28, 2019
290357f
Update src/button-group/button-group.js
chasestarr Feb 28, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 10 additions & 3 deletions documentation-site/components/code.js
Expand Up @@ -7,16 +7,23 @@ LICENSE file in the root directory of this source tree.
// @flow
import * as React from 'react';
import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter';
import {Block} from 'baseui/block';

type PropsT = {
children: string,
language: string,
};

const Code = (props: PropsT) => (
<SyntaxHighlighter language={props.language} useInlineStyles={false}>
{props.children}
</SyntaxHighlighter>
<Block overflow="scroll">
<SyntaxHighlighter
language={props.language}
useInlineStyles={false}
style={{overflow: 'scroll'}}
>
{props.children}
</SyntaxHighlighter>
</Block>
);

Code.defaultProps = {
Expand Down
19 changes: 19 additions & 0 deletions documentation-site/components/json.js
@@ -0,0 +1,19 @@
/*
Copyright (c) 2018 Uber Technologies, Inc.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
// @flow
import * as React from 'react';
import Code from './code.js';

type PropsT = {
src: string,
};

const JSONViewer = (props: PropsT) => (
<Code language="javascript">{JSON.stringify(props.src, null, 2)}</Code>
);

export default JSONViewer;
31 changes: 31 additions & 0 deletions documentation-site/pages/getting-started/internationalization.mdx
@@ -0,0 +1,31 @@
<!--
Copyright (c) 2018 Uber Technologies, Inc.

This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
-->

import Example from '../../components/example';
import Layout from '../../components/layout';
import JSON from '../../components/json';
import en_US from 'baseui/locale/en_US.js';

import InternationalizationExample from 'examples/internationalization/example.js';

export default Layout;

# Internationalization

Base UI supports English as the default language. Following the instructions below, you
can use other languages too.

<Example title="Internationalization example" path="examples/internationalization/example.js">
<InternationalizationExample />
</Example>

## The shape of the locale file

<JSON src={en_US} />

If you'd like to contribute a locale, please send a Pull Request based
on the [en_US](https://github.com/uber-web/baseui/tree/master/src/locale) locale.
4 changes: 4 additions & 0 deletions documentation-site/routes.js
Expand Up @@ -36,6 +36,10 @@ const routes = [
text: 'Comparison with other component libraries',
path: '/getting-started/comparison',
},
{
text: 'Internationalization',
path: '/getting-started/internationalization',
},
],
},
{
Expand Down
17 changes: 17 additions & 0 deletions documentation-site/static/examples/internationalization/example.js
@@ -0,0 +1,17 @@
import React from 'react';
import {LocaleProvider} from 'baseui';
import {StatefulPagination} from 'baseui/pagination';

const localeOverrideHu = {
pagination: {
next: 'Következő',
prev: 'Előző',
preposition: ' ',
},
};

export default () => (
<LocaleProvider locale={localeOverrideHu}>
<StatefulPagination numPages={10} />
</LocaleProvider>
);
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -131,6 +131,7 @@
"dependencies": {
"date-fns": "2.0.0-alpha.27",
"focus-trap": "^4.0.2",
"just-extend": "^4.0.2",
"memoize-one": "^5.0.0",
"popper.js": "^1.14.3",
"react-dropzone": "^9.0.0",
Expand Down
19 changes: 19 additions & 0 deletions src/accordion/locale.js
@@ -0,0 +1,19 @@
/*
Copyright (c) 2018 Uber Technologies, Inc.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
// @flow

export type AccordionLocaleT = {|
collapse: string,
expand: string,
|};

const locale = {
collapse: 'Collapse',
expand: 'Expand',
};

export default locale;
57 changes: 32 additions & 25 deletions src/accordion/panel.js
Expand Up @@ -6,6 +6,7 @@ LICENSE file in the root directory of this source tree.
*/
// @flow
import * as React from 'react';
import {LocaleContext} from '../locale/index.js';
import {getOverrides, mergeOverrides} from '../helpers/overrides.js';
import {
Plus as PlusIcon,
Expand Down Expand Up @@ -91,31 +92,37 @@ class Panel extends React.Component<PanelPropsT> {
);
const ToggleIconComponent = expanded ? CheckIndeterminateIcon : PlusIcon;
return (
<PanelContainer {...sharedProps} {...panelContainerProps}>
<Header
tabIndex={0}
role="button"
aria-expanded={expanded}
aria-disabled={disabled || null}
{...sharedProps}
{...headerProps}
onClick={this.onClick}
onKeyDown={this.onKeyDown}
>
{title}
<ToggleIconComponent
size={16}
title={expanded ? 'Collapse' : 'Expand'}
{...sharedProps}
{...toggleIconProps}
// $FlowFixMe
overrides={toggleIconOverrides}
/>
</Header>
<Content {...sharedProps} {...contentProps}>
{children}
</Content>
</PanelContainer>
<LocaleContext.Consumer>
{locale => (
<PanelContainer {...sharedProps} {...panelContainerProps}>
<Header
tabIndex={0}
role="button"
aria-expanded={expanded}
aria-disabled={disabled || null}
{...sharedProps}
{...headerProps}
onClick={this.onClick}
onKeyDown={this.onKeyDown}
>
{title}
<ToggleIconComponent
size={16}
title={
expanded ? locale.accordion.collapse : locale.accordion.expand
}
{...sharedProps}
{...toggleIconProps}
// $FlowFixMe
overrides={toggleIconOverrides}
/>
</Header>
<Content {...sharedProps} {...contentProps}>
{children}
</Content>
</PanelContainer>
)}
</LocaleContext.Consumer>
);
}
}
Expand Down
Expand Up @@ -2,7 +2,7 @@

exports[`Breadcrumbs displays separators in correct positions 1`] = `
<MockStyledComponent
aria-label="Breadcrumbs navigation"
aria-label=""
>
<MockStyledComponent
href="#"
Expand Down
7 changes: 4 additions & 3 deletions src/breadcrumbs/__tests__/breadcrumbs.test.js
Expand Up @@ -9,18 +9,19 @@ LICENSE file in the root directory of this source tree.
import React from 'react';
import {shallow} from 'enzyme';
import {StyledLink} from '../../link/index.js';
import {Breadcrumbs} from '../index.js';
import {BreadcrumbsRoot as Breadcrumbs} from '../breadcrumbs.js';

describe('Breadcrumbs', () => {
it('applies correct accessibility attributes to root element', () => {
const ariaLabel = 'Breadcrumbs navigation';
const example = shallow(
<Breadcrumbs>
<Breadcrumbs locale={{ariaLabel}}>
<StyledLink href="#">Parent Page</StyledLink>
<StyledLink href="#">Sub-Parent Page</StyledLink>
<span>Current Page</span>
</Breadcrumbs>,
);
expect(example).toHaveProp('aria-label', 'Breadcrumbs navigation');
expect(example).toHaveProp('aria-label', ariaLabel);
});

it('displays separators in correct positions', () => {
Expand Down
25 changes: 21 additions & 4 deletions src/breadcrumbs/breadcrumbs.js
Expand Up @@ -9,12 +9,16 @@ LICENSE file in the root directory of this source tree.

import React, {Children} from 'react';

import {LocaleContext} from '../locale/index.js';
import type {BreadcrumbsPropsT} from './types.js';
import type {BreadcrumbLocaleT} from './locale.js';
import {StyledRoot, StyledSeparator, StyledIcon} from './styled-components.js';
import {getOverrides} from '../helpers/overrides.js';

function Breadcrumbs({children, overrides = {}}: BreadcrumbsPropsT) {
const numChildren = Children.count(children);
type LocaleT = {|locale?: BreadcrumbLocaleT|};
export function BreadcrumbsRoot(props: {|...BreadcrumbsPropsT, ...LocaleT|}) {
const {overrides = {}} = props;
const numChildren = Children.count(props.children);
const childrenWithSeparators = [];

const [Root, baseRootProps] = getOverrides(overrides.Root, StyledRoot);
Expand All @@ -24,7 +28,7 @@ function Breadcrumbs({children, overrides = {}}: BreadcrumbsPropsT) {
StyledSeparator,
);

Children.forEach(children, (child, index) => {
Children.forEach(props.children, (child, index) => {
childrenWithSeparators.push(child);

if (index !== numChildren - 1) {
Expand All @@ -37,12 +41,25 @@ function Breadcrumbs({children, overrides = {}}: BreadcrumbsPropsT) {
});

return (
<Root aria-label="Breadcrumbs navigation" {...baseRootProps}>
<Root
aria-label={
props.ariaLabel || (props.locale ? props.locale.ariaLabel : '')
}
{...baseRootProps}
>
{childrenWithSeparators}
</Root>
);
}

function Breadcrumbs(props: BreadcrumbsPropsT) {
return (
<LocaleContext.Consumer>
{locale => <BreadcrumbsRoot {...props} locale={locale.breadcrumbs} />}
</LocaleContext.Consumer>
);
}

Breadcrumbs.defaultProps = {
overrides: {},
};
Expand Down
17 changes: 17 additions & 0 deletions src/breadcrumbs/locale.js
@@ -0,0 +1,17 @@
/*
Copyright (c) 2018 Uber Technologies, Inc.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
// @flow

export type BreadcrumbLocaleT = {|
ariaLabel: string,
|};

const locale = {
ariaLabel: 'Breadcrumbs navigation',
};

export default locale;
5 changes: 3 additions & 2 deletions src/breadcrumbs/types.js
Expand Up @@ -18,10 +18,11 @@ export type OverridesT = {
Icon?: OverrideT<*>,
};

export type BreadcrumbsPropsT = {
export type BreadcrumbsPropsT = {|
children?: Node,
overrides?: OverridesT,
};
ariaLabel?: string,
|};

export type StyledRootPropsT = {
$theme: ThemeT,
Expand Down
2 changes: 1 addition & 1 deletion src/button-group/__tests__/button-group.test.js
Expand Up @@ -11,7 +11,7 @@ import {shallow} from 'enzyme';

import {Button} from '../../button/index.js';

import {ButtonGroup} from '../index.js';
import {ButtonGroupRoot as ButtonGroup} from '../button-group.js';

function buildSimpleWrapper(props = {}) {
return shallow(
Expand Down
25 changes: 21 additions & 4 deletions src/button-group/button-group.js
Expand Up @@ -10,9 +10,11 @@ import React from 'react';

import {KIND, SIZE, SHAPE} from '../button/index.js';
import {getOverrides} from '../helpers/overrides.js';
import {LocaleContext} from '../locale/index.js';

import {StyledRoot} from './styled-components.js';
import type {PropsT} from './types.js';
import type {ButtonGroupLocaleT} from './locale.js';

function isSelected(selected, index) {
if (!Array.isArray(selected) && typeof selected !== 'number') {
Expand Down Expand Up @@ -49,12 +51,18 @@ function getBorderRadii(index, length) {
};
}

export default function ButtonGroup(props: PropsT) {
type LocaleT = {|locale?: ButtonGroupLocaleT|};
export function ButtonGroupRoot(props: {|...PropsT, ...LocaleT|}) {
const {overrides = {}} = props;
const [Root, rootProps] = getOverrides(overrides.Root, StyledRoot);

return (
<Root aria-label={props.ariaLabel} {...rootProps}>
<Root
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could context consumer be placed here rather than in the wrapper component? not sure why locale needs to be provided through props

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, sadly enyzme does not fully support contexts, so sadly the test framework affected our production code :(

is this correct @tajo ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, this is a workaround for Enzyme. It can't shallow render the renderProp.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we simply mount and avoid the indirection? i've found that code bases grow in puzzling ways when ad hoc refactoring to enable test patterns

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, 100%.

We've tried that too, it won't work - I think Vojtech will want to bring up the issues with enzyme, and start a chat on what we can do about it

for now, would you be ok with it as is, and fix it once we figure out what to do with enzyme?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for sure - i would personally prefer to remove the test than add the additional indirection, but up to you

aria-label={
props.ariaLabel || (props.locale ? props.locale.ariaLabel : '')
}
{...rootProps}
>
{React.Children.map(props.children, (child, index) => {
if (!React.isValidElement(child)) {
return null;
Expand All @@ -78,7 +86,9 @@ export default function ButtonGroup(props: PropsT) {
}
},
overrides: {
BaseButton: {style: getBorderRadii(index, props.children.length)},
BaseButton: {
style: getBorderRadii(index, props.children.length),
},
...child.props.overrides,
},
shape: props.shape,
Expand All @@ -89,8 +99,15 @@ export default function ButtonGroup(props: PropsT) {
);
}

gergelyke marked this conversation as resolved.
Show resolved Hide resolved
export default function ButtonGroup(props: PropsT) {
return (
<LocaleContext.Consumer>
{locale => <ButtonGroupRoot {...props} locale={locale.buttongroup} />}
</LocaleContext.Consumer>
);
}

ButtonGroup.defaultProps = {
ariaLabel: 'button group',
disabled: false,
onClick: () => {},
shape: SHAPE.default,
Expand Down