merge master

This commit is contained in:
zombiej 2019-08-29 23:41:46 +08:00
commit 3d8a4f3612
282 changed files with 16141 additions and 2368 deletions

View File

@ -44,6 +44,9 @@ references:
- test_node:
requires:
- setup
- check_metadata:
requires:
- setup
jobs:
setup:
@ -72,6 +75,7 @@ jobs:
- *attach_workspace
- run: npm run dist
- run: node ./tests/dekko/dist.test.js
- run: npm run bundlesize
- persist_to_workspace:
root: ~/ant-design
paths:
@ -147,6 +151,13 @@ jobs:
- *attach_workspace
- run: npm run test-node -- -w 1
check_metadata:
<<: *container_config
steps:
- checkout
- *attach_workspace
- run: node ./scripts/check-demo.js
workflows:
version: 2
build_test:

View File

@ -7,3 +7,10 @@ components/*/__tests__/type.tsx
# Docs templates
site/theme/template/IconDisplay/*.js
site/theme/template/IconDisplay/*.jsx
typings
es/**/*
lib/**/*
node_modules
_site
dist
**/*.d.ts

View File

@ -16,7 +16,7 @@ const eslintrc = {
},
settings: {
react: {
version: '16.8',
version: '16.9',
},
},
parser: '@typescript-eslint/parser',
@ -24,13 +24,14 @@ const eslintrc = {
// https://github.com/typescript-eslint/typescript-eslint/issues/46#issuecomment-470486034
overrides: [
{
files: ['*.tsx'],
files: ['*.ts', '*.tsx'],
rules: {
'@typescript-eslint/no-unused-vars': [2, { args: 'none' }],
},
},
],
rules: {
camelcase: 0,
'react/jsx-one-expression-per-line': 0,
'react/prop-types': 0,
'react/forbid-prop-types': 0,
@ -56,6 +57,8 @@ const eslintrc = {
'jsx-a11y/anchor-is-valid': 0,
'comma-dangle': ['error', 'always-multiline'],
'react/jsx-filename-extension': 0,
'react/state-in-constructor': 0,
'react/jsx-props-no-spreading': 0,
'prefer-destructuring': 0, // TODO: remove later
'consistent-return': 0, // TODO: remove later
'no-return-assign': 0, // TODO: remove later
@ -78,15 +81,20 @@ const eslintrc = {
'react/display-name': 0,
// ban this for Number.isNaN needs polyfill
'no-restricted-globals': 0,
'max-classes-per-file': 0,
'react/static-property-placement': 0,
},
globals: {
gtag: true,
},
};
if (process.env.RUN_ENV === 'DEMO') {
eslintrc.globals = {
eslintrc.globals = Object.assign(eslintrc.globals, {
React: true,
ReactDOM: true,
mountNode: true,
};
});
Object.assign(eslintrc.rules, {
indent: 0,
@ -100,6 +108,8 @@ if (process.env.RUN_ENV === 'DEMO') {
'react/no-multi-comp': 0,
'jsx-a11y/href-no-hash': 0,
'import/no-extraneous-dependencies': 0,
'import/no-unresolved': 0,
'jsx-a11y/control-has-associated-label': 0,
});
}

14
.github/main.workflow vendored
View File

@ -1,14 +0,0 @@
workflow "Deploy website" {
on = "release"
resolves = ["Deploy"]
}
action "Deploy" {
uses = "docker://node:10"
runs = [
"sh",
"-c",
"git remote set-url origin https://${DEPLOY_TOKEN}@github.com/ant-design/ant-design.git && npm install && npm run deploy"
],
secrets = ["DEPLOY_TOKEN"]
}

19
.github/workflows/deploy-site.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Deploy website
on:
release:
branches:
- master
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Deploy website
uses: JamesIves/github-pages-deploy-action@master
env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
BRANCH: gh-pages
FOLDER: _site
BUILD_SCRIPT: npm install && npm run predeploy

19
.github/workflows/nodejs.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Node CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Use Node.js 10.x
uses: actions/setup-node@v1
with:
version: 10.x
- name: npm install, build, and test
run: |
npm install
npm run test-all

View File

@ -15,6 +15,63 @@ timeline: true
---
## 3.22.2
`2019-08-27`
- 🐞 Fix Mentions has additional height in Form. [#18478](https://github.com/ant-design/ant-design/pull/18478)
- 🐞 Fix disabled Input should not be allowed to clear. [#18482](https://github.com/ant-design/ant-design/pull/18482)
- 🐞 Fix Input.Password crash with `Cannot read property 'input' of null` when unmount. [#18475](https://github.com/ant-design/ant-design/pull/18475)
- 🐞 Fix Table `style` should applied to outside wrapper. [#18494](https://github.com/ant-design/ant-design/pull/18494)
- 🐞 Fix PageHeader default english text. [#18471](https://github.com/ant-design/ant-design/pull/18471) [@hjiawei](https://github.com/hjiawei)
## 3.22.1
`2019-08-26`
- 🔥 The official website now supports the search icon through the picture! [#18425](https://github.com/ant-design/ant-design/pull/18425)
- 💄 Tweak Table expand icon style. [c5344bd](https://github.com/ant-design/ant-design/commit/c5344bde529a2f2ec814f46e7ec5d249eac8d608)
- 🐞 Fix prop `style` is getting duplicated on Table. [#18330](https://github.com/ant-design/ant-design/pull/18330) [@MrHeer](https://github.com/MrHeer)
- 🐞 Fix Input line height style bug in IE11. [#17759](https://github.com/ant-design/ant-design/pull/17759)
- 🐞 Fix Steps progressDot broken style. [#18356](https://github.com/ant-design/ant-design/pull/18356)
- 🐞 Fix an issue with plain icon button style errors. [#18458](https://github.com/ant-design/ant-design/pull/18458) [@qhanw](https://github.com/qhanw)
- 🐞 Fix TextArea with `autosize` in controlled mode that scrollbar blink when typing. [#18401](https://github.com/ant-design/ant-design/pull/18401)
- 🐞 Fixed an issue where Input.Password `ref` could not get the input element and had no `focus` and `blur` method. [#18441](https://github.com/ant-design/ant-design/pull/18441)
- 🐞 Fix Upload wrong line break. [#18423](https://github.com/ant-design/ant-design/pull/18423)
- 💄 Add less variables `@select-dropdown-bg` `@select-item-selected-bg` `@select-item-active-bg` `@anchor-border-colorr` `@descriptions-bg`. [#18444](https://github.com/ant-design/ant-design/pull/18444) [#18372](https://github.com/ant-design/ant-design/pull/18440) [@MrHeer](https://github.com/MrHeer)
## 3.22.0
`2019-08-17`
- 🔥 New type `navigation` of Step. [#17994](https://github.com/ant-design/ant-design/pull/17994)
- <img width="600" class="markdown-inline-image" src="https://gw.alipayobjects.com/zos/antfincdn/oc7rRuPBbR/421d7885-a822-4375-9deb-92d607e0d9de.png" />
- 🇷🇴 Add Romanian locale. [#18163](https://github.com/ant-design/ant-design/pull/18163) [@stefy](https://github.com/stefy)
- Anchor
- 🌟 Add `getCurrentAnchor` property to specify current active anchor. [#17823](https://github.com/ant-design/ant-design/pull/17823) [@shaodahong](https://github.com/shaodahong)
- 🌟 Add `targetOffset` property to customize scroll position offset. [#17827](https://github.com/ant-design/ant-design/pull/17827) [@shaodahong](https://github.com/shaodahong)
- 🌟 Drawer supports popup in custom dom node. [#18167](https://github.com/ant-design/ant-design/pull/18167)
- 🌟 Mentions support `getPopupContainer` property. [#18298](https://github.com/ant-design/ant-design/pull/18298) [@vijayst](https://github.com/vijayst)
- 🌟 Modal support custom `closeIcon`. [#18309](https://github.com/ant-design/ant-design/pull/18309)
- 🌟 Upload support to preview `jfif` format images. [#18322](https://github.com/ant-design/ant-design/pull/18322)
- 💄 Tweak Descriptions.Item padding bottom via size. [#18270](https://github.com/ant-design/ant-design/pull/18270)
- Cascader
- 🌟 Allow input `autoComplete` to be overrided. [#18279](https://github.com/ant-design/ant-design/pull/18279) [@zomars](https://github.com/zomars)
- 🐞 Fix wrong `notFoundContent` width when using `fieldNames`.[#18325](https://github.com/ant-design/ant-design/pull/18325)
- 🐞 Fix missing `options` cause crash. [#18190](https://github.com/ant-design/ant-design/pull/18190) [@nnecec](https://github.com/nnecec)
- 🐞 Fix Menu.SubMenu `className` applied to popup menu mistakenly. [#18290](https://github.com/ant-design/ant-design/pull/18290)
- 🐞 Upgrade react-slick to fix Carousel lifecycle warning. [#18209](https://github.com/ant-design/ant-design/pull/18209)
- 🐞 Fix Button `received false for a non-boolean attribute loading` warning. [#18208](https://github.com/ant-design/ant-design/pull/18208)
- 🐞 Fix style problem when hovering Table selected row. [#18261](https://github.com/ant-design/ant-design/pull/18261)
- 🐞 Fix Checkbox hovering border color when it is `disabled`. [#18168](https://github.com/ant-design/ant-design/pull/18168)
- 🐞 Fix Progress missing or messed gradient color. [#18284](https://github.com/ant-design/ant-design/pull/18284)
- 🐞 修复 TextArea scrollbar blinking problem when using `autosize` and `maxRows`. [#18289](https://github.com/ant-design/ant-design/pull/18289)
- TypeScript
- 🐞 Fix some components (Tooltip, Breadcrumb, Badge) importing error. [#18282](https://github.com/ant-design/ant-design/pull/18282) [@lidianhao123](https://github.com/lidianhao123)
- 🐞 Fix MonthPicker `monthCellContentRender` property definition. [#18192](https://github.com/ant-design/ant-design/pull/18192) [@JonathanLee-LX](https://github.com/JonathanLee-LX)
- 🐞 Fix Upload.Dragger `children` error. [#18196](https://github.com/ant-design/ant-design/pull/18196)
- 🐞 Fix Tag.CheckableTag `style` property definition. [#18300](https://github.com/ant-design/ant-design/pull/18300)
## 3.21.4
`2019-08-09`
@ -985,7 +1042,6 @@ timeline: true
- 🐞 Fixed Table `Cannot read property 'children' of undefined` error when customize `column.title` as ReactNode. [#13542](https://github.com/ant-design/ant-design/issues/13542) [@geraldchen890806](https://github.com/geraldchen890806)
- 🐞 Fixed another border problem of Button when customized less variable `@border-width-base`. [#13534](https://github.com/ant-design/ant-design/issues/13534) [@morenyang](https://github.com/morenyang)
- 🐞 Fixed Upload don't support resolve `Blob` object when `beforeUpload` returns a Promise. [#13528](https://github.com/ant-design/ant-design/pull/13528/) [@huanz](https://github.com/huanz)
- https://github.com/ant-design/ant-design/pull/13536
- 🐞 Fixed two props of Dropdown TypeScript definitions. [#13536](https://github.com/ant-design/ant-design/pull/13536) [@wangxingkang](https://github.com/wangxingkang)
## 3.11.1

View File

@ -15,6 +15,63 @@ timeline: true
---
## 3.22.2
`2019-08-27`
- 🐞 修复 Mentions 在 Form 中高度略高的问题。[#18478](https://github.com/ant-design/ant-design/pull/18478)
- 🐞 修复失效 Input 依然支持 allowClear 的问题。[#18482](https://github.com/ant-design/ant-design/pull/18482)
- 🐞 修复 Input.Password unmount 时报错 `Cannot read property 'input' of null`。[#18475](https://github.com/ant-design/ant-design/pull/18475)
- 🐞 修正 Table `style` 属性到最外层容器。[#18494](https://github.com/ant-design/ant-design/pull/18494)
- 🐞 修正 PageHeader 默认英文文案。[#18471](https://github.com/ant-design/ant-design/pull/18471) [@hjiawei](https://github.com/hjiawei)
## 3.22.1
`2019-08-26`
- 🔥 官网现在支持通过图片搜索图标啦![#18425](https://github.com/ant-design/ant-design/pull/18425)
- 💄 调整 Table 展开按钮的样式。[c5344bd](https://github.com/ant-design/ant-design/commit/c5344bde529a2f2ec814f46e7ec5d249eac8d608)
- 🐞 修复 Table 的 `style` 属性被应用了两次的问题。[#18330](https://github.com/ant-design/ant-design/pull/18330) [@MrHeer](https://github.com/MrHeer)
- 🐞 修复 Input 在 IE11 下错位的问题。[#17759](https://github.com/ant-design/ant-design/pull/17759)
- 🐞 修复 Input.Password `ref` 获取不到 input 元素且没有 `focus``blur` 方法的问题。[#18441](https://github.com/ant-design/ant-design/pull/18441)
- 🐞 修复 Steps `progressDot` 样式错位问题。[#18356](https://github.com/ant-design/ant-design/pull/18356)
- 🐞 修复纯图标按钮样式错误的问题。[#18458](https://github.com/ant-design/ant-design/pull/18458) [@qhanw](https://github.com/qhanw)
- 🐞 修复 TextArea 受控模式下配置 `autosize` 时,输入会导致滚动条闪烁的问题。[#18401](https://github.com/ant-design/ant-design/pull/18401)
- 🐞 修复 Upload 错误换行的问题。[#18423](https://github.com/ant-design/ant-design/pull/18423)
- 💄 增加 less 变量 `@select-dropdown-bg` `@select-item-selected-bg` `@select-item-active-bg` `@anchor-border-colorr` `@descriptions-bg`。[#18444](https://github.com/ant-design/ant-design/pull/18444) [#18372](https://github.com/ant-design/ant-design/pull/18440) [@MrHeer](https://github.com/MrHeer)
## 3.22.0
`2019-08-17`
- 🔥 新增 Steps `type="navigation"` 导航步骤条。[#17994](https://github.com/ant-design/ant-design/pull/17994)
- <img width="600" class="markdown-inline-image" src="https://gw.alipayobjects.com/zos/antfincdn/oc7rRuPBbR/421d7885-a822-4375-9deb-92d607e0d9de.png" />
- 🇷🇴 新增罗马尼亚语。[#18163](https://github.com/ant-design/ant-design/pull/18163) [@stefy](https://github.com/stefy)
- Anchor
- 🌟 新增 `getCurrentAnchor` 属性用于指定当前高亮锚点。[#17823](https://github.com/ant-design/ant-design/pull/17823) [@shaodahong](https://github.com/shaodahong)
- 🌟 新增 `targetOffset` 属性用于自定义滚动偏移量。[#17827](https://github.com/ant-design/ant-design/pull/17827) [@shaodahong](https://github.com/shaodahong)
- 🌟 支持 Drawer 在局部节点弹出。[#18167](https://github.com/ant-design/ant-design/pull/18167)
- 🌟 Mentions 新增 `getPopupContainer` 属性。[#18298](https://github.com/ant-design/ant-design/pull/18298) [@vijayst](https://github.com/vijayst)
- 🌟 Modal 支持 `closeIcon` 属性用于自定义关闭图标。[#18309](https://github.com/ant-design/ant-design/pull/18309)
- 🌟 Upload 支持预览 `jfif` 格式图片。[#18322](https://github.com/ant-design/ant-design/pull/18322)
- 💄 调整不同大小下 Descriptions.Item 内边距样式。 [#18270](https://github.com/ant-design/ant-design/pull/18270)
- Cascader
- 🌟 允许自定义输入框的 `autoComplete` 属性可以被覆盖。[#18279](https://github.com/ant-design/ant-design/pull/18279) [@zomars](https://github.com/zomars)
- 🐞 修复指定 `fieldNames` 时空数据弹层宽度问题。[#18325](https://github.com/ant-design/ant-design/pull/18325)
- 🐞 修复 `options` 参数未传入数组类型导致页面崩溃。[#18190](https://github.com/ant-design/ant-design/pull/18190) [@nnecec](https://github.com/nnecec)
- 🐞 修复 Menu.SubMenu `className` 错误应用到弹层上的问题。[#18290](https://github.com/ant-design/ant-design/pull/18290)
- 🐞 升级 react-slick 以修复 Carousel 的生命周期警告问题。[#18209](https://github.com/ant-design/ant-design/pull/18209)
- 🐞 修复 Button `received false for a non-boolean attribute loading` 警告信息。[#18208](https://github.com/ant-design/ant-design/pull/18208)
- 🐞 修复 Table 选中行 hover 背景样式问题。[#18261](https://github.com/ant-design/ant-design/pull/18261)
- 🐞 修复 Checkbox 失效时的 hover 边框颜色。[#18168](https://github.com/ant-design/ant-design/pull/18168)
- 🐞 修复 Progress 渐变色混乱和失效的问题。[#18284](https://github.com/ant-design/ant-design/pull/18284)
- 🐞 修复 TextArea `autosize` 内使用 `maxRows` 时滚动条闪烁的问题。[#18289](https://github.com/ant-design/ant-design/pull/18289)
- TypeScript
- 🐞 修复部分组件Tooltip、Breadcrumb、Badge无法引入的问题。[#18282](https://github.com/ant-design/ant-design/pull/18282) [@lidianhao123](https://github.com/lidianhao123)
- 🐞 修复 MonthPicker `monthCellContentRender` 属性。[#18192](https://github.com/ant-design/ant-design/pull/18192) [@JonathanLee-LX](https://github.com/JonathanLee-LX)
- 🐞 修复 Upload.Dragger `children` 报错的问题。[#18196](https://github.com/ant-design/ant-design/pull/18196)
- 🐞 修复 Tag.CheckableTag 的 `style` 属性定义。[#18300](https://github.com/ant-design/ant-design/pull/18300)
## 3.21.4
`2019-08-09`
@ -982,7 +1039,6 @@ timeline: true
- 🐞 修复 Table 使用自定义列头时报 `Cannot read property 'children' of undefined` 的问题。[#13542](https://github.com/ant-design/ant-design/issues/13542) [@geraldchen890806](https://github.com/geraldchen890806)
- 🐞 修复另一个 Input 在自定义了 less 变量 `@border-width-base` 时的边框问题。[#13534](https://github.com/ant-design/ant-design/pull/13534) [@morenyang](https://github.com/morenyang)
- 🐞 修复 Upload 的 `beforeUpload` 方法返回 Promise 时不支持 resolve `Blob` 对象的问题。[#13528](https://github.com/ant-design/ant-design/pull/13528/) [@huanz](https://github.com/huanz)
- https://github.com/ant-design/ant-design/pull/13536
- 🐞 修复 Dropdown 两个属性的 TypeScript 定义。[#13536](https://github.com/ant-design/ant-design/pull/13536) [@wangxingkang](https://github.com/wangxingkang)
## 3.11.1

View File

@ -1,10 +1,12 @@
const __NULL__ = { notExist: true };
export function spyElementPrototypes(Element, properties) {
const propNames = Object.keys(properties);
const originDescriptors = {};
propNames.forEach(propName => {
const originDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, propName);
originDescriptors[propName] = originDescriptor;
originDescriptors[propName] = originDescriptor || __NULL__;
const spyProp = properties[propName];
@ -37,7 +39,9 @@ export function spyElementPrototypes(Element, properties) {
mockRestore() {
propNames.forEach(propName => {
const originDescriptor = originDescriptors[propName];
if (typeof originDescriptor === 'function') {
if (originDescriptor === __NULL__) {
delete Element.prototype[propName];
} else if (typeof originDescriptor === 'function') {
Element.prototype[propName] = originDescriptor;
} else {
Object.defineProperty(Element.prototype, propName, originDescriptor);

View File

@ -0,0 +1,13 @@
import { easeInOutCubic } from '../easings';
describe('Test easings', () => {
it('easeInOutCubic return value', () => {
const nums = [];
// eslint-disable-next-line no-plusplus
for (let index = 0; index < 5; index++) {
nums.push(easeInOutCubic(index, 1, 5, 4));
}
expect(nums).toEqual([1, 1.25, 3, 4.75, 5]);
});
});

View File

@ -0,0 +1,56 @@
import scrollTo from '../scrollTo';
describe('Test ScrollTo function', () => {
let dateNowMock;
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
beforeEach(() => {
dateNowMock = jest
.spyOn(Date, 'now')
.mockImplementationOnce(() => 0)
.mockImplementationOnce(() => 1000);
});
afterEach(() => {
dateNowMock.mockRestore();
});
it('test scrollTo', async () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.scrollY = y;
window.pageYOffset = y;
});
scrollTo(1000);
jest.runAllTimers();
expect(window.pageYOffset).toBe(1000);
scrollToSpy.mockRestore();
});
it('test callback - option', async () => {
const cbMock = jest.fn();
scrollTo(1000, {
callback: cbMock,
});
jest.runAllTimers();
expect(cbMock).toHaveBeenCalledTimes(1);
});
it('test getContainer - option', async () => {
const div = document.createElement('div');
scrollTo(1000, {
getContainer: () => div,
});
jest.runAllTimers();
expect(div.scrollTop).toBe(1000);
});
});

View File

@ -9,6 +9,8 @@ import triggerEvent from '../triggerEvent';
import Wave from '../wave';
import TransButton from '../transButton';
import openAnimation from '../openAnimation';
import ResizeObserver from '../resizeObserver';
import { spyElementPrototype } from '../../__tests__/util/domHook';
describe('Test utils function', () => {
beforeAll(() => {
@ -143,7 +145,9 @@ describe('Test utils function', () => {
it('bindAnimationEvent should return when node is null', () => {
const wrapper = mount(
<Wave>
<button type="button" disabled />
<button type="button" disabled>
button
</button>
</Wave>,
).instance();
expect(wrapper.bindAnimationEvent()).toBe(undefined);
@ -152,7 +156,9 @@ describe('Test utils function', () => {
it('bindAnimationEvent.onClick should return when children is hidden', () => {
const wrapper = mount(
<Wave>
<button type="button" style={{ display: 'none' }} />
<button type="button" style={{ display: 'none' }}>
button
</button>
</Wave>,
).instance();
expect(wrapper.bindAnimationEvent()).toBe(undefined);
@ -217,4 +223,47 @@ describe('Test utils function', () => {
expect(done).toHaveBeenCalled();
});
});
describe('ResizeObserver', () => {
let domMock;
beforeAll(() => {
domMock = spyElementPrototype(HTMLDivElement, 'getBoundingClientRect', () => {
return {
width: 1128 + Math.random(),
height: 903 + Math.random(),
};
});
});
afterAll(() => {
domMock.mockRestore();
});
it('should not trigger `onResize` if size shaking', () => {
const onResize = jest.fn();
let divNode;
const wrapper = mount(
<ResizeObserver onResize={onResize}>
<div
ref={node => {
divNode = node;
}}
/>
</ResizeObserver>,
);
// First trigger
wrapper.instance().onResize([{ target: divNode }]);
onResize.mockReset();
// Repeat trigger should not trigger outer `onResize` with shaking
for (let i = 0; i < 10; i += 1) {
wrapper.instance().onResize([{ target: divNode }]);
}
expect(onResize).not.toHaveBeenCalled();
});
});
});

View File

@ -1,5 +1,6 @@
import { tuple } from './type';
// eslint-disable-next-line import/prefer-default-export
export const PresetColorTypes = tuple(
'pink',
'red',

View File

@ -0,0 +1,9 @@
// eslint-disable-next-line import/prefer-default-export
export function easeInOutCubic(t: number, b: number, c: number, d: number) {
const cc = c - b;
t /= d / 2;
if (t < 1) {
return (cc / 2) * t * t * t + b;
}
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
}

View File

@ -28,7 +28,7 @@ export default function wrapperRaf(callback: () => void, delayFrames: number = 1
return myId;
}
wrapperRaf.cancel = function(pid?: number) {
wrapperRaf.cancel = function cancel(pid?: number) {
if (pid === undefined) return;
raf.cancel(ids[pid]);

View File

@ -1,5 +1,6 @@
import * as React from 'react';
// eslint-disable-next-line import/prefer-default-export
export function cloneElement(element: React.ReactNode, ...restArgs: any[]) {
if (!React.isValidElement(element)) return element;

View File

@ -10,9 +10,19 @@ interface ResizeObserverProps {
onResize?: () => void;
}
class ReactResizeObserver extends React.Component<ResizeObserverProps, {}> {
interface ResizeObserverState {
height: number;
width: number;
}
class ReactResizeObserver extends React.Component<ResizeObserverProps, ResizeObserverState> {
resizeObserver: ResizeObserver | null = null;
state = {
width: 0,
height: 0,
};
componentDidMount() {
this.onComponentUpdated();
}
@ -38,10 +48,30 @@ class ReactResizeObserver extends React.Component<ResizeObserverProps, {}> {
}
}
onResize = () => {
onResize: ResizeObserverCallback = (entries: ResizeObserverEntry[]) => {
const { onResize } = this.props;
if (onResize) {
onResize();
const { target } = entries[0];
const { width, height } = target.getBoundingClientRect();
/**
* Resize observer trigger when content size changed.
* In most case we just care about element size,
* let's use `boundary` instead of `contentRect` here to avoid shaking.
*/
const fixedWidth = Math.floor(width);
const fixedHeight = Math.floor(height);
if (this.state.width !== fixedWidth || this.state.height !== fixedHeight) {
this.setState({
width: fixedWidth,
height: fixedHeight,
});
if (onResize) {
onResize();
}
}
};

View File

@ -12,6 +12,7 @@ if (typeof window !== 'undefined') {
};
};
window.matchMedia = window.matchMedia || matchMediaPolyfill;
// eslint-disable-next-line global-require
enquire = require('enquire.js');
}
@ -57,8 +58,8 @@ const responsiveObserve = {
}
const token = (++subUid).toString();
subscribers.push({
token: token,
func: func,
token,
func,
});
func(screens);
return token;

View File

@ -0,0 +1,37 @@
import raf from 'raf';
import getScroll from './getScroll';
import { easeInOutCubic } from './easings';
interface ScrollToOptions {
/** Scroll container, default as window */
getContainer?: () => HTMLElement | Window;
/** Scroll end callback */
callback?: () => any;
/** Animation duration, default as 450 */
duration?: number;
}
export default function scrollTo(y: number, options: ScrollToOptions = {}) {
const { getContainer = () => window, callback, duration = 450 } = options;
const container = getContainer();
const scrollTop = getScroll(container, true);
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop);
} else {
(container as HTMLElement).scrollTop = nextScrollTop;
}
if (time < duration) {
raf(frameFunc);
} else if (typeof callback === 'function') {
callback();
}
};
raf(frameFunc);
}

View File

@ -1,4 +1,4 @@
function isStyleSupport(styleName: string | Array<string>): boolean {
const isStyleSupport = (styleName: string | Array<string>): boolean => {
if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
const { documentElement } = window.document;
@ -6,7 +6,7 @@ function isStyleSupport(styleName: string | Array<string>): boolean {
return styleNameList.some(name => name in documentElement.style);
}
return false;
}
};
export const isFlexSupported = isStyleSupport(['flex', 'webkitFlex', 'Flex', 'msFlex']);

View File

@ -20,6 +20,7 @@ export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
}
export function throttleByAnimationFrameDecorator() {
// eslint-disable-next-line func-names
return function(target: any, key: string, descriptor: any) {
const fn = descriptor.value;
let definingProperty = false;

View File

@ -0,0 +1,7 @@
import warning, { resetWarned } from 'rc-util/lib/warning';
export { resetWarned };
export default (valid: boolean, component: string, message: string): void => {
warning(valid, `[antd: ${component}] ${message}`);
};

View File

@ -1,14 +0,0 @@
import warning from 'warning';
let warned: Record<string, boolean> = {};
export function resetWarned() {
warned = {};
}
export default (valid: boolean, component: string, message: string): void => {
if (!valid && !warned[message]) {
warning(false, `[antd: ${component}] ${message}`);
warned[message] = true;
}
};

View File

@ -3,6 +3,7 @@ import { mount } from 'enzyme';
import Affix from '..';
import { getObserverEntities } from '../utils';
import Button from '../../button';
import { spyElementPrototype } from '../../__tests__/util/domHook';
const events = {};
@ -40,6 +41,7 @@ class AffixMounter extends React.Component {
describe('Affix Render', () => {
let wrapper;
let domMock;
const classRect = {
container: {
@ -48,23 +50,21 @@ describe('Affix Render', () => {
},
};
const originGetBoundingClientRect = HTMLElement.prototype.getBoundingClientRect;
HTMLElement.prototype.getBoundingClientRect = function getBoundingClientRect() {
return (
classRect[this.className] || {
top: 0,
bottom: 0,
}
);
};
beforeAll(() => {
jest.useFakeTimers();
domMock = spyElementPrototype(HTMLElement, 'getBoundingClientRect', function mockBounding() {
return (
classRect[this.className] || {
top: 0,
bottom: 0,
}
);
});
});
afterAll(() => {
jest.useRealTimers();
HTMLElement.prototype.getBoundingClientRect = originGetBoundingClientRect;
domMock.mockRestore();
});
const movePlaceholder = top => {
classRect.fixed = {
@ -185,7 +185,7 @@ describe('Affix Render', () => {
.find('ReactResizeObserver')
.at(index)
.instance()
.onResize();
.onResize([{ target: { getBoundingClientRect: () => ({ width: 99, height: 99 }) } }]);
jest.runAllTimers();
expect(updateCalled).toHaveBeenCalled();

View File

@ -1,5 +1,5 @@
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import Affix from './';
import Affix from '.';
export type BindElement = HTMLElement | Window | null | undefined;
export type Rect = ClientRect | DOMRect;

View File

@ -3,10 +3,10 @@ import * as ReactDOM from 'react-dom';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import raf from 'raf';
import Affix from '../affix';
import AnchorLink from './AnchorLink';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import scrollTo from '../_util/scrollTo';
import getScroll from '../_util/getScroll';
function getDefaultContainer() {
@ -35,52 +35,7 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number
return rect.top;
}
function easeInOutCubic(t: number, b: number, c: number, d: number) {
const cc = c - b;
t /= d / 2;
if (t < 1) {
return (cc / 2) * t * t * t + b;
}
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
}
const sharpMatcherRegx = /#([^#]+)$/;
function scrollTo(
href: string,
offsetTop = 0,
getContainer: () => AnchorContainer,
callback = () => {},
) {
const container = getContainer();
const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(href);
if (!sharpLinkMatch) {
return;
}
const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
}
const eleOffsetTop = getOffsetTop(targetElement, container);
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop);
} else {
(container as HTMLElement).scrollTop = nextScrollTop;
}
if (time < 450) {
raf(frameFunc);
} else {
callback();
}
};
raf(frameFunc);
}
type Section = {
link: string;
@ -99,10 +54,14 @@ export interface AnchorProps {
affix?: boolean;
showInkInFixed?: boolean;
getContainer?: () => AnchorContainer;
/** Return customize highlight anchor */
getCurrentAnchor?: () => string;
onClick?: (
e: React.MouseEvent<HTMLElement>,
link: { title: React.ReactNode; href: string },
) => void;
/** Scroll to target offset value, if none, it's offsetTop prop value or 0. */
targetOffset?: number;
}
export interface AnchorState {
@ -205,6 +164,12 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
}
getCurrentAnchor(offsetTop = 0, bounds = 5): string {
const { getCurrentAnchor } = this.props;
if (typeof getCurrentAnchor === 'function') {
return getCurrentAnchor();
}
const activeLink = '';
if (typeof document === 'undefined') {
return activeLink;
@ -237,6 +202,34 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
return '';
}
handleScrollTo = (link: string) => {
const { offsetTop, getContainer, targetOffset } = this.props as AnchorDefaultProps;
this.setState({ activeLink: link });
const container = getContainer();
const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(link);
if (!sharpLinkMatch) {
return;
}
const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) {
return;
}
const eleOffsetTop = getOffsetTop(targetElement, container);
let y = scrollTop + eleOffsetTop;
y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
this.animating = true;
scrollTo(y, {
callback: () => {
this.animating = false;
},
getContainer,
});
};
saveInkNode = (node: HTMLSpanElement) => {
this.inkNode = node;
};
@ -246,8 +239,11 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
return;
}
const { activeLink } = this.state;
const { offsetTop, bounds } = this.props;
const currentActiveLink = this.getCurrentAnchor(offsetTop, bounds);
const { offsetTop, bounds, targetOffset } = this.props;
const currentActiveLink = this.getCurrentAnchor(
targetOffset !== undefined ? targetOffset : offsetTop || 0,
bounds,
);
if (activeLink !== currentActiveLink) {
this.setState({
activeLink: currentActiveLink,
@ -255,15 +251,6 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
}
};
handleScrollTo = (link: string) => {
const { offsetTop, getContainer } = this.props as AnchorDefaultProps;
this.animating = true;
this.setState({ activeLink: link });
scrollTo(link, offsetTop, getContainer, () => {
this.animating = false;
});
};
updateInk = () => {
if (typeof document === 'undefined') {
return;

View File

@ -1,12 +1,29 @@
import React from 'react';
import { mount } from 'enzyme';
import Anchor from '..';
import { spyElementPrototypes } from '../../__tests__/util/domHook';
import { sleep } from '../../../tests/utils';
const { Link } = Anchor;
const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));
describe('Anchor Render', () => {
const getBoundingClientRectMock = jest.fn(() => ({
width: 100,
height: 100,
top: 1000,
}));
const getClientRectsMock = jest.fn(() => ({
length: 1,
}));
const headingSpy = spyElementPrototypes(HTMLHeadingElement, {
getBoundingClientRect: getBoundingClientRectMock,
getClientRects: getClientRectsMock,
});
afterAll(() => {
headingSpy.mockRestore();
});
it('Anchor render perfectly', () => {
const wrapper = mount(
<Anchor>
@ -64,7 +81,7 @@ describe('Anchor Render', () => {
wrapper.instance().handleScrollTo('##API');
expect(wrapper.instance().state.activeLink).toBe('##API');
expect(scrollToSpy).not.toHaveBeenCalled();
await delay(1000);
await sleep(1000);
expect(scrollToSpy).toHaveBeenCalled();
});
@ -154,7 +171,7 @@ describe('Anchor Render', () => {
</Anchor>,
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
await delay(1000);
await sleep(1000);
wrapper.setProps({ getContainer: getContainerB });
expect(removeListenerSpy).not.toHaveBeenCalled();
});
@ -187,7 +204,7 @@ describe('Anchor Render', () => {
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await delay(1000);
await sleep(1000);
wrapper.setProps({ getContainer: getContainerB });
expect(removeListenerSpy).toHaveBeenCalled();
});
@ -239,9 +256,67 @@ describe('Anchor Render', () => {
);
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await delay(1000);
await sleep(1000);
holdContainer.container = document.getElementById('API2');
wrapper.setProps({ 'data-only-trigger-re-render': true });
expect(removeListenerSpy).toHaveBeenCalled();
});
it('Anchor getCurrentAnchor prop', () => {
const getCurrentAnchor = () => '#API2';
const wrapper = mount(
<Anchor getCurrentAnchor={getCurrentAnchor}>
<Link href="#API1" title="API1" />
<Link href="#API2" title="API2" />
</Anchor>,
);
expect(wrapper.instance().state.activeLink).toBe('#API2');
});
it('Anchor targetOffset prop', () => {
jest.useFakeTimers();
let dateNowMock;
function dataNowMockFn() {
return jest
.spyOn(Date, 'now')
.mockImplementationOnce(() => 0)
.mockImplementationOnce(() => 1000);
}
dateNowMock = dataNowMockFn();
const scrollToSpy = jest.spyOn(window, 'scrollTo');
let root = document.getElementById('root');
if (!root) {
root = document.createElement('div', { id: 'root' });
root.id = 'root';
document.body.appendChild(root);
}
mount(<h1 id="API">Hello</h1>, { attachTo: root });
const wrapper = mount(
<Anchor>
<Link href="#API" title="API" />
</Anchor>,
);
wrapper.instance().handleScrollTo('#API');
jest.runAllTimers();
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 });
wrapper.instance().handleScrollTo('#API');
jest.runAllTimers();
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 });
wrapper.instance().handleScrollTo('#API');
jest.runAllTimers();
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
dateNowMock.mockRestore();
jest.useRealTimers();
});
});

View File

@ -80,6 +80,80 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
</div>
`;
exports[`renders ./components/anchor/demo/customizeHighlight.md correctly 1`] = `
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor fixed"
>
<div
class="ant-anchor-ink"
>
<span
class="ant-anchor-ink-ball"
/>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-basic"
title="Basic demo"
>
Basic demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-static"
title="Static demo"
>
Static demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#API"
title="API"
>
API
</a>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#Anchor-Props"
title="Anchor Props"
>
Anchor Props
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#Link-Props"
title="Link Props"
>
Link Props
</a>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/anchor/demo/onClick.md correctly 1`] = `
<div
class="ant-anchor-wrapper"
@ -227,3 +301,83 @@ exports[`renders ./components/anchor/demo/static.md correctly 1`] = `
</div>
</div>
`;
exports[`renders ./components/anchor/demo/targetOffset.md correctly 1`] = `
<div>
<div
class=""
>
<div
class="ant-anchor-wrapper"
style="max-height:100vh"
>
<div
class="ant-anchor"
>
<div
class="ant-anchor-ink"
>
<span
class="ant-anchor-ink-ball"
/>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-basic"
title="Basic demo"
>
Basic demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#components-anchor-demo-static"
title="Static demo"
>
Static demo
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#API"
title="API"
>
API
</a>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#Anchor-Props"
title="Anchor Props"
>
Anchor Props
</a>
</div>
<div
class="ant-anchor-link"
>
<a
class="ant-anchor-link-title"
href="#Link-Props"
title="Link Props"
>
Link Props
</a>
</div>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,36 @@
---
order: 4
title:
zh-CN: 自定义锚点高亮
en-US: Customize the anchor highlight
---
## zh-CN
自定义锚点高亮。
## en-US
Customize the anchor highlight.
```jsx
import { Anchor } from 'antd';
const { Link } = Anchor;
const getCurrentAnchor = () => {
return '#components-anchor-demo-static';
};
ReactDOM.render(
<Anchor affix={false} getCurrentAnchor={getCurrentAnchor}>
<Link href="#components-anchor-demo-basic" title="Basic demo" />
<Link href="#components-anchor-demo-static" title="Static demo" />
<Link href="#API" title="API">
<Link href="#Anchor-Props" title="Anchor Props" />
<Link href="#Link-Props" title="Link Props" />
</Link>
</Anchor>,
mountNode,
);
```

View File

@ -0,0 +1,47 @@
---
order: 4
title:
zh-CN: 设置锚点滚动偏移量
en-US: Set Anchor scroll offset
---
## zh-CN
锚点目标滚动到屏幕正中间。
## en-US
Anchor target scroll to screen center.
```jsx
import { Anchor } from 'antd';
const { Link } = Anchor;
class AnchorExample extends React.Component {
state = {
targetOffset: undefined,
};
componentDidMount() {
this.setState({
targetOffset: window.innerHeight / 2,
});
}
render() {
return (
<Anchor targetOffset={this.state.targetOffset}>
<Link href="#components-anchor-demo-basic" title="Basic demo" />
<Link href="#components-anchor-demo-static" title="Static demo" />
<Link href="#API" title="API">
<Link href="#Anchor-Props" title="Anchor Props" />
<Link href="#Link-Props" title="Link Props" />
</Link>
</Anchor>
);
}
}
ReactDOM.render(<AnchorExample />, mountNode);
```

View File

@ -24,6 +24,8 @@ For displaying anchor hyperlinks on page and jumping between them.
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 | |
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false | |
| onClick | set the handler to handle `click` event | Function(e: Event, link: Object) | - | 3.9.0 |
| getCurrentAnchor | Customize the anchor highlight | () => string | - | 3.22.0 |
| targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetOffset) | number | `offsetTop` | 3.22.0 |
### Link Props

View File

@ -25,6 +25,8 @@ title: Anchor
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | |
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false | |
| onClick | `click` 事件的 handler | Function(e: Event, link: Object) | - | 3.9.0 |
| getCurrentAnchor | 自定义高亮的锚点 | () => string | - | 3.22.0 |
| targetOffset | 锚点滚动偏移量,默认与 offsetTop 相同,[例子](#components-anchor-demo-targetOffset) | number | `offsetTop` | 3.22.0 |
### Link Props

View File

@ -27,7 +27,7 @@
width: @anchor-border-width;
height: 100%;
margin: 0 auto;
background-color: @border-color-split;
background-color: @anchor-border-color;
content: ' ';
}
&-ball {

View File

@ -1,76 +1,152 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
<div
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px"
>
<div>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox"
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px"
>
<div
class="ant-select-selection__rendered"
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox"
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
class="ant-select-selection__rendered"
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
unselectable="on"
>
input here
</div>
<ul>
<li
class="ant-select-search ant-select-search--inline"
>
<div
class="ant-select-search__field__wrap"
>
<input
class="ant-input ant-select-search__field"
type="text"
value=""
/>
<span
class="ant-select-search__field__mirror"
>
 
</span>
</div>
</li>
</ul>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
input here
</div>
<ul>
<li
class="ant-select-search ant-select-search--inline"
<span
aria-label="down"
class="anticon anticon-down ant-select-arrow-icon"
role="img"
>
<div
class="ant-select-search__field__wrap"
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<input
class="ant-input ant-select-search__field"
type="text"
value=""
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
<span
class="ant-select-search__field__mirror"
>
 
</span>
</div>
</li>
</ul>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-arrow-icon"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</svg>
</span>
</span>
</span>
</div>
</div>
<br />
<br />
<div
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px"
>
<div
aria-autocomplete="list"
aria-controls=""
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox"
>
<div
class="ant-select-selection__rendered"
>
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none"
unselectable="on"
>
control mode
</div>
<ul>
<li
class="ant-select-search ant-select-search--inline"
>
<div
class="ant-select-search__field__wrap"
>
<input
class="ant-input ant-select-search__field"
type="text"
value=""
/>
<span
class="ant-select-search__field__mirror"
>
 
</span>
</div>
</li>
</ul>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-select-arrow-icon"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
`;

View File

@ -1,8 +1,11 @@
import React from 'react';
import { mount } from 'enzyme';
import AutoComplete from '..';
import mountTest from '../../../tests/shared/mountTest';
describe('AutoComplete with Custom Input Element Render', () => {
mountTest(AutoComplete);
it('AutoComplete with custom Input render perfectly', () => {
const wrapper = mount(
<AutoComplete dataSource={['12345', '23456', '34567']}>

View File

@ -11,7 +11,7 @@ title:
## en-US
Basic Usage, set datasource of autocomplete with `dataSource` property.
Basic Usage, set data source of autocomplete with `dataSource` property.
```jsx
import { AutoComplete } from 'antd';
@ -22,25 +22,43 @@ function onSelect(value) {
class Complete extends React.Component {
state = {
value: '',
dataSource: [],
};
handleSearch = value => {
onSearch = searchText => {
this.setState({
dataSource: !value ? [] : [value, value + value, value + value + value],
dataSource: !searchText ? [] : [searchText, searchText.repeat(2), searchText.repeat(3)],
});
};
onChange = value => {
this.setState({ value });
};
render() {
const { dataSource } = this.state;
const { dataSource, value } = this.state;
return (
<AutoComplete
dataSource={dataSource}
style={{ width: 200 }}
onSelect={onSelect}
onSearch={this.handleSearch}
placeholder="input here"
/>
<div>
<AutoComplete
dataSource={dataSource}
style={{ width: 200 }}
onSelect={onSelect}
onSearch={this.onSearch}
placeholder="input here"
/>
<br />
<br />
<AutoComplete
value={value}
dataSource={dataSource}
style={{ width: 200 }}
onSelect={onSelect}
onSearch={this.onSearch}
onChange={this.onChange}
placeholder="control mode"
/>
</div>
);
}
}

View File

@ -48,3 +48,11 @@ const dataSource = ['12345', '23456', '34567'];
| ------- | ------------ | ------- |
| blur() | remove focus | |
| focus() | get focus | |
## FAQ
### Why text composition system not works well with onSearch in controlled mode?
Please use `onChange` to manage control state. `onSearch` is used for searching input which is not same as `onChange`. Besides, click on the option also not trigger the `onSearch` event.
Related issue: [#18230](https://github.com/ant-design/ant-design/issues/18230) [#17916](https://github.com/ant-design/ant-design/issues/17916)

View File

@ -50,3 +50,11 @@ const dataSource = ['12345', '23456', '34567'];
| ------- | -------- | ---- |
| blur() | 移除焦点 | |
| focus() | 获取焦点 | |
## FAQ
### 为何受控状态下使用 onSearch 无法输入中文?
请使用 `onChange` 进行受控管理。`onSearch` 触发于搜索输入,与 `onChange` 时机不同。此外,点选选项时也不会触发 `onSearch` 事件。
相关 issue[#18230](https://github.com/ant-design/ant-design/issues/18230) [#17916](https://github.com/ant-design/ant-design/issues/17916)

View File

@ -1,8 +1,11 @@
import React from 'react';
import { mount } from 'enzyme';
import Avatar from '..';
import mountTest from '../../../tests/shared/mountTest';
describe('Avatar Render', () => {
mountTest(Avatar);
let originOffsetWidth;
beforeAll(() => {
// Mock offsetHeight

View File

@ -1,16 +1,41 @@
import React from 'react';
import { mount } from 'enzyme';
import { sleep } from '../../../tests/utils';
import mountTest from '../../../tests/shared/mountTest';
import BackTop from '..';
describe('BackTop', () => {
mountTest(BackTop);
it('should scroll to top after click it', async () => {
const wrapper = mount(<BackTop visibilityHeight={-1} />);
document.documentElement.scrollTop = 400;
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.scrollY = y;
window.pageYOffset = y;
});
window.scrollTo(0, 400);
// trigger scroll manually
wrapper.instance().handleScroll();
await new Promise(resolve => setTimeout(resolve, 0));
await sleep();
wrapper.find('.ant-back-top').simulate('click');
await new Promise(resolve => setTimeout(resolve, 1000));
expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
await sleep(500);
expect(window.pageYOffset).toBe(0);
scrollToSpy.mockRestore();
});
it('support onClick', async () => {
const onClick = jest.fn();
const wrapper = mount(<BackTop onClick={onClick} visibilityHeight={-1} />);
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.scrollY = y;
window.pageYOffset = y;
});
window.scrollTo(0, 400);
// trigger scroll manually
wrapper.instance().handleScroll();
await sleep();
wrapper.find('.ant-back-top').simulate('click');
expect(onClick).toHaveBeenCalled();
scrollToSpy.mockRestore();
});
});

View File

@ -3,20 +3,9 @@ import Animate from 'rc-animate';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import classNames from 'classnames';
import omit from 'omit.js';
import raf from 'raf';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getScroll from '../_util/getScroll';
const easeInOutCubic = (t: number, b: number, c: number, d: number) => {
const cc = c - b;
t /= d / 2;
if (t < 1) {
return (cc / 2) * t * t * t + b;
}
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
};
function noop() {}
import scrollTo from '../_util/scrollTo';
function getDefaultTarget() {
return window;
@ -58,41 +47,14 @@ export default class BackTop extends React.Component<BackTopProps, any> {
}
}
setScrollTop(value: number) {
const getTarget = this.props.target || getDefaultTarget;
const targetNode = getTarget();
if (targetNode === window) {
document.body.scrollTop = value;
document.documentElement!.scrollTop = value;
} else {
(targetNode as HTMLElement).scrollTop = value;
}
}
getCurrentScrollTop = () => {
const getTarget = this.props.target || getDefaultTarget;
const targetNode = getTarget();
if (targetNode === window) {
return window.pageYOffset || document.body.scrollTop || document.documentElement!.scrollTop;
}
return (targetNode as HTMLElement).scrollTop;
};
scrollToTop = (e: React.MouseEvent<HTMLDivElement>) => {
const scrollTop = this.getCurrentScrollTop();
const startTime = Date.now();
const frameFunc = () => {
const timestamp = Date.now();
const time = timestamp - startTime;
this.setScrollTop(easeInOutCubic(time, scrollTop, 0, 450));
if (time < 450) {
raf(frameFunc);
} else {
this.setScrollTop(0);
}
};
raf(frameFunc);
(this.props.onClick || noop)(e);
const { target = getDefaultTarget, onClick } = this.props;
scrollTo(0, {
getContainer: target,
});
if (typeof onClick === 'function') {
onClick(e);
}
};
handleScroll = () => {

View File

@ -1,4 +1,4 @@
import React, { createElement, Component } from 'react';
import * as React from 'react';
import omit from 'omit.js';
import classNames from 'classnames';
import { polyfill } from 'react-lifecycles-compat';
@ -47,7 +47,7 @@ export interface ScrollNumberState {
count?: string | number | null;
}
class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
class ScrollNumber extends React.Component<ScrollNumberProps, ScrollNumberState> {
static defaultProps = {
count: null,
onAnimated() {},
@ -126,7 +126,7 @@ class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
const position = this.getPositionByNum(num, i);
const removeTransition =
this.state.animateStarted || getNumberArray(this.lastCount)[i] === undefined;
return createElement(
return React.createElement(
'span',
{
className: `${prefixCls}-only`,
@ -200,7 +200,7 @@ class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
),
});
}
return createElement(component as any, newProps, this.renderNumberElement(prefixCls));
return React.createElement(component as any, newProps, this.renderNumberElement(prefixCls));
};
render() {

View File

@ -2907,7 +2907,9 @@ exports[`Badge should support offset when count is a ReactNode 1`] = `
<a
class="head-example"
href="#"
/>
>
head
</a>
<span
class="ant-scroll-number-custom-component custom"
style="right:-10px;margin-top:20px;color:#f5222d"

View File

@ -2,8 +2,11 @@ import React from 'react';
import { mount, render } from 'enzyme';
import Badge from '../index';
import Tooltip from '../../tooltip';
import mountTest from '../../../tests/shared/mountTest';
describe('Badge', () => {
mountTest(Badge);
beforeEach(() => {
jest.useFakeTimers();
});
@ -84,7 +87,9 @@ describe('Badge', () => {
it('should support offset when count is a ReactNode', () => {
const wrapper = render(
<Badge count={<span className="custom" style={{ color: '#f5222d' }} />} offset={[10, 20]}>
<a href="#" className="head-example" />
<a href="#" className="head-example">
head
</a>
</Badge>,
);
expect(wrapper).toMatchSnapshot();

View File

@ -1,4 +1,4 @@
import React, { cloneElement } from 'react';
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import BreadcrumbItem from './BreadcrumbItem';
@ -141,9 +141,9 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
"Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children",
);
return cloneElement(element, {
return React.cloneElement(element, {
separator,
key: index,
key: index, // eslint-disable-line react/no-array-index-key
});
});
}

View File

@ -1,8 +1,11 @@
import React from 'react';
import { mount, render } from 'enzyme';
import Breadcrumb from '../index';
import mountTest from '../../../tests/shared/mountTest';
describe('Breadcrumb', () => {
mountTest(Breadcrumb);
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
afterEach(() => {

View File

@ -23,6 +23,9 @@
& > span:last-child {
color: @breadcrumb-last-item-color;
a {
color: @breadcrumb-last-item-color;
}
}
& > span:last-child &-separator {

View File

@ -253,6 +253,22 @@ exports[`renders ./components/button/demo/button-group.md correctly 1`] = `
cloud-download
</button>
</div>
<div
class="ant-btn-group"
>
<button
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
type="button"
>
cloud
</button>
<button
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
type="button"
>
cloud-download
</button>
</div>
</div>
`;
@ -427,6 +443,14 @@ exports[`renders ./components/button/demo/icon.md correctly 1`] = `
</svg>
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-circle"
type="button"
>
<span>
A
</span>
</button>
<button
class="ant-btn ant-btn-primary"
type="button"
@ -696,7 +720,7 @@ exports[`renders ./components/button/demo/loading.md correctly 1`] = `
</button>
<br />
<button
class="ant-btn ant-btn-circle ant-btn-loading"
class="ant-btn ant-btn-primary ant-btn-icon-only ant-btn-loading"
type="button"
>
<span
@ -721,7 +745,32 @@ exports[`renders ./components/button/demo/loading.md correctly 1`] = `
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-circle ant-btn-loading"
class="ant-btn ant-btn-primary ant-btn-circle ant-btn-icon-only ant-btn-loading"
type="button"
>
<span
aria-label="loading"
class="anticon anticon-loading"
role="img"
>
<svg
aria-hidden="true"
class="anticon-spin"
data-icon="loading"
fill="currentColor"
focusable="false"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"
/>
</svg>
</span>
</button>
<button
class="ant-btn ant-btn-danger ant-btn-round ant-btn-icon-only ant-btn-loading"
type="button"
>
<span
@ -904,6 +953,31 @@ exports[`renders ./components/button/demo/size.md correctly 1`] = `
</span>
</button>
<br />
<button
class="ant-btn ant-btn-primary ant-btn-lg ant-btn-icon-only"
type="button"
>
<span
aria-label="download"
class="anticon anticon-download"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-circle ant-btn-lg ant-btn-icon-only"
type="button"
@ -929,6 +1003,31 @@ exports[`renders ./components/button/demo/size.md correctly 1`] = `
</svg>
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-round ant-btn-lg ant-btn-icon-only"
type="button"
>
<span
aria-label="download"
class="anticon anticon-download"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="download"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"
/>
</svg>
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-round ant-btn-lg"
type="button"

View File

@ -3,8 +3,12 @@ import { render, mount } from 'enzyme';
import renderer from 'react-test-renderer';
import { Search } from '@ant-design/icons';
import Button from '..';
import mountTest from '../../../tests/shared/mountTest';
describe('Button', () => {
mountTest(Button);
mountTest(Button.Group);
it('renders correctly', () => {
const wrapper = render(<Button>Follow</Button>);
expect(wrapper).toMatchSnapshot();

View File

@ -254,11 +254,13 @@ class Button extends React.Component<ButtonProps, ButtonState> {
break;
}
const iconType = loading ? 'loading' : icon;
const classes = classNames(prefixCls, className, {
[`${prefixCls}-${type}`]: type,
[`${prefixCls}-${shape}`]: shape,
[`${prefixCls}-${sizeCls}`]: sizeCls,
[`${prefixCls}-icon-only`]: !children && children !== 0 && icon,
[`${prefixCls}-icon-only`]: !children && children !== 0 && iconType,
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-background-ghost`]: ghost,
[`${prefixCls}-two-chinese-chars`]: hasTwoCNChar && autoInsertSpace,

View File

@ -56,6 +56,10 @@ ReactDOM.render(
<Button type="primary" icon="cloud" />
<Button type="primary" icon="cloud-download" />
</ButtonGroup>
<ButtonGroup>
<Button type="primary" size="small" icon="cloud" />
<Button type="primary" size="small" icon="cloud-download" />
</ButtonGroup>
</div>,
mountNode,
);

View File

@ -13,7 +13,7 @@ title:
## en-US
`Button` components can contain an `Icon`. This is done by setting the `icon` property or placing an `Icon` component within the `Button`
`Button` components can contain an `Icon`. This is done by setting the `icon` property or placing an `Icon` component within the `Button`.
If you want specific control over the positioning and placement of the `Icon`, then that should be done by placing the `Icon` component within the `Button` rather than using the `icon` property.
@ -24,6 +24,9 @@ import { Search as IconSearch } from '@ant-design/icons';
ReactDOM.render(
<div>
<Button type="primary" shape="circle" icon={<IconSearch />} />
<Button type="primary" shape="circle">
A
</Button>
<Button type="primary" icon={<IconSearch />}>
Search
</Button>

View File

@ -52,8 +52,9 @@ class App extends React.Component {
Click me!
</Button>
<br />
<Button shape="circle" loading />
<Button type="primary" loading />
<Button type="primary" shape="circle" loading />
<Button type="danger" shape="round" loading />
</div>
);
}

View File

@ -55,7 +55,9 @@ class ButtonSize extends React.Component {
Link
</Button>
<br />
<Button type="primary" icon={<Download />} size={size} />
<Button type="primary" shape="circle" icon={<Download />} size={size} />
<Button type="primary" shape="round" icon={<Download />} size={size} />
<Button type="primary" shape="round" icon={<Download />} size={size}>
Download
</Button>

View File

@ -71,8 +71,15 @@
.btn-link;
}
&-icon-only {
.btn-square(@btn-prefix-cls);
}
&-round {
.btn-round(@btn-prefix-cls);
&.@{btn-prefix-cls}-icon-only {
width: auto;
}
}
&-circle,
@ -136,11 +143,6 @@
.btn-group(@btn-prefix-cls);
}
&:not(&-circle):not(&-circle-outline)&-icon-only {
padding-right: 8px;
padding-left: 8px;
}
// http://stackoverflow.com/a/21281554/3040605
&:focus > span,
&:active > span {

View File

@ -237,17 +237,33 @@
);
}
}
// circle button: the content only contains icon
.btn-circle(@btnClassName: btn) {
.square(@btn-circle-size);
.button-size(@btn-circle-size; 0; @font-size-base + 2px; 50%);
// square button: the content only contains icon
.btn-square(@btnClassName: btn) {
.square(@btn-square-size);
.button-size(@btn-square-size; 0; @font-size-base + 2px; @btn-border-radius-base);
&.@{btnClassName}-lg {
.square(@btn-circle-size-lg);
.button-size(@btn-circle-size-lg; 0; @btn-font-size-lg + 2px; 50%);
.square(@btn-square-size-lg);
.button-size(@btn-square-size-lg; 0; @btn-font-size-lg + 2px; @btn-border-radius-base);
}
&.@{btnClassName}-sm {
.square(@btn-circle-size-sm);
.button-size(@btn-circle-size-sm; 0; @font-size-base; 50%);
.square(@btn-square-size-sm);
.button-size(@btn-square-size-sm; 0; @font-size-base; @btn-border-radius-base);
}
}
// circle button: the content only contains icon
.btn-circle(@btnClassName: btn) {
min-width: @btn-height-base;
padding-right: 0;
padding-left: 0;
text-align: center;
border-radius: 50%;
&.@{btnClassName}-lg {
min-width: @btn-height-lg;
border-radius: 50%;
}
&.@{btnClassName}-sm {
min-width: @btn-height-sm;
border-radius: 50%;
}
}
// Horizontal button groups style

View File

@ -49,7 +49,7 @@ export default class Header extends React.Component<HeaderProps, any> {
private calenderHeaderNode: HTMLDivElement;
getYearSelectElement(prefixCls: string, year: number) {
const { yearSelectOffset, yearSelectTotal, locale, fullscreen, validRange } = this.props;
const { yearSelectOffset, yearSelectTotal, locale = {}, fullscreen, validRange } = this.props;
let start = year - (yearSelectOffset as number);
let end = start + (yearSelectTotal as number);
if (validRange) {
@ -171,7 +171,7 @@ export default class Header extends React.Component<HeaderProps, any> {
};
getTypeSwitch = () => {
const { locale, type, fullscreen } = this.props;
const { locale = {}, type, fullscreen } = this.props;
const size = fullscreen ? 'default' : 'small';
return (
<Group onChange={this.onInternalTypeChange} value={type} size={size}>

View File

@ -7,8 +7,12 @@ import Header from '../Header';
import Select from '../../select';
import Group from '../../radio/group';
import Button from '../../radio/radioButton';
import mountTest from '../../../tests/shared/mountTest';
describe('Calendar', () => {
mountTest(Calendar);
mountTest(() => <Header value={Moment()} />);
it('Calendar should be selectable', () => {
const onSelect = jest.fn();
const wrapper = mount(<Calendar onSelect={onSelect} />);

View File

@ -1,2 +1,3 @@
import ar_EG from '../../date-picker/locale/ar_EG';
export default ar_EG;

View File

@ -1,2 +1,3 @@
import bg_BG from '../../date-picker/locale/bg_BG';
export default bg_BG;

View File

@ -1,2 +1,3 @@
import ca_ES from '../../date-picker/locale/ca_ES';
export default ca_ES;

View File

@ -1,2 +1,3 @@
import cs_CZ from '../../date-picker/locale/cs_CZ';
export default cs_CZ;

View File

@ -1,2 +1,3 @@
import da_DK from '../../date-picker/locale/da_DK';
export default da_DK;

View File

@ -1,2 +1,3 @@
import de_DE from '../../date-picker/locale/de_DE';
export default de_DE;

View File

@ -1,2 +1,3 @@
import el_GR from '../../date-picker/locale/el_GR';
export default el_GR;

View File

@ -1,2 +1,3 @@
import en_GB from '../../date-picker/locale/en_GB';
export default en_GB;

View File

@ -1,2 +1,3 @@
import en_US from '../../date-picker/locale/en_US';
export default en_US;

View File

@ -1,2 +1,3 @@
import es_ES from '../../date-picker/locale/es_ES';
export default es_ES;

View File

@ -1,2 +1,3 @@
import et_EE from '../../date-picker/locale/et_EE';
export default et_EE;

View File

@ -1,2 +1,3 @@
import fa_IR from '../../date-picker/locale/fa_IR';
export default fa_IR;

View File

@ -1,2 +1,3 @@
import fi_FI from '../../date-picker/locale/fi_FI';
export default fi_FI;

View File

@ -1,2 +1,3 @@
import fr_BE from '../../date-picker/locale/fr_BE';
export default fr_BE;

View File

@ -1,2 +1,3 @@
import fr_FR from '../../date-picker/locale/fr_FR';
export default fr_FR;

View File

@ -1,2 +1,3 @@
import he_IL from '../../date-picker/locale/he_IL';
export default he_IL;

View File

@ -1,2 +1,3 @@
import hi_IN from '../../date-picker/locale/hi_IN';
export default hi_IN;

View File

@ -1,2 +1,3 @@
import hr_HR from '../../date-picker/locale/hr_HR';
export default hr_HR;

View File

@ -1,2 +1,3 @@
import hu_HU from '../../date-picker/locale/hu_HU';
export default hu_HU;

View File

@ -1,2 +1,3 @@
import id_ID from '../../date-picker/locale/id_ID';
export default id_ID;

View File

@ -1,2 +1,3 @@
import is_IS from '../../date-picker/locale/is_IS';
export default is_IS;

View File

@ -1,2 +1,3 @@
import it_IT from '../../date-picker/locale/it_IT';
export default it_IT;

View File

@ -1,2 +1,3 @@
import ja_JP from '../../date-picker/locale/ja_JP';
export default ja_JP;

View File

@ -1,2 +1,3 @@
import kn_IN from '../../date-picker/locale/kn_IN';
export default kn_IN;

View File

@ -1,2 +1,3 @@
import ko_KR from '../../date-picker/locale/ko_KR';
export default ko_KR;

View File

@ -1,2 +1,3 @@
import ku_IQ from '../../date-picker/locale/ku_IQ';
export default ku_IQ;

View File

@ -1,2 +1,3 @@
import lv_LV from '../../date-picker/locale/lv_LV';
export default lv_LV;

View File

@ -1,2 +1,3 @@
import mn_MN from '../../date-picker/locale/mn_MN';
export default mn_MN;

View File

@ -1,2 +1,3 @@
import ms_MY from '../../date-picker/locale/ms_MY';
export default ms_MY;

View File

@ -1,2 +1,3 @@
import nb_NO from '../../date-picker/locale/nb_NO';
export default nb_NO;

View File

@ -1,2 +1,3 @@
import nl_BE from '../../date-picker/locale/nl_BE';
export default nl_BE;

View File

@ -1,2 +1,3 @@
import nl_NL from '../../date-picker/locale/nl_NL';
export default nl_NL;

View File

@ -1,2 +1,3 @@
import pl_PL from '../../date-picker/locale/pl_PL';
export default pl_PL;

View File

@ -1,2 +1,3 @@
import pt_BR from '../../date-picker/locale/pt_BR';
export default pt_BR;

View File

@ -1,2 +1,3 @@
import pt_PT from '../../date-picker/locale/pt_PT';
export default pt_PT;

View File

@ -0,0 +1,3 @@
import ro_RO from '../../date-picker/locale/ro_RO';
export default ro_RO;

View File

@ -1,2 +1,3 @@
import ru_RU from '../../date-picker/locale/ru_RU';
export default ru_RU;

View File

@ -1,2 +1,3 @@
import sk_SK from '../../date-picker/locale/sk_SK';
export default sk_SK;

View File

@ -1,2 +1,3 @@
import sl_SI from '../../date-picker/locale/sl_SI';
export default sl_SI;

View File

@ -1,2 +1,3 @@
import sr_RS from '../../date-picker/locale/sr_RS';
export default sr_RS;

View File

@ -1,2 +1,3 @@
import sv_SE from '../../date-picker/locale/sv_SE';
export default sv_SE;

View File

@ -1,2 +1,3 @@
import ta_IN from '../../date-picker/locale/ta_IN';
export default ta_IN;

Some files were not shown because too many files have changed in this diff Show More