feat: update config-provider

pull/1790/head
tangjinzhou 2020-02-11 17:31:43 +08:00
parent 2875d57a25
commit 50018321a2
12 changed files with 302 additions and 38 deletions

View File

@ -1,5 +1,5 @@
module.exports = {
dev: {
componentName: 'checkbox', // dev components
componentName: 'config-provider', // dev components
},
};

View File

@ -162,7 +162,7 @@ export default {
render() {
if (this.configProvider.csp) {
this.csp = csp;
this.csp = this.configProvider.csp;
}
return this.$slots.default && this.$slots.default[0];
},

View File

@ -0,0 +1,44 @@
import { mount } from '@vue/test-utils';
import ConfigProvider from '..';
import Button from '../../button';
import mountTest from '../../../tests/shared/mountTest';
describe('ConfigProvider', () => {
mountTest({
render() {
return (
<ConfigProvider>
<div />
</ConfigProvider>
);
},
});
it('Content Security Policy', () => {
const csp = { nonce: 'test-antd' };
const wrapper = mount({
render() {
return (
<ConfigProvider csp={csp}>
<Button />
</ConfigProvider>
);
},
});
expect(wrapper.find({ name: 'Wave' }).vm.csp).toBe(csp);
});
it('autoInsertSpaceInButton', () => {
const wrapper = mount({
render() {
return (
<ConfigProvider autoInsertSpaceInButton={false}>
<Button>确定</Button>
</ConfigProvider>
);
},
});
expect(wrapper.find({ name: 'AButton' }).text()).toBe('确定');
});
});

View File

@ -1,4 +1,5 @@
<script>
import Locale from './locale.md';
import CN from '../index.zh-CN.md';
import US from '../index.en-US.md';
@ -16,9 +17,14 @@ export default {
type: '其他',
title: 'ConfigProvider',
render() {
const csp = { nonce: 'test-antd' };
return (
<div>
<md cn={md.cn} us={md.us} />
<Locale />
<a-config-provider csp={csp}>
<a-button>确定</a-button>
</a-config-provider>
<api>
<template slot="cn">
<CN />

View File

@ -0,0 +1,151 @@
<cn>
#### 国际化
此处列出 Ant Design Vue 中需要国际化支持的组件,你可以在演示里切换语言。
</cn>
<us>
#### Locale
Components which need localization support are listed here, you can toggle the language in the demo.
</us>
```tpl
<template>
<div>
<div class="change-locale">
<span style="margin-right: 16px">Change locale of components: </span>
<a-radio-group :value="locale" @change="changeLocale">
<a-radio-button key="en" :value="enUS">
English
</a-radio-button>
<a-radio-button key="cn" :value="zhCN">
中文
</a-radio-button>
</a-radio-group>
</div>
<a-config-provider :locale="locale">
<div :key="locale ? locale.locale : 'en'" class="locale-components">
<div class="example">
<a-pagination :default-current="1" :total="50" show-size-changer />
</div>
<div class="example">
<a-select show-search style="width: 200px">
<a-select-option value="jack">
jack
</a-select-option>
<a-select-option value="lucy">
lucy
</a-select-option>
</a-select>
<a-date-picker />
<a-time-picker />
<a-range-picker style="width: 200px" />
</div>
<div class="example">
<a-button type="primary" @click="visible = true">
Show Modal
</a-button>
<a-button @click="info">
Show info
</a-button>
<a-button @click="confirm">
Show confirm
</a-button>
<a-popconfirm title="Question?">
<a href="#">Click to confirm</a>
</a-popconfirm>
</div>
<div class="example">
<a-transfer :data-source="[]" show-search :target-keys="[]" :render="item => item.title" />
</div>
<div style="width: 319px; border: 1px solid #d9d9d9; border-radius: 4px">
<a-calendar :fullscreen="false" :value="moment()" />
</div>
<div class="example">
<a-table :data-source="[]" :columns="columns" />
</div>
<a-modal v-model="visible" title="Locale Modal">
<p>Locale Modal</p>
</a-modal>
</div>
</a-config-provider>
</div>
</template>
<script>
import enUS from 'ant-design-vue/locale-provider/en_US';
import zhCN from 'ant-design-vue/locale-provider/zh_CN';
import moment from 'moment';
import 'moment/locale/zh-cn';
moment.locale('en');
const columns = [
{
title: 'Name',
dataIndex: 'name',
filters: [
{
text: 'filter1',
value: 'filter1',
},
],
},
{
title: 'Age',
dataIndex: 'age',
},
];
export default {
data() {
return {
columns,
visible: false,
locale: enUS,
moment,
enUS,
zhCN,
};
},
methods: {
changeLocale(e) {
const localeValue = e.target.value;
this.locale = localeValue;
if (!localeValue) {
moment.locale('en');
} else {
moment.locale('zh-cn');
}
},
info() {
this.$info({
title: 'some info',
content: 'some info',
});
},
confirm(){
this.$confirm({
title: 'some info',
content: 'some info',
});
},
},
};
</script>
<style scoped>
.locale-components {
border-top: 1px solid #d9d9d9;
padding-top: 16px;
}
.example {
margin: 16px 0;
}
.example > * {
margin-right: 8px;
}
.change-locale {
margin-bottom: 16px;
}
</style>
```

View File

@ -1,9 +0,0 @@
<svg width="64" height="41" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 1)" fill="none" fill-rule="evenodd">
<ellipse fill="#F5F5F5" cx="32" cy="33" rx="32" ry="7"/>
<g fill-rule="nonzero" stroke="#D9D9D9">
<path d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"/>
<path d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z" fill="#FAFAFA"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 657 B

View File

@ -25,7 +25,7 @@ This component provides a configuration to all Vue components underneath itself
### Content Security Policy
Some component use dynamic style to support wave effect. You can config `csp` prop if Content Security Policy (CSP) is enabled:
Some components use dynamic style to support wave effect. You can config `csp` prop if Content Security Policy (CSP) is enabled:
```html
<a-config-provider :csp="{ nonce: 'YourNonceCode' }">
@ -35,10 +35,36 @@ Some component use dynamic style to support wave effect. You can config `csp` pr
## API
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| autoInsertSpaceInButton | Set `false` to remove space between 2 chinese characters on Button | boolean | true |
| csp | Set [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) config | { nonce: string } | - |
| renderEmpty | set empty content of components. Ref [Empty](/components/empty/) | slot-scope \| Function(componentName: string): ReactNode | - |
| getPopupContainer | to set the container of the popup element. The default is to create a `div` element in `body`. | Function(triggerNode, dialogContext) | `() => document.body` |
| prefixCls | set prefix class | string | ant |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| autoInsertSpaceInButton | Set `false` to remove space between 2 chinese characters on Button | boolean | true | |
| csp | Set [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) config | { nonce: string } | - | |
| renderEmpty | set empty content of components. Ref [Empty](/components/empty/) | slot-scope \| Function(componentName: string): ReactNode | - | |
| getPopupContainer | to set the container of the popup element. The default is to create a `div` element in `body`. | Function(triggerNode, dialogContext) | `() => document.body` | |
| locale | language package setting, you can find the packages in [antd/es/locale](http://unpkg.com/ant-design-vue/es/locale/) | object | - | 1.5.0 |
| prefixCls | set prefix class | string | ant | |
| pageHeader | Unify the ghost of pageHeader ,Ref [pageHeader](<(/components/page-header)> | { ghost:boolean } | 'true' | 1.5.0 |
## FAQ
#### Does the locale problem still exist in DatePicker even if ConfigProvider `locale` is used?
Please make sure you set moment locale by `moment.locale('zh-cn')` or that you don't have two different versions of moment.
#### Modal throw error when setting `getPopupContainer`?
When you config `getPopupContainer` to parentNode globally, Modal will throw error of `triggerNode is undefined` because it did not have a triggerNode.
```diff
<ConfigProvider
- getPopupContainer={triggerNode => triggerNode.parentNode}
+ getPopupContainer={node => {
+ if (node) {
+ return node.parentNode;
+ }
+ return document.body;
+ }}
>
<App />
</ConfigProvider>
```

View File

@ -3,11 +3,13 @@ import PropTypes from '../_util/vue-types';
import { filterEmpty, getComponentFromProp } from '../_util/props-util';
import defaultRenderEmpty from './renderEmpty';
import Base from '../base';
import LocaleProvider, { ANT_MARK } from '../locale-provider';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
function getWatch(keys = []) {
const watch = {};
keys.forEach(k => {
watch[k] = function() {
watch[k] = function(value) {
this._proxyVm._data[k] = value;
};
});
@ -22,6 +24,8 @@ const ConfigProvider = {
renderEmpty: PropTypes.func,
csp: PropTypes.object,
autoInsertSpaceInButton: PropTypes.bool,
locale: PropTypes.object,
pageHeader: PropTypes.object,
},
provide() {
const _self = this;
@ -39,7 +43,7 @@ const ConfigProvider = {
};
},
watch: {
...getWatch(['prefixCls', 'csp', 'autoInsertSpaceInButton']),
...getWatch(['prefixCls', 'csp', 'autoInsertSpaceInButton', 'locale', 'pageHeader']),
},
methods: {
renderEmptyComponent(h, name) {
@ -52,9 +56,21 @@ const ConfigProvider = {
if (customizePrefixCls) return customizePrefixCls;
return suffixCls ? `${prefixCls}-${suffixCls}` : prefixCls;
},
renderProvider(legacyLocale) {
return (
<LocaleProvider locale={this.locale || legacyLocale} _ANT_MARK__={ANT_MARK}>
{this.$slots.default ? filterEmpty(this.$slots.default)[0] : null}
</LocaleProvider>
);
},
},
render() {
return this.$slots.default ? filterEmpty(this.$slots.default)[0] : null;
return (
<LocaleReceiver
scopedSlots={{ default: (_, __, legacyLocale) => this.renderProvider(legacyLocale) }}
/>
);
},
};

View File

@ -35,10 +35,35 @@ ConfigProvider 使用 Vue 的 [provide / inject](https://vuejs.org/v2/api/#provi
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| autoInsertSpaceInButton | 设置为 `false` 时,移除按钮中 2 个汉字之间的空格 | boolean | true |
| csp | 设置 [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) 配置 | { nonce: string } | - |
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty/) | slot-scope \| Function(componentName: string): VNode | - |
| getPopupContainer | 弹出框Select, Tooltip, Menu 等等)渲染父节点,默认渲染到 body 上。 | Function(triggerNode, dialogContext) | () => document.body |
| prefixCls | 设置统一样式前缀 | string | ant |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| autoInsertSpaceInButton | 设置为 `false` 时,移除按钮中 2 个汉字之间的空格 | boolean | true | |
| csp | 设置 [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) 配置 | { nonce: string } | - | |
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty/) | slot-scope \| Function(componentName: string): VNode | - | |
| getPopupContainer | 弹出框Select, Tooltip, Menu 等等)渲染父节点,默认渲染到 body 上。 | Function(triggerNode, dialogContext) | () => document.body | |
| locale | 语言包配置,语言包可到 [antd/es/locale](http://unpkg.com/antd/es/locale/) 目录下寻找 | object | - | 1.5.0 |
| pageHeader | 统一设置 pageHeader 的 ghost参考 [pageHeader](<(/components/page-header)>) | { ghost: boolean } | 'true' | 1.5.0 |
## FAQ
#### 为什么我使用了 ConfigProvider `locale`,时间类组件的国际化还有问题?
请检查是否设置了 `moment.locale('zh-cn')`,或者是否有两个版本的 moment 共存。
#### 配置 `getPopupContainer` 导致 Modal 报错?
当如下全局设置 `getPopupContainer` 为触发节点的 parentNode 时,由于 Modal 的用法不存在 `triggerNode`,这样会导致 `triggerNode is undefined` 的报错,需要增加一个判断条件。
```diff
<ConfigProvider
- getPopupContainer={triggerNode => triggerNode.parentNode}
+ getPopupContainer={node => {
+ if (node) {
+ return node.parentNode;
+ }
+ return document.body;
+ }}
>
<App />
</ConfigProvider>
```

View File

@ -1,6 +1,5 @@
import PropTypes from '../_util/vue-types';
import Empty from '../empty';
import emptyImg from './empty.svg';
import { ConfigConsumerProps } from './';
const RenderEmpty = {
@ -19,13 +18,14 @@ const RenderEmpty = {
switch (componentName) {
case 'Table':
case 'List':
return <Empty image={emptyImg} class={`${prefix}-normal`} />;
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />;
case 'Select':
case 'TreeSelect':
case 'Cascader':
case 'Transfer':
return <Empty image={emptyImg} class={`${prefix}-small`} />;
case 'Mentions':
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} class={`${prefix}-small`} />;
default:
return <Empty />;

View File

@ -37,6 +37,7 @@ export default {
render() {
const { $scopedSlots } = this;
const children = this.children || $scopedSlots.default;
return children(this.getLocale(), this.getLocaleCode());
const { antLocale } = this.localeData;
return children(this.getLocale(), this.getLocaleCode(), antLocale);
},
};

View File

@ -3,6 +3,7 @@ import * as moment from 'moment';
import interopDefault from '../_util/interopDefault';
import { changeConfirmLocale } from '../modal/locale';
import Base from '../base';
import warning from '../_util/warning';
// export interface Locale {
// locale: string;
// Pagination?: Object;
@ -16,7 +17,7 @@ import Base from '../base';
// Select?: Object;
// Upload?: Object;
// }
export const ANT_MARK = 'internalMark';
function setMomentLocale(locale) {
if (locale && locale.locale) {
interopDefault(moment).locale(locale.locale);
@ -29,8 +30,14 @@ const LocaleProvider = {
name: 'ALocaleProvider',
props: {
locale: PropTypes.object.def({}),
_ANT_MARK__: PropTypes.string,
},
data() {
warning(
this._ANT_MARK__ === ANT_MARK,
'LocaleProvider',
'`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead',
);
return {
antLocale: {
...this.locale,
@ -50,6 +57,7 @@ const LocaleProvider = {
exist: true,
};
setMomentLocale(val);
changeConfirmLocale(val && val.Modal);
},
},
created() {
@ -57,10 +65,6 @@ const LocaleProvider = {
setMomentLocale(locale);
changeConfirmLocale(locale && locale.Modal);
},
updated() {
const { locale } = this;
changeConfirmLocale(locale && locale.Modal);
},
beforeDestroy() {
changeConfirmLocale();
},