diff --git a/docs/pages/api/autocomplete.md b/docs/pages/api/autocomplete.md index 4181b753f1c9d2..7bfda0bf290186 100644 --- a/docs/pages/api/autocomplete.md +++ b/docs/pages/api/autocomplete.md @@ -29,7 +29,9 @@ You can learn more about the difference by [reading this guide](/guides/minimizi | autoSelect | bool | false | If `true`, the selected option becomes the value of the input when the Autocomplete loses focus unless the user chooses a different option or changes the character string in the input. | | classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. | | clearOnEscape | bool | false | If `true`, clear all values when the user presses escape and the popup is closed. | +| clearText | string | 'Clear' | Text label for the clear icon button. | | closeIcon | node | <CloseIcon fontSize="small" /> | The icon to display in place of the default close icon. | +| closeText | string | 'Close' | Text label for the close popup icon button. | | debug | bool | false | If `true`, the popup will ignore the blur event if the input if filled. You can inspect the popup markup with your browser tools. Consider this option when you need to customize the component. | | defaultValue | any | | The default input value. Use when the component is not controlled. | | disableClearable | bool | false | If `true`, the input can't be cleared. | @@ -57,6 +59,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi | onInputChange | func | | Callback fired when the input value changes.

**Signature:**
`function(event: object, value: string) => void`
*event:* The event source of the callback.
*value:* null | | onOpen | func | | Callback fired when the popup requests to be opened. Use in controlled mode (see open).

**Signature:**
`function(event: object) => void`
*event:* The event source of the callback. | | open | bool | | Control the popup` open state. | +| openText | string | 'Open' | Text label for the open popup icon button. | | options | array | [] | Array of options. | | PaperComponent | elementType | Paper | The component used to render the body of the popup. | | PopperComponent | elementType | Popper | The component used to position the popup. | @@ -64,14 +67,8 @@ You can learn more about the difference by [reading this guide](/guides/minimizi | renderGroup | func | | Render the group.

**Signature:**
`function(option: any) => ReactNode`
*option:* The group to render. | | renderInput * | func | | Render the input.

**Signature:**
`function(params: object) => ReactNode`
*params:* null | | renderOption | func | | Render the option, use `getOptionLabel` by default.

**Signature:**
`function(option: any, state: object) => ReactNode`
*option:* The option to render.
*state:* The state of the component. | -<<<<<<< HEAD | renderTags | func | | Render the selected value.

**Signature:**
`function(value: any, getTagProps: function) => ReactNode`
*value:* The `value` provided to the component.
*getTagProps:* A tag props getter. | | value | any | | The value of the autocomplete. | -======= -| renderTags | func | | Render the selected value.

**Signature:**
`function(value: any) => ReactNode`
*value:* The `value` provided to the component. | -| titles | { clearPopup: string, closePopup: string, openPopup: string } | { openPopup: 'Open popup', closePopup: 'Close popup', clearPopup: 'Clear',} | Titles to display when hovering the arrow or clear buttons. | -| value | any | | The input value. | ->>>>>>> Add new props to accept custom titles for Autocomplete buttons The `ref` is forwarded to the root element. diff --git a/docs/pages/api/table-pagination.md b/docs/pages/api/table-pagination.md index 0d3907117e0e15..da4de808f0402c 100644 --- a/docs/pages/api/table-pagination.md +++ b/docs/pages/api/table-pagination.md @@ -26,12 +26,14 @@ A `TableCell` based component for placing inside `TableFooter` for pagination. |:-----|:-----|:--------|:------------| | ActionsComponent | elementType | TablePaginationActions | The component used for displaying the actions. Either a string to use a DOM element or a component. | | backIconButtonProps | object | | Props applied to the back arrow [`IconButton`](/api/icon-button/) component. | +| backIconButtonText | string | 'Previous page' | Text label for the back arrow icon button. | | classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. | | component | elementType | TableCell | The component used for the root node. Either a string to use a DOM element or a component. | | count * | number | | The total number of rows. | | labelDisplayedRows | func | ({ from, to, count }) =>`${from}-${to === -1 ? count : to} of ${count}` | Customize the displayed rows label. | | labelRowsPerPage | node | 'Rows per page:' | Customize the rows per page label. Invoked with a `{ from, to, count, page }` object. | | nextIconButtonProps | object | | Props applied to the next arrow [`IconButton`](/api/icon-button/) element. | +| nextIconButtonText | string | 'Next page' | Text label for the next arrow icon button. | | onChangePage * | func | | Callback fired when the page is changed.

**Signature:**
`function(event: object, page: number) => void`
*event:* The event source of the callback.
*page:* The page selected. | | onChangeRowsPerPage | func | | Callback fired when the number of rows per page is changed.

**Signature:**
`function(event: object) => void`
*event:* The event source of the callback. | | page * | number | | The zero-based index of the current page. | diff --git a/docs/pages/guides/globalization.js b/docs/pages/guides/globalization.js new file mode 100644 index 00000000000000..46530830ba1b8e --- /dev/null +++ b/docs/pages/guides/globalization.js @@ -0,0 +1,14 @@ +import React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; + +const req = require.context('docs/src/pages/guides/globalization', false, /\.(md|js|tsx)$/); +const reqSource = require.context( + '!raw-loader!../../src/pages/guides/globalization', + false, + /\.(js|tsx)$/, +); +const reqPrefix = 'pages/guides/globalization'; + +export default function Page() { + return ; +} diff --git a/docs/src/modules/components/MarkdownElement.js b/docs/src/modules/components/MarkdownElement.js index 5e02f77131e33d..e0b611f2431534 100644 --- a/docs/src/modules/components/MarkdownElement.js +++ b/docs/src/modules/components/MarkdownElement.js @@ -215,7 +215,6 @@ const styles = theme => ({ }, '& table': { width: '100%', - display: 'block', overflowX: 'auto', WebkitOverflowScrolling: 'touch', // iOS momentum scrolling. borderCollapse: 'collapse', diff --git a/docs/src/modules/components/ThemeContext.js b/docs/src/modules/components/ThemeContext.js index 383a47d90482db..27b9fac1d1392a 100644 --- a/docs/src/modules/components/ThemeContext.js +++ b/docs/src/modules/components/ThemeContext.js @@ -5,11 +5,23 @@ import { createMuiTheme, darken, } from '@material-ui/core/styles'; +import { useSelector } from 'react-redux'; import useMediaQuery from '@material-ui/core/useMediaQuery'; +import { enUS, zhCN, ruRU, ptBR, esES, frFR, deDE, jaJP } from '@material-ui/core/locale'; import { blue, pink } from '@material-ui/core/colors'; import { getCookie } from 'docs/src/modules/utils/helpers'; import { darkTheme, setPrismTheme } from 'docs/src/modules/components/prism'; -import deepmerge from 'deepmerge'; + +const languageMap = { + en: enUS, + zh: zhCN, + ru: ruRU, + pt: ptBR, + es: esES, + fr: frFR, + de: deDE, + ja: jaJP, +}; export const themeColor = blue[700]; @@ -20,72 +32,59 @@ const themeInitialOptions = { spacing: 8, // spacing unit }; -/** - * @typedef {import('@material-ui/core/src/styles/createMuiTheme').ThemeOptions} ThemeOptions - * - * - * @param {ThemeOptions} themeOptions - * @returns {ThemeOptions} - */ -function usingHighDensity(themeOptions) { - return deepmerge(themeOptions, { - props: { - MuiButton: { - size: 'small', - }, - MuiFilledInput: { - margin: 'dense', - }, - MuiFormControl: { - margin: 'dense', - }, - MuiFormHelperText: { - margin: 'dense', - }, - MuiIconButton: { - size: 'small', - }, - MuiInputBase: { - margin: 'dense', - }, - MuiInputLabel: { - margin: 'dense', - }, - MuiListItem: { - dense: true, - }, - MuiOutlinedInput: { - margin: 'dense', - }, - MuiFab: { - size: 'small', - }, - MuiTable: { - size: 'small', - }, - MuiTextField: { - margin: 'dense', - }, - MuiToolbar: { - variant: 'dense', - }, +const highDensity = { + props: { + MuiButton: { + size: 'small', }, - overrides: { - MuiIconButton: { - sizeSmall: { - // minimal touch target hit spacing - marginLeft: 4, - marginRight: 4, - padding: 12, - }, + MuiFilledInput: { + margin: 'dense', + }, + MuiFormControl: { + margin: 'dense', + }, + MuiFormHelperText: { + margin: 'dense', + }, + MuiIconButton: { + size: 'small', + }, + MuiInputBase: { + margin: 'dense', + }, + MuiInputLabel: { + margin: 'dense', + }, + MuiListItem: { + dense: true, + }, + MuiOutlinedInput: { + margin: 'dense', + }, + MuiFab: { + size: 'small', + }, + MuiTable: { + size: 'small', + }, + MuiTextField: { + margin: 'dense', + }, + MuiToolbar: { + variant: 'dense', + }, + }, + overrides: { + MuiIconButton: { + sizeSmall: { + // minimal touch target hit spacing + marginLeft: 4, + marginRight: 4, + padding: 12, }, }, - }); -} - -function usingIdentity(themeOptions) { - return themeOptions; -} + }, +}; export const DispatchContext = React.createContext(() => { throw new Error('Forgot to wrap component in ThemeContext.Provider'); @@ -143,6 +142,7 @@ export function ThemeProvider(props) { } }, themeInitialOptions); + const userLanguage = useSelector(state => state.options.userLanguage); const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); const preferredType = prefersDarkMode ? 'dark' : 'light'; const { dense, direction, paletteColors, paletteType = preferredType, spacing } = themeOptions; @@ -173,9 +173,8 @@ export function ThemeProvider(props) { }, [direction]); const theme = React.useMemo(() => { - const themeDecorator = dense ? usingHighDensity : usingIdentity; const nextTheme = createMuiTheme( - themeDecorator({ + { direction, nprogress: { color: paletteType === 'light' ? '#000' : '#fff', @@ -194,7 +193,9 @@ export function ThemeProvider(props) { ...paletteColors, }, spacing, - }), + }, + dense ? highDensity : null, + languageMap[userLanguage], ); nextTheme.palette.background.level2 = @@ -204,7 +205,7 @@ export function ThemeProvider(props) { paletteType === 'light' ? '#fff' : nextTheme.palette.grey[900]; return nextTheme; - }, [dense, direction, paletteColors, paletteType, spacing]); + }, [dense, direction, paletteColors, paletteType, spacing, userLanguage]); React.useEffect(() => { // Expose the theme as a global variable so people can play with it. diff --git a/docs/src/pages.js b/docs/src/pages.js index 340c7361c4ca7c..c0e00c687a5d10 100644 --- a/docs/src/pages.js +++ b/docs/src/pages.js @@ -184,6 +184,7 @@ const pages = [ { pathname: '/guides/migration-v3', title: 'Migration From v3' }, { pathname: '/guides/migration-v0x', title: 'Migration From v0.x' }, { pathname: '/guides/testing' }, + { pathname: '/guides/globalization' }, { pathname: '/guides/right-to-left', title: 'Right-to-left' }, { pathname: '/guides/flow' }, ], diff --git a/docs/src/pages/components/tables/EnhancedTable.js b/docs/src/pages/components/tables/EnhancedTable.js index f1517a71619ec5..639b2c2ced4adf 100644 --- a/docs/src/pages/components/tables/EnhancedTable.js +++ b/docs/src/pages/components/tables/EnhancedTable.js @@ -342,12 +342,6 @@ export default function EnhancedTable() { count={rows.length} rowsPerPage={rowsPerPage} page={page} - backIconButtonProps={{ - 'aria-label': 'previous page', - }} - nextIconButtonProps={{ - 'aria-label': 'next page', - }} onChangePage={handleChangePage} onChangeRowsPerPage={handleChangeRowsPerPage} /> diff --git a/docs/src/pages/components/tables/EnhancedTable.tsx b/docs/src/pages/components/tables/EnhancedTable.tsx index c210ca5a84fb1d..0af02705ca14b9 100644 --- a/docs/src/pages/components/tables/EnhancedTable.tsx +++ b/docs/src/pages/components/tables/EnhancedTable.tsx @@ -370,12 +370,6 @@ export default function EnhancedTable() { count={rows.length} rowsPerPage={rowsPerPage} page={page} - backIconButtonProps={{ - 'aria-label': 'previous page', - }} - nextIconButtonProps={{ - 'aria-label': 'next page', - }} onChangePage={handleChangePage} onChangeRowsPerPage={handleChangeRowsPerPage} /> diff --git a/docs/src/pages/components/tables/StickyHeadTable.js b/docs/src/pages/components/tables/StickyHeadTable.js index 3890ed7999c591..3c53bc6fa4e06d 100644 --- a/docs/src/pages/components/tables/StickyHeadTable.js +++ b/docs/src/pages/components/tables/StickyHeadTable.js @@ -122,12 +122,6 @@ export default function StickyHeadTable() { count={rows.length} rowsPerPage={rowsPerPage} page={page} - backIconButtonProps={{ - 'aria-label': 'previous page', - }} - nextIconButtonProps={{ - 'aria-label': 'next page', - }} onChangePage={handleChangePage} onChangeRowsPerPage={handleChangeRowsPerPage} /> diff --git a/docs/src/pages/components/tables/StickyHeadTable.tsx b/docs/src/pages/components/tables/StickyHeadTable.tsx index e2ae21519a8910..dfa5dece803272 100644 --- a/docs/src/pages/components/tables/StickyHeadTable.tsx +++ b/docs/src/pages/components/tables/StickyHeadTable.tsx @@ -138,12 +138,6 @@ export default function StickyHeadTable() { count={rows.length} rowsPerPage={rowsPerPage} page={page} - backIconButtonProps={{ - 'aria-label': 'previous page', - }} - nextIconButtonProps={{ - 'aria-label': 'next page', - }} onChangePage={handleChangePage} onChangeRowsPerPage={handleChangeRowsPerPage} /> diff --git a/docs/src/pages/customization/theming/theming.md b/docs/src/pages/customization/theming/theming.md index 72a901f8c078b0..9e04ed98e4f1b1 100644 --- a/docs/src/pages/customization/theming/theming.md +++ b/docs/src/pages/customization/theming/theming.md @@ -64,13 +64,14 @@ The main point to understand is that the injected CSS is cached with the followi ## API -### `createMuiTheme(options) => theme` +### `createMuiTheme(options, ...args) => theme` Generate a theme base on the options received. #### Arguments 1. `options` (*Object*): Takes an incomplete theme object and adds the missing parts. +2. `...args` (*Array*): Deep merge the arguments with the about to be returned theme. #### Returns diff --git a/docs/src/pages/guides/globalization/Locales.js b/docs/src/pages/guides/globalization/Locales.js new file mode 100644 index 00000000000000..cde68435d23e8f --- /dev/null +++ b/docs/src/pages/guides/globalization/Locales.js @@ -0,0 +1,33 @@ +import React from 'react'; +import TablePagination from '@material-ui/core/TablePagination'; +import Rating from '@material-ui/lab/Rating'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import TextField from '@material-ui/core/TextField'; +import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; +import { zhCN } from '@material-ui/core/locale'; + +const theme = createMuiTheme({}, zhCN); + +export default function Locales() { + return ( +
+ + {}} + /> + ( + + )} + /> + + +
+ ); +} diff --git a/docs/src/pages/guides/globalization/Locales.tsx b/docs/src/pages/guides/globalization/Locales.tsx new file mode 100644 index 00000000000000..cde68435d23e8f --- /dev/null +++ b/docs/src/pages/guides/globalization/Locales.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import TablePagination from '@material-ui/core/TablePagination'; +import Rating from '@material-ui/lab/Rating'; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import TextField from '@material-ui/core/TextField'; +import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; +import { zhCN } from '@material-ui/core/locale'; + +const theme = createMuiTheme({}, zhCN); + +export default function Locales() { + return ( +
+ + {}} + /> + ( + + )} + /> + + +
+ ); +} diff --git a/docs/src/pages/guides/globalization/globalization.md b/docs/src/pages/guides/globalization/globalization.md new file mode 100644 index 00000000000000..abf715a50d6ee1 --- /dev/null +++ b/docs/src/pages/guides/globalization/globalization.md @@ -0,0 +1,48 @@ +# Globalization + +

Globalization is a process which combines the translation of component messages (localization) with their adaptation to specific cultures (internationalization).

+ +The default locale of Material-UI is English (United States). If you want to use other locales, you can follow the instructions below. + +## Locale text + +Use the theme to configure the locale text globally + +```jsx +import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles'; +import { zhCN } from '@material-ui/core/locale'; + +const theme = createMuiTheme({ + palette: { + primary: { main: '#1976d2' }, + }, +}, zhCN); + + + + +``` + +### Supported locales + +| Locale | BCP 47 language tag | Import name +|:-------|:---------|:---------| +| Chinese (Simplified) | zh-CN | `zhCN` | +| English (United States) | en-US | `enUS` | +| French | fr-FR | `frFR` | +| German | de-DE | `deDE` | +| Japanese | ja-JP | `jaJP` | +| Portuguese (Brazil) | pt-BR | `ptBR` | +| Russian | ru-RU | `ruRU` | +| Spanish | es-ES | `esES` | + +You can find the [sources](https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/locale/index.js) in the GitHub repository. + +### Example + +{{"demo": "pages/guides/globalization/Locales.js", "defaultCodeOpen": false}} + +## RTL Support + +Right-to-left languages are Arabic, Hebrew, and others. +Follow [the guide](/guides/right-to-left/) to use them. diff --git a/docs/translations/translations.json b/docs/translations/translations.json index 28d9b53a0b05fa..dcad523f11aa6f 100644 --- a/docs/translations/translations.json +++ b/docs/translations/translations.json @@ -221,6 +221,7 @@ "/components/rating": "Rating", "/components/skeleton": "Skeleton", "/components/tree-view": "Tree View", - "/customization/density": "Density" + "/customization/density": "Density", + "/guides/globalization": "Globalization" } } diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts b/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts index 8fd6b53728081f..40765412c27fcf 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts @@ -45,6 +45,14 @@ export interface AutocompleteProps * The icon to display in place of the default close icon. */ closeIcon?: React.ReactNode; + /** + * Text label for the clear icon button. + */ + clearText?: string; + /** + * Text label for the close popup icon button. + */ + closeText?: string; /** * If `true`, the input will be disabled. */ @@ -70,6 +78,10 @@ export interface AutocompleteProps * Text to display when there are no options. */ noOptionsText?: React.ReactNode; + /** + * Text label for the open popup icon button. + */ + openText?: string; /** * The component used to render the body of the popup. */ @@ -112,14 +124,6 @@ export interface AutocompleteProps * @returns {ReactNode} */ renderTags?: (value: any, getTagProps: GetTagProps) => React.ReactNode; - /** - * Titles to display when hovering the arrow or clear buttons. - */ - titles?: { - openPopup: string; - closePopup: string; - clearPopup: string; - }; } export type AutocompleteClassKey = diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js index 7602a2b26bfa26..52ffc0ce768063 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.js @@ -164,7 +164,9 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { classes, className, clearOnEscape = false, + clearText = 'Clear', closeIcon = , + closeText = 'Close', debug = false, defaultValue, disableClearable = false, @@ -192,6 +194,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { onInputChange, onOpen, open, + openText = 'Open', options = [], PaperComponent = Paper, PopperComponent: PopperComponentProp = Popper, @@ -200,11 +203,6 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { renderInput, renderOption: renderOptionProp, renderTags, - titles = { - openPopup: 'Open popup', - closePopup: 'Close popup', - clearPopup: 'Clear', - }, value: valueProp, ...other } = props; @@ -306,7 +304,7 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) { {disableClearable || disabled ? null : ( >>>>>> Add new props to accept custom titles for Autocomplete buttons */ value: PropTypes.any, }; diff --git a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js index ee6f66b22893e5..06390ff1096fea 100644 --- a/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js +++ b/packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js @@ -100,7 +100,7 @@ describe('', () => { // TODO: computeAccessibleName expect(buttons[0]).to.have.attribute('title', 'Clear'); // TODO: computeAccessibleName - expect(buttons[1]).to.have.attribute('title', 'Open popup'); + expect(buttons[1]).to.have.attribute('title', 'Open'); buttons.forEach(button => { expect(button, 'button is not in tab order').to.have.property('tabIndex', -1); }); @@ -141,7 +141,7 @@ describe('', () => { // TODO: computeAccessibleName expect(buttons[0]).to.have.attribute('title', 'Clear'); // TODO: computeAccessibleName - expect(buttons[1]).to.have.attribute('title', 'Close popup'); + expect(buttons[1]).to.have.attribute('title', 'Close'); buttons.forEach(button => { expect(button, 'button is not in tab order').to.have.property('tabIndex', -1); }); @@ -407,7 +407,7 @@ describe('', () => { renderInput={params => } />, ); - expect(queryByTitle('Open popup').disabled).to.be.true; + expect(queryByTitle('Open').disabled).to.be.true; }); it('should not render the clear button', () => { diff --git a/packages/material-ui/src/TablePagination/TablePagination.js b/packages/material-ui/src/TablePagination/TablePagination.js index 8a9cee2a08af04..cb259930378d1b 100644 --- a/packages/material-ui/src/TablePagination/TablePagination.js +++ b/packages/material-ui/src/TablePagination/TablePagination.js @@ -77,6 +77,7 @@ const TablePagination = React.forwardRef(function TablePagination(props, ref) { const { ActionsComponent = TablePaginationActions, backIconButtonProps, + backIconButtonText = 'Previous page', classes, className, colSpan: colSpanProp, @@ -85,6 +86,7 @@ const TablePagination = React.forwardRef(function TablePagination(props, ref) { labelDisplayedRows = defaultLabelDisplayedRows, labelRowsPerPage = 'Rows per page:', nextIconButtonProps, + nextIconButtonText = 'Next page', onChangePage, onChangeRowsPerPage, page, @@ -143,9 +145,17 @@ const TablePagination = React.forwardRef(function TablePagination(props, ref) { `${from}-${to === -1 ? count : to} of ${count}`, + nextIconButtonText: 'Siguiente página', + }, + MuiRating: { + getLabelText: value => `${value} ${value !== 1 ? 'Sterne' : 'Star'}`, + }, + MuiAutocomplete: { + clearText: 'Klar', + closeText: 'Schließen', + loadingText: 'Wird geladen…', + noOptionsText: 'Keine Optionen', + openText: 'Öffnen', + }, + }, +}; + +// default +export const enUS = {}; + +/** + props: { + MuiTablePagination: { + backIconButtonText: 'Previous page', + labelRowsPerPage: 'Rows per page:', + labelDisplayedRows: ({ from, to, count }) => `${from}-${to === -1 ? count : to} of ${count}`, + nextIconButtonText: 'Next page', + }, + MuiRating: { + getLabelText: value => `${value} Star${value !== 1 ? 's' : ''}`, + }, + MuiAutocomplete: { + clearText: 'Clear', + closeText: 'Close', + loadingText: 'Loading…', + noOptionsText: 'No options', + openText: 'Open', + }, + }, +*/ + +export const esES = { + props: { + MuiTablePagination: { + backIconButtonText: 'Pagina anterior', + labelRowsPerPage: 'Filas por página:', + labelDisplayedRows: ({ from, to, count }) => `${from}-${to === -1 ? count : to} of ${count}`, + nextIconButtonText: 'Siguiente página', + }, + MuiRating: { + getLabelText: value => `${value} Estrella${value !== 1 ? 's' : ''}`, + }, + MuiAutocomplete: { + clearText: 'Claro', + closeText: 'Cerrar', + loadingText: 'Cargando…', + noOptionsText: 'Sin opciones', + openText: 'Abierto', + }, + }, +}; + +export const frFR = { + props: { + MuiTablePagination: { + backIconButtonText: 'Page précédente', + labelRowsPerPage: 'lignes par page :', + labelDisplayedRows: ({ from, to, count }) => `${from}-${to === -1 ? count : to} sur ${count}`, + nextIconButtonText: 'Page suivante', + }, + MuiRating: { + getLabelText: value => `${value} Etoile${value !== 1 ? 's' : ''}`, + }, + MuiAutocomplete: { + clearText: 'Vider', + closeText: 'Fermer', + loadingText: 'Chargement…', + noOptionsText: 'Pas de résultats', + openText: 'Ouvrir', + }, + }, +}; + +export const jaJP = { + props: { + MuiTablePagination: { + backIconButtonText: '前のページ', + labelRowsPerPage: 'ページごとの行:', + labelDisplayedRows: ({ from, to, count }) => `${from}-${to === -1 ? count : to} of ${count}`, + nextIconButtonText: '次のページ', + }, + MuiRating: { + getLabelText: value => `${value} ${value !== 1 ? '出演者' : '星'}`, + }, + MuiAutocomplete: { + clearText: 'クリア', + closeText: '閉じる', + loadingText: '積み込み…', + noOptionsText: '結果がありません', + openText: '開いた', + }, + }, +}; + +export const ptBR = { + props: { + MuiTablePagination: { + backIconButtonText: 'Página anterior', + labelRowsPerPage: 'Linhas por página:', + labelDisplayedRows: ({ from, to, count }) => `${from}-${to === -1 ? count : to} of ${count}`, + nextIconButtonText: 'Próxima página', + }, + MuiRating: { + getLabelText: value => `${value} Estrela${value !== 1 ? 's' : ''}`, + }, + MuiAutocomplete: { + clearText: 'Claro', + closeText: 'Fechar', + loadingText: 'Carregando…', + noOptionsText: 'Sem opções', + openText: 'Abrir', + }, + }, +}; + +export const ruRU = { + props: { + MuiTablePagination: { + backIconButtonText: 'Предыдущая страница', + labelRowsPerPage: 'Строк на страницу:', + labelDisplayedRows: ({ from, to, count }) => `${from}-${to === -1 ? count : to} of ${count}`, + nextIconButtonText: 'Следующая страница', + }, + MuiRating: { + getLabelText: value => `${value} ${value !== 1 ? 'Звезды' : 'звезда'}`, + }, + MuiAutocomplete: { + clearText: 'чистый', + closeText: 'близко', + loadingText: 'загрузка…', + noOptionsText: 'Нет вариантов', + openText: 'открыто', + }, + }, +}; + +export const zhCN = { + props: { + MuiTablePagination: { + backIconButtonText: '上一页', + labelDisplayedRows: ({ from, to, count }) => `${from}-${to === -1 ? count : to} 的 ${count}`, + labelRowsPerPage: '每页行数:', + nextIconButtonText: '下一页', + }, + MuiRating: { + getLabelText: value => `${value} 星${value !== 1 ? '星' : ''}`, + }, + MuiAutocomplete: { + clearText: '明确', + closeText: '关', + loadingText: '载入中…', + noOptionsText: '没有选择', + openText: '打开', + }, + }, +}; diff --git a/packages/material-ui/src/styles/createMuiTheme.d.ts b/packages/material-ui/src/styles/createMuiTheme.d.ts index 0f0c49d6ffb151..1a7a0b781733b9 100644 --- a/packages/material-ui/src/styles/createMuiTheme.d.ts +++ b/packages/material-ui/src/styles/createMuiTheme.d.ts @@ -42,4 +42,4 @@ export interface Theme { zIndex: ZIndex; } -export default function createMuiTheme(options?: ThemeOptions): Theme; +export default function createMuiTheme(options?: ThemeOptions, ...args: object[]): Theme; diff --git a/packages/material-ui/src/styles/createMuiTheme.js b/packages/material-ui/src/styles/createMuiTheme.js index 495a8dd9d970db..24a70cdb0a804d 100644 --- a/packages/material-ui/src/styles/createMuiTheme.js +++ b/packages/material-ui/src/styles/createMuiTheme.js @@ -9,12 +9,11 @@ import createSpacing from './createSpacing'; import transitions from './transitions'; import zIndex from './zIndex'; -function createMuiTheme(options = {}) { +function createMuiTheme(options = {}, ...args) { const { breakpoints: breakpointsInput = {}, mixins: mixinsInput = {}, palette: paletteInput = {}, - shadows: shadowsInput, spacing: spacingInput, typography: typographyInput = {}, ...other @@ -24,25 +23,25 @@ function createMuiTheme(options = {}) { const breakpoints = createBreakpoints(breakpointsInput); const spacing = createSpacing(spacingInput); - const muiTheme = { - breakpoints, - direction: 'ltr', - mixins: createMixins(breakpoints, spacing, mixinsInput), - overrides: {}, // Inject custom styles - palette, - props: {}, // Inject custom props - shadows: shadowsInput || shadows, - typography: createTypography(palette, typographyInput), - spacing, - ...deepmerge( - { - shape, - transitions, - zIndex, - }, - other, - ), - }; + let muiTheme = deepmerge( + { + breakpoints, + direction: 'ltr', + mixins: createMixins(breakpoints, spacing, mixinsInput), + overrides: {}, // Inject custom styles + palette, + props: {}, // Provide default props + shadows, + typography: createTypography(palette, typographyInput), + spacing, + shape, + transitions, + zIndex, + }, + other, + ); + + muiTheme = args.reduce((acc, argument) => deepmerge(acc, argument), muiTheme); if (process.env.NODE_ENV !== 'production') { const pseudoClasses = [ diff --git a/packages/material-ui/src/styles/createMuiTheme.test.js b/packages/material-ui/src/styles/createMuiTheme.test.js index 143d0128ba3e74..ffd12ec0b38d81 100644 --- a/packages/material-ui/src/styles/createMuiTheme.test.js +++ b/packages/material-ui/src/styles/createMuiTheme.test.js @@ -116,4 +116,9 @@ describe('createMuiTheme', () => { ); }); }); + + it('deep merges multiple arguments', () => { + const muiTheme = createMuiTheme({}, { foo: 'bar' }); + assert.strictEqual(muiTheme.foo, 'bar'); + }); });