mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
Merge branch 'master' into next-merge-master
This commit is contained in:
commit
4490d9dbdc
6
.github/workflows/ui-upload.yml
vendored
6
.github/workflows/ui-upload.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download commit artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
uses: dawidd6/action-download-artifact@v2.23.0
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
name: commit
|
||||
@ -35,7 +35,7 @@ jobs:
|
||||
run: echo "::set-output name=id::$(<commit.txt)"
|
||||
|
||||
- name: Download branch artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
uses: dawidd6/action-download-artifact@v2.23.0
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
name: branch
|
||||
@ -45,7 +45,7 @@ jobs:
|
||||
run: echo "::set-output name=id::$(<branch.txt)"
|
||||
|
||||
- name: Download snapshots artifact
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
uses: dawidd6/action-download-artifact@v2.23.0
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
workflow_conclusion: success
|
||||
|
@ -1,9 +1,13 @@
|
||||
import { sleep } from '../../../tests/utils';
|
||||
import { waitFakeTimer } from '../../../tests/utils';
|
||||
import scrollTo from '../scrollTo';
|
||||
|
||||
describe('Test ScrollTo function', () => {
|
||||
let dateNowMock: jest.SpyInstance;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
dateNowMock = jest
|
||||
.spyOn(Date, 'now')
|
||||
@ -11,7 +15,12 @@ describe('Test ScrollTo function', () => {
|
||||
.mockImplementationOnce(() => 1000);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
dateNowMock.mockRestore();
|
||||
});
|
||||
|
||||
@ -22,7 +31,7 @@ describe('Test ScrollTo function', () => {
|
||||
});
|
||||
|
||||
scrollTo(1000);
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(window.pageYOffset).toBe(1000);
|
||||
|
||||
@ -34,7 +43,7 @@ describe('Test ScrollTo function', () => {
|
||||
scrollTo(1000, {
|
||||
callback: cbMock,
|
||||
});
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
expect(cbMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@ -43,7 +52,7 @@ describe('Test ScrollTo function', () => {
|
||||
scrollTo(1000, {
|
||||
getContainer: () => div,
|
||||
});
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
expect(div.scrollTop).toBe(1000);
|
||||
});
|
||||
|
||||
@ -51,7 +60,7 @@ describe('Test ScrollTo function', () => {
|
||||
scrollTo(1000, {
|
||||
getContainer: () => document,
|
||||
});
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
expect(document.documentElement.scrollTop).toBe(1000);
|
||||
});
|
||||
|
||||
@ -60,7 +69,7 @@ describe('Test ScrollTo function', () => {
|
||||
duration: 1100,
|
||||
getContainer: () => document,
|
||||
});
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
expect(document.documentElement.scrollTop).toBe(1000);
|
||||
});
|
||||
});
|
||||
|
@ -2,7 +2,7 @@
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import raf from 'rc-util/lib/raf';
|
||||
import React from 'react';
|
||||
import { sleep, render, fireEvent } from '../../../tests/utils';
|
||||
import { waitFakeTimer, render, fireEvent } from '../../../tests/utils';
|
||||
import getDataOrAriaProps from '../getDataOrAriaProps';
|
||||
import delayRaf from '../raf';
|
||||
import { isStyleSupport } from '../styleChecker';
|
||||
@ -14,6 +14,18 @@ import TransButton from '../transButton';
|
||||
|
||||
describe('Test utils function', () => {
|
||||
describe('throttle', () => {
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
});
|
||||
|
||||
it('throttle function should work', async () => {
|
||||
const callback = jest.fn();
|
||||
const throttled = throttleByAnimationFrame(callback);
|
||||
@ -21,7 +33,7 @@ describe('Test utils function', () => {
|
||||
|
||||
throttled();
|
||||
throttled();
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mock.calls.length).toBe(1);
|
||||
@ -33,7 +45,7 @@ describe('Test utils function', () => {
|
||||
|
||||
throttled();
|
||||
throttled.cancel();
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
@ -50,7 +62,7 @@ describe('Test utils function', () => {
|
||||
test.callback();
|
||||
test.callback();
|
||||
test.callback();
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(callbackFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
@ -1,17 +1,26 @@
|
||||
import React from 'react';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import { render, sleep, fireEvent, act } from '../../../tests/utils';
|
||||
import { render, waitFakeTimer, fireEvent, act } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import Wave from '../wave';
|
||||
|
||||
describe('Wave component', () => {
|
||||
mountTest(Wave);
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
const styles = document.getElementsByTagName('style');
|
||||
for (let i = 0; i < styles.length; i += 1) {
|
||||
styles[i].remove();
|
||||
@ -69,7 +78,7 @@ describe('Wave component', () => {
|
||||
</Wave>,
|
||||
);
|
||||
container.querySelector('button')?.click();
|
||||
await sleep(0);
|
||||
await waitFakeTimer();
|
||||
let styles: HTMLCollectionOf<HTMLStyleElement> | HTMLStyleElement[] = (
|
||||
container.querySelector('button')?.getRootNode() as HTMLButtonElement
|
||||
).getElementsByTagName('style');
|
||||
@ -87,7 +96,7 @@ describe('Wave component', () => {
|
||||
</Wave>,
|
||||
);
|
||||
container.querySelector('button')?.click();
|
||||
await sleep(200);
|
||||
await waitFakeTimer();
|
||||
let styles: HTMLCollectionOf<HTMLStyleElement> | HTMLStyleElement[] = (
|
||||
container.querySelector('button')?.getRootNode() as HTMLButtonElement
|
||||
).getElementsByTagName('style');
|
||||
@ -104,7 +113,7 @@ describe('Wave component', () => {
|
||||
</Wave>,
|
||||
);
|
||||
container.querySelector('div')?.click();
|
||||
await sleep(0);
|
||||
await waitFakeTimer();
|
||||
let styles: HTMLCollectionOf<HTMLStyleElement> | HTMLStyleElement[] = (
|
||||
container.querySelector('div')?.getRootNode() as HTMLDivElement
|
||||
).getElementsByTagName('style');
|
||||
@ -121,7 +130,7 @@ describe('Wave component', () => {
|
||||
</Wave>,
|
||||
);
|
||||
container.querySelector('div')?.click();
|
||||
await sleep(0);
|
||||
await waitFakeTimer();
|
||||
let styles: HTMLCollectionOf<HTMLStyleElement> | HTMLStyleElement[] = (
|
||||
container.querySelector('div')?.getRootNode() as HTMLDivElement
|
||||
).getElementsByTagName('style');
|
||||
@ -138,7 +147,7 @@ describe('Wave component', () => {
|
||||
</Wave>,
|
||||
);
|
||||
container.querySelector('div')?.click();
|
||||
await sleep(0);
|
||||
await waitFakeTimer();
|
||||
let styles: HTMLCollectionOf<HTMLStyleElement> | HTMLStyleElement[] = (
|
||||
container.querySelector('div')?.getRootNode() as HTMLDivElement
|
||||
).getElementsByTagName('style');
|
||||
@ -157,7 +166,7 @@ describe('Wave component', () => {
|
||||
</Wave>,
|
||||
);
|
||||
container.querySelector('button')?.click();
|
||||
await sleep(0);
|
||||
await waitFakeTimer();
|
||||
let styles: HTMLCollectionOf<HTMLStyleElement> | HTMLStyleElement[] = (
|
||||
container.querySelector('button')?.getRootNode() as HTMLButtonElement
|
||||
).getElementsByTagName('style');
|
||||
@ -175,7 +184,7 @@ describe('Wave component', () => {
|
||||
</ConfigProvider>,
|
||||
);
|
||||
container.querySelector('button')?.click();
|
||||
await sleep(0);
|
||||
await waitFakeTimer();
|
||||
let styles: HTMLCollectionOf<HTMLStyleElement> | HTMLStyleElement[] = (
|
||||
container.querySelector('button')?.getRootNode() as HTMLButtonElement
|
||||
).getElementsByTagName('style');
|
||||
@ -242,7 +251,7 @@ describe('Wave component', () => {
|
||||
</Wave>,
|
||||
);
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
await sleep(200);
|
||||
await waitFakeTimer();
|
||||
let styles = (container.querySelector('button')!.getRootNode() as any).getElementsByTagName(
|
||||
'style',
|
||||
);
|
||||
@ -261,7 +270,7 @@ describe('Wave component', () => {
|
||||
</Wave>,
|
||||
);
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
await sleep(200);
|
||||
await waitFakeTimer();
|
||||
let styles = (container.querySelector('button')!.getRootNode() as any).getElementsByTagName(
|
||||
'style',
|
||||
);
|
||||
|
@ -23,8 +23,8 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) {
|
||||
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
|
||||
if (isWindow(container)) {
|
||||
(container as Window).scrollTo(window.pageXOffset, nextScrollTop);
|
||||
} else if (container instanceof HTMLDocument || container.constructor.name === 'HTMLDocument') {
|
||||
(container as HTMLDocument).documentElement.scrollTop = nextScrollTop;
|
||||
} else if (container instanceof Document || container.constructor.name === 'HTMLDocument') {
|
||||
(container as Document).documentElement.scrollTop = nextScrollTop;
|
||||
} else {
|
||||
(container as HTMLElement).scrollTop = nextScrollTop;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import type { InternalAffixClass } from '..';
|
||||
import type { AffixProps, InternalAffixClass } from '..';
|
||||
import Affix from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { render, sleep, triggerResize, waitFakeTimer } from '../../../tests/utils';
|
||||
import { render, triggerResize, waitFakeTimer } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import { getObserverEntities } from '../utils';
|
||||
|
||||
@ -66,6 +66,7 @@ describe('Affix Render', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
const entities = getObserverEntities();
|
||||
entities.splice(0, entities.length);
|
||||
});
|
||||
@ -81,6 +82,11 @@ describe('Affix Render', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
jest.clearAllTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
domMock.mockRestore();
|
||||
});
|
||||
@ -96,12 +102,12 @@ describe('Affix Render', () => {
|
||||
events.scroll({
|
||||
type: 'scroll',
|
||||
});
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
};
|
||||
|
||||
it('Anchor render perfectly', async () => {
|
||||
const { container } = render(<AffixMounter />);
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
|
||||
await movePlaceholder(0);
|
||||
expect(container.querySelector('.ant-affix')).toBeFalsy();
|
||||
@ -113,10 +119,16 @@ describe('Affix Render', () => {
|
||||
expect(container.querySelector('.ant-affix')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('Anchor correct render when target is null', async () => {
|
||||
expect(() => {
|
||||
render(<Affix target={null as unknown as AffixProps['target']}>test</Affix>);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('support offsetBottom', async () => {
|
||||
const { container } = render(<AffixMounter offsetBottom={0} />);
|
||||
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
|
||||
await movePlaceholder(300);
|
||||
expect(container.querySelector('.ant-affix')).toBeTruthy();
|
||||
@ -132,14 +144,14 @@ describe('Affix Render', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const { container, rerender } = render(<AffixMounter offsetTop={0} onChange={onChange} />);
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
|
||||
await movePlaceholder(-100);
|
||||
expect(onChange).toHaveBeenLastCalledWith(true);
|
||||
expect(container.querySelector('.ant-affix')).toHaveStyle({ top: 0 });
|
||||
|
||||
rerender(<AffixMounter offsetTop={10} onChange={onChange} />);
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-affix')).toHaveStyle({ top: `10px` });
|
||||
});
|
||||
|
||||
@ -172,7 +184,6 @@ describe('Affix Render', () => {
|
||||
expect(affixInstance!.state.status).toBe(0);
|
||||
expect(affixInstance!.state.affixStyle).toBe(undefined);
|
||||
expect(affixInstance!.state.placeholderStyle).toBe(undefined);
|
||||
await sleep(100);
|
||||
});
|
||||
|
||||
it('instance change', async () => {
|
||||
@ -182,7 +193,7 @@ describe('Affix Render', () => {
|
||||
|
||||
const getTarget = () => target;
|
||||
const { rerender } = render(<Affix target={getTarget}>{null}</Affix>);
|
||||
await sleep(100);
|
||||
await waitFakeTimer();
|
||||
expect(getObserverEntities()).toHaveLength(1);
|
||||
expect(getObserverEntities()[0].target).toBe(container);
|
||||
|
||||
@ -210,7 +221,7 @@ describe('Affix Render', () => {
|
||||
},
|
||||
);
|
||||
|
||||
await sleep(20);
|
||||
await waitFakeTimer();
|
||||
await movePlaceholder(300);
|
||||
expect(affixInstance!.state.affixStyle).toBeTruthy();
|
||||
});
|
||||
@ -221,8 +232,6 @@ describe('Affix Render', () => {
|
||||
'.fixed', // outer
|
||||
].forEach(selector => {
|
||||
it(`trigger listener when size change: ${selector}`, async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const updateCalled = jest.fn();
|
||||
const { container } = render(
|
||||
<AffixMounter offsetBottom={0} onTestUpdatePosition={updateCalled} />,
|
||||
@ -237,9 +246,6 @@ describe('Affix Render', () => {
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(updateCalled).toHaveBeenCalled();
|
||||
|
||||
jest.clearAllTimers();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import Anchor from '..';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
|
||||
import type { InternalAnchorClass } from '../Anchor';
|
||||
|
||||
const { Link } = Anchor;
|
||||
@ -32,6 +32,7 @@ describe('Anchor Render', () => {
|
||||
const getClientRectsMock = jest.spyOn(HTMLHeadingElement.prototype, 'getClientRects');
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
getBoundingClientRectMock.mockReturnValue({
|
||||
width: 100,
|
||||
height: 100,
|
||||
@ -40,7 +41,12 @@ describe('Anchor Render', () => {
|
||||
getClientRectsMock.mockReturnValue({ length: 1 } as DOMRectList);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
getBoundingClientRectMock.mockRestore();
|
||||
getClientRectsMock.mockRestore();
|
||||
});
|
||||
@ -96,7 +102,7 @@ describe('Anchor Render', () => {
|
||||
anchorInstance!.handleScrollTo('/#/faq?locale=en#Q1');
|
||||
expect(anchorInstance!.state.activeLink).toBe('/#/faq?locale=en#Q1');
|
||||
expect(scrollToSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -137,7 +143,7 @@ describe('Anchor Render', () => {
|
||||
anchorInstance!.handleScrollTo(`##${hash}`);
|
||||
expect(anchorInstance!.state.activeLink).toBe(`##${hash}`);
|
||||
const calls = scrollToSpy.mock.calls.length;
|
||||
await sleep(1000);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy.mock.calls.length).toBeGreaterThan(calls);
|
||||
});
|
||||
|
||||
@ -250,7 +256,7 @@ describe('Anchor Render', () => {
|
||||
);
|
||||
|
||||
const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove');
|
||||
await sleep(1000);
|
||||
await waitFakeTimer();
|
||||
rerender(
|
||||
<Anchor getContainer={getContainerB}>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
@ -287,7 +293,7 @@ describe('Anchor Render', () => {
|
||||
|
||||
const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove');
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
await waitFakeTimer();
|
||||
rerender(
|
||||
<Anchor getContainer={getContainerB}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
@ -354,7 +360,7 @@ describe('Anchor Render', () => {
|
||||
);
|
||||
const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove');
|
||||
expect(removeListenerSpy).not.toHaveBeenCalled();
|
||||
await sleep(1000);
|
||||
await waitFakeTimer();
|
||||
holdContainer.container = document.getElementById(hash2);
|
||||
rerender(
|
||||
<Anchor getContainer={getContainer}>
|
||||
@ -409,21 +415,21 @@ describe('Anchor Render', () => {
|
||||
);
|
||||
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
setProps({ offsetTop: 100 });
|
||||
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
setProps({ targetOffset: 200 });
|
||||
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
|
||||
dateNowMock.mockRestore();
|
||||
@ -474,19 +480,19 @@ describe('Anchor Render', () => {
|
||||
);
|
||||
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
setProps({ offsetTop: 100 });
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
setProps({ targetOffset: 200 });
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
|
||||
dateNowMock.mockRestore();
|
||||
@ -584,19 +590,19 @@ describe('Anchor Render', () => {
|
||||
</Anchor>,
|
||||
);
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
setProps({ offsetTop: 100 });
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
setProps({ targetOffset: 200 });
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
|
||||
dateNowMock.mockRestore();
|
||||
@ -653,18 +659,18 @@ describe('Anchor Render', () => {
|
||||
</Anchor>,
|
||||
);
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
dateNowMock = dataNowMockFn();
|
||||
|
||||
setProps({ offsetTop: 100 });
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
dateNowMock = dataNowMockFn();
|
||||
setProps({ targetOffset: 200 });
|
||||
anchorInstance!.handleScrollTo(`#${hash}`);
|
||||
await sleep(30);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
|
||||
dateNowMock.mockRestore();
|
||||
|
@ -1,8 +1,8 @@
|
||||
---
|
||||
order: 28
|
||||
title:
|
||||
zh-CN: 基本
|
||||
en-US: Basic
|
||||
zh-CN: 弹出位置
|
||||
en-US: Placement
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
@ -46,7 +46,7 @@ You should use [Menu](/components/menu/) as `overlay`. The menu items and divide
|
||||
| disabled | Whether the dropdown menu is disabled | boolean | - | |
|
||||
| icon | Icon (appears on the right) | ReactNode | - | |
|
||||
| overlay | The dropdown menu | [Menu](/components/menu) | - | |
|
||||
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomRight` | |
|
||||
| size | Size of the button, the same as [Button](/components/button/#API) | string | `default` | |
|
||||
| trigger | The trigger mode which executes the dropdown action | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| type | Type of the button, the same as [Button](/components/button/#API) | string | `default` | |
|
||||
|
@ -50,7 +50,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/eedWN59yJ/Dropdown.svg
|
||||
| disabled | 菜单是否禁用 | boolean | - | |
|
||||
| icon | 右侧的 icon | ReactNode | - | |
|
||||
| overlay | 菜单 | [Menu](/components/menu/) | - | |
|
||||
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` | |
|
||||
| placement | 菜单弹出位置:`bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomRight` | |
|
||||
| size | 按钮大小,和 [Button](/components/button/#API) 一致 | string | `default` | |
|
||||
| trigger | 触发下拉的行为 | Array<`click`\|`hover`\|`contextMenu`> | \[`hover`] | |
|
||||
| type | 按钮类型,和 [Button](/components/button/#API) 一致 | string | `default` | |
|
||||
|
@ -13,6 +13,7 @@ import { FormItemInputContext, NoFormStyle } from '../form/context';
|
||||
import type { InputStatus } from '../_util/statusUtils';
|
||||
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
|
||||
import warning from '../_util/warning';
|
||||
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
|
||||
import { hasPrefixSuffix } from './utils';
|
||||
|
||||
// CSSINJS
|
||||
@ -177,25 +178,7 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
|
||||
}, [inputHasPrefixSuffix]);
|
||||
|
||||
// ===================== Remove Password value =====================
|
||||
const removePasswordTimeoutRef = useRef<number[]>([]);
|
||||
const removePasswordTimeout = () => {
|
||||
removePasswordTimeoutRef.current.push(
|
||||
window.setTimeout(() => {
|
||||
if (
|
||||
inputRef.current?.input &&
|
||||
inputRef.current?.input.getAttribute('type') === 'password' &&
|
||||
inputRef.current?.input.hasAttribute('value')
|
||||
) {
|
||||
inputRef.current?.input.removeAttribute('value');
|
||||
}
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
removePasswordTimeout();
|
||||
return () => removePasswordTimeoutRef.current.forEach(item => window.clearTimeout(item));
|
||||
}, []);
|
||||
const removePasswordTimeout = useRemovePasswordTimeout(inputRef, true);
|
||||
|
||||
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||
removePasswordTimeout();
|
||||
|
@ -2,10 +2,12 @@ import EyeInvisibleOutlined from '@ant-design/icons/EyeInvisibleOutlined';
|
||||
import EyeOutlined from '@ant-design/icons/EyeOutlined';
|
||||
import classNames from 'classnames';
|
||||
import omit from 'rc-util/lib/omit';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useRef, useState } from 'react';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigConsumer } from '../config-provider';
|
||||
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
|
||||
import type { InputProps, InputRef } from './Input';
|
||||
import Input from './Input';
|
||||
|
||||
@ -26,12 +28,19 @@ const ActionMap: Record<string, string> = {
|
||||
|
||||
const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
|
||||
// Remove Password value
|
||||
const removePasswordTimeout = useRemovePasswordTimeout(inputRef);
|
||||
|
||||
const onVisibleChange = () => {
|
||||
const { disabled } = props;
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
if (visible) {
|
||||
removePasswordTimeout();
|
||||
}
|
||||
setVisible(prevState => !prevState);
|
||||
};
|
||||
|
||||
@ -87,7 +96,7 @@ const Password = React.forwardRef<InputRef, PasswordProps>((props, ref) => {
|
||||
omittedProps.size = size;
|
||||
}
|
||||
|
||||
return <Input ref={ref} {...omittedProps} />;
|
||||
return <Input ref={composeRef(ref, inputRef)} {...omittedProps} />;
|
||||
};
|
||||
|
||||
return <ConfigConsumer>{renderPassword}</ConfigConsumer>;
|
||||
|
@ -108,4 +108,17 @@ describe('Input.Password', () => {
|
||||
await sleep();
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not show value attribute in input element after toggle visibility', async () => {
|
||||
const { container } = render(<Input.Password />);
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'value' } });
|
||||
await sleep();
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
|
||||
await sleep();
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeTruthy();
|
||||
fireEvent.click(container.querySelector('.ant-input-password-icon')!);
|
||||
await sleep();
|
||||
expect(container.querySelector('input')?.getAttribute('value')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
32
components/input/hooks/useRemovePasswordTimeout.ts
Normal file
32
components/input/hooks/useRemovePasswordTimeout.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import type { InputRef } from '../Input';
|
||||
|
||||
export default function useRemovePasswordTimeout(
|
||||
inputRef: React.RefObject<InputRef>,
|
||||
triggerOnMount?: boolean,
|
||||
) {
|
||||
const removePasswordTimeoutRef = useRef<number[]>([]);
|
||||
const removePasswordTimeout = () => {
|
||||
removePasswordTimeoutRef.current.push(
|
||||
window.setTimeout(() => {
|
||||
if (
|
||||
inputRef.current?.input &&
|
||||
inputRef.current?.input.getAttribute('type') === 'password' &&
|
||||
inputRef.current?.input.hasAttribute('value')
|
||||
) {
|
||||
inputRef.current?.input.removeAttribute('value');
|
||||
}
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (triggerOnMount) {
|
||||
removePasswordTimeout();
|
||||
}
|
||||
|
||||
return () => removePasswordTimeoutRef.current.forEach(item => window.clearTimeout(item));
|
||||
}, []);
|
||||
|
||||
return removePasswordTimeout;
|
||||
}
|
@ -204,7 +204,7 @@ class Spin extends React.Component<SpinClassProps, SpinState> {
|
||||
}
|
||||
}
|
||||
|
||||
const SpinFC: SpinFCType = (props: SpinProps) => {
|
||||
const SpinFC: SpinFCType = props => {
|
||||
const { prefixCls: customizePrefixCls } = props;
|
||||
const { getPrefixCls } = React.useContext(ConfigContext);
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import useForceUpdate from '../_util/hooks/useForceUpdate';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
import type { StatisticProps } from './Statistic';
|
||||
import Statistic from './Statistic';
|
||||
import type { countdownValueType, FormatConfig } from './utils';
|
||||
import type { countdownValueType, FormatConfig, valueType } from './utils';
|
||||
import { formatCountdown } from './utils';
|
||||
|
||||
const REFRESH_INTERVAL = 1000 / 30;
|
||||
@ -15,84 +16,54 @@ interface CountdownProps extends StatisticProps {
|
||||
}
|
||||
|
||||
function getTime(value?: countdownValueType) {
|
||||
return new Date(value as any).getTime();
|
||||
return new Date(value as valueType).getTime();
|
||||
}
|
||||
|
||||
class Countdown extends React.Component<CountdownProps, {}> {
|
||||
static defaultProps: Partial<CountdownProps> = {
|
||||
format: 'HH:mm:ss',
|
||||
const Countdown: React.FC<CountdownProps> = props => {
|
||||
const { value, format = 'HH:mm:ss', onChange, onFinish } = props;
|
||||
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const countdown = React.useRef<NodeJS.Timer | null>(null);
|
||||
|
||||
const stopTimer = () => {
|
||||
onFinish?.();
|
||||
if (countdown.current) {
|
||||
clearInterval(countdown.current);
|
||||
countdown.current = null;
|
||||
}
|
||||
};
|
||||
|
||||
countdownId?: number;
|
||||
|
||||
componentDidMount() {
|
||||
this.syncTimer();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.syncTimer();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.stopTimer();
|
||||
}
|
||||
|
||||
syncTimer = () => {
|
||||
const { value } = this.props;
|
||||
|
||||
const syncTimer = () => {
|
||||
const timestamp = getTime(value);
|
||||
if (timestamp >= Date.now()) {
|
||||
this.startTimer();
|
||||
} else {
|
||||
this.stopTimer();
|
||||
countdown.current = setInterval(() => {
|
||||
forceUpdate();
|
||||
onChange?.(timestamp - Date.now());
|
||||
if (timestamp < Date.now()) {
|
||||
stopTimer();
|
||||
}
|
||||
}, REFRESH_INTERVAL);
|
||||
}
|
||||
};
|
||||
|
||||
startTimer = () => {
|
||||
if (this.countdownId) return;
|
||||
|
||||
const { onChange, value } = this.props;
|
||||
const timestamp = getTime(value);
|
||||
|
||||
this.countdownId = window.setInterval(() => {
|
||||
this.forceUpdate();
|
||||
|
||||
if (onChange && timestamp > Date.now()) {
|
||||
onChange(timestamp - Date.now());
|
||||
React.useEffect(() => {
|
||||
syncTimer();
|
||||
return () => {
|
||||
if (countdown.current) {
|
||||
clearInterval(countdown.current);
|
||||
countdown.current = null;
|
||||
}
|
||||
}, REFRESH_INTERVAL);
|
||||
};
|
||||
};
|
||||
}, [value]);
|
||||
|
||||
stopTimer = () => {
|
||||
const { onFinish, value } = this.props;
|
||||
if (this.countdownId) {
|
||||
clearInterval(this.countdownId);
|
||||
this.countdownId = undefined;
|
||||
const formatter = (formatValue: countdownValueType, config: FormatConfig) =>
|
||||
formatCountdown(formatValue, { ...config, format });
|
||||
|
||||
const timestamp = getTime(value);
|
||||
if (onFinish && timestamp < Date.now()) {
|
||||
onFinish();
|
||||
}
|
||||
}
|
||||
};
|
||||
const valueRender = (node: React.ReactElement<HTMLDivElement>) =>
|
||||
cloneElement(node, { title: undefined });
|
||||
|
||||
formatCountdown = (value: countdownValueType, config: FormatConfig) => {
|
||||
const { format } = this.props;
|
||||
return formatCountdown(value, { ...config, format });
|
||||
};
|
||||
return <Statistic {...props} valueRender={valueRender} formatter={formatter} />;
|
||||
};
|
||||
|
||||
// Countdown do not need display the timestamp
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
valueRender = (node: React.ReactElement<HTMLDivElement>) =>
|
||||
cloneElement(node, {
|
||||
title: undefined,
|
||||
});
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Statistic valueRender={this.valueRender} {...this.props} formatter={this.formatCountdown} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Countdown;
|
||||
export default React.memo(Countdown);
|
||||
|
@ -5,7 +5,6 @@ import Statistic from '..';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import type Countdown from '../Countdown';
|
||||
import { formatTimeStr } from '../utils';
|
||||
|
||||
describe('Statistic', () => {
|
||||
@ -105,13 +104,8 @@ describe('Statistic', () => {
|
||||
it('time going', async () => {
|
||||
const now = Date.now() + 1000;
|
||||
const onFinish = jest.fn();
|
||||
const instance = React.createRef<Countdown>();
|
||||
const { unmount } = render(
|
||||
<Statistic.Countdown ref={instance} value={now} onFinish={onFinish} />,
|
||||
);
|
||||
|
||||
// setInterval should work
|
||||
expect(instance.current!.countdownId).not.toBe(undefined);
|
||||
const { unmount } = render(<Statistic.Countdown value={now} onFinish={onFinish} />);
|
||||
|
||||
await sleep(10);
|
||||
|
||||
@ -161,11 +155,9 @@ describe('Statistic', () => {
|
||||
describe('time finished', () => {
|
||||
it('not call if time already passed', () => {
|
||||
const now = Date.now() - 1000;
|
||||
const instance = React.createRef<Countdown>();
|
||||
const onFinish = jest.fn();
|
||||
render(<Statistic.Countdown ref={instance} value={now} onFinish={onFinish} />);
|
||||
render(<Statistic.Countdown value={now} onFinish={onFinish} />);
|
||||
|
||||
expect(instance.current!.countdownId).toBe(undefined);
|
||||
expect(onFinish).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
@ -554,7 +554,6 @@ const ForwardTable = React.forwardRef(InternalTable) as <RecordType extends obje
|
||||
type InternalTableType = typeof ForwardTable;
|
||||
|
||||
interface TableInterface extends InternalTableType {
|
||||
defaultProps?: Partial<TableProps<any>>;
|
||||
SELECTION_COLUMN: typeof SELECTION_COLUMN;
|
||||
EXPAND_COLUMN: typeof RcTable.EXPAND_COLUMN;
|
||||
SELECTION_ALL: 'SELECT_ALL';
|
||||
|
@ -227,6 +227,22 @@ describe('Table', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/37977
|
||||
it('should render title when enable ellipsis, sorter and filters', () => {
|
||||
const data = [] as any;
|
||||
const columns = [
|
||||
{ title: 'id', dataKey: 'id', ellipsis: true, sorter: true, filters: [] },
|
||||
{ title: 'age', dataKey: 'age', ellipsis: true, sorter: true },
|
||||
{ title: 'age', dataKey: 'age', ellipsis: true, filters: [] },
|
||||
];
|
||||
const { container } = render(<Table columns={columns} dataSource={data} />);
|
||||
container
|
||||
.querySelectorAll<HTMLTableCellElement>('.ant-table-thead th.ant-table-cell')
|
||||
.forEach(td => {
|
||||
expect((td.attributes as any).title).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('warn about rowKey when using index parameter', () => {
|
||||
warnSpy.mockReset();
|
||||
const columns = [
|
||||
|
@ -20374,6 +20374,7 @@ Array [
|
||||
aria-label="Name sortable"
|
||||
class="ant-table-cell ant-table-cell-ellipsis ant-table-column-has-sorters"
|
||||
tabindex="0"
|
||||
title="Name"
|
||||
>
|
||||
<div
|
||||
class="ant-table-filter-column"
|
||||
@ -20680,6 +20681,7 @@ Array [
|
||||
aria-label="Age sortable"
|
||||
class="ant-table-cell ant-table-cell-ellipsis ant-table-column-has-sorters"
|
||||
tabindex="0"
|
||||
title="Age"
|
||||
>
|
||||
<div
|
||||
class="ant-table-column-sorters"
|
||||
@ -20765,6 +20767,7 @@ Array [
|
||||
aria-label="Address sortable"
|
||||
class="ant-table-cell ant-table-cell-ellipsis ant-table-column-has-sorters"
|
||||
tabindex="0"
|
||||
title="Address"
|
||||
>
|
||||
<div
|
||||
class="ant-table-filter-column"
|
||||
|
@ -15392,6 +15392,7 @@ Array [
|
||||
aria-label="Name sortable"
|
||||
class="ant-table-cell ant-table-cell-ellipsis ant-table-column-has-sorters"
|
||||
tabindex="0"
|
||||
title="Name"
|
||||
>
|
||||
<div
|
||||
class="ant-table-filter-column"
|
||||
@ -15486,6 +15487,7 @@ Array [
|
||||
aria-label="Age sortable"
|
||||
class="ant-table-cell ant-table-cell-ellipsis ant-table-column-has-sorters"
|
||||
tabindex="0"
|
||||
title="Age"
|
||||
>
|
||||
<div
|
||||
class="ant-table-column-sorters"
|
||||
@ -15547,6 +15549,7 @@ Array [
|
||||
aria-label="Address sortable"
|
||||
class="ant-table-cell ant-table-cell-ellipsis ant-table-column-has-sorters"
|
||||
tabindex="0"
|
||||
title="Address"
|
||||
>
|
||||
<div
|
||||
class="ant-table-filter-column"
|
||||
|
@ -207,18 +207,15 @@ function injectSorter<RecordType>(
|
||||
|
||||
// Inform the screen-reader so it can tell the visually impaired user which column is sorted
|
||||
if (sorterOrder) {
|
||||
if (sorterOrder === 'ascend') {
|
||||
cell['aria-sort'] = 'ascending';
|
||||
} else {
|
||||
cell['aria-sort'] = 'descending';
|
||||
}
|
||||
cell['aria-sort'] = sorterOrder === 'ascend' ? 'ascending' : 'descending';
|
||||
} else {
|
||||
cell['aria-label'] = `${renderColumnTitle(column.title, {})} sortable`;
|
||||
}
|
||||
|
||||
cell.className = classNames(cell.className, `${prefixCls}-column-has-sorters`);
|
||||
cell.tabIndex = 0;
|
||||
|
||||
if (column.ellipsis) {
|
||||
cell.title = (renderColumnTitle(column.title, {}) ?? '').toString();
|
||||
}
|
||||
return cell;
|
||||
},
|
||||
};
|
||||
|
@ -23,7 +23,6 @@ export interface DirectoryTreeProps<T extends BasicDataNode = DataNode> extends
|
||||
type DirectoryTreeCompoundedComponent = (<T extends BasicDataNode | DataNode = DataNode>(
|
||||
props: React.PropsWithChildren<DirectoryTreeProps<T>> & { ref?: React.Ref<RcTree> },
|
||||
) => React.ReactElement) & {
|
||||
defaultProps: Partial<React.PropsWithChildren<DirectoryTreeProps<any>>>;
|
||||
displayName?: string;
|
||||
};
|
||||
|
||||
|
@ -159,7 +159,6 @@ export interface TreeProps<T extends BasicDataNode = DataNode>
|
||||
type CompoundedComponent = (<T extends BasicDataNode | DataNode = DataNode>(
|
||||
props: React.PropsWithChildren<TreeProps<T>> & { ref?: React.Ref<RcTree> },
|
||||
) => React.ReactElement) & {
|
||||
defaultProps: Partial<React.PropsWithChildren<TreeProps<any>>>;
|
||||
TreeNode: typeof TreeNode;
|
||||
DirectoryTree: typeof DirectoryTree;
|
||||
};
|
||||
|
@ -288,7 +288,7 @@ const Base = React.forwardRef((props: InternalBlockProps, ref: any) => {
|
||||
const [ellipsisFontSize, setEllipsisFontSize] = React.useState(0);
|
||||
const onResize = ({ offsetWidth }: { offsetWidth: number }, element: HTMLElement) => {
|
||||
setEllipsisWidth(offsetWidth);
|
||||
setEllipsisFontSize(parseInt(window.getComputedStyle?.(element).fontSize, 10));
|
||||
setEllipsisFontSize(parseInt(window.getComputedStyle?.(element).fontSize, 10) || 0);
|
||||
};
|
||||
|
||||
// >>>>> JS Ellipsis
|
||||
|
@ -185,9 +185,9 @@
|
||||
"@types/react-window": "^1.8.2",
|
||||
"@types/shallowequal": "^1.1.1",
|
||||
"@types/warning": "^3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"antd-img-crop": "^4.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.40.0",
|
||||
"@typescript-eslint/parser": "^5.40.0",
|
||||
"antd-img-crop": "4.2.5",
|
||||
"array-move": "^4.0.0",
|
||||
"babel-plugin-add-react-displayname": "^0.0.5",
|
||||
"bisheng": "^3.7.0-alpha.4",
|
||||
@ -291,7 +291,7 @@
|
||||
"stylelint-order": "^5.0.0",
|
||||
"theme-switcher": "^1.0.2",
|
||||
"ts-node": "^10.8.2",
|
||||
"typescript": "~4.8.0",
|
||||
"typescript": "~4.8.4",
|
||||
"webpack-bundle-analyzer": "^4.1.0",
|
||||
"xhr-mock": "^2.4.1",
|
||||
"yaml-front-matter": "^4.0.0"
|
||||
|
@ -13,12 +13,6 @@ interface ColorPickerProps {
|
||||
}
|
||||
|
||||
export default class ColorPicker extends Component<ColorPickerProps> {
|
||||
static defaultProps = {
|
||||
onChange: noop,
|
||||
onChangeComplete: noop,
|
||||
position: 'bottom',
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(props: ColorPickerProps) {
|
||||
if ('color' in props) {
|
||||
return {
|
||||
@ -43,28 +37,28 @@ export default class ColorPicker extends Component<ColorPickerProps> {
|
||||
};
|
||||
|
||||
handleChange = (color: { hex: string }) => {
|
||||
const { onChange } = this.props;
|
||||
const { onChange = noop } = this.props;
|
||||
this.setState({ color: color.hex });
|
||||
onChange(color.hex, color);
|
||||
};
|
||||
|
||||
handleChangeComplete = (color: { hex: string }) => {
|
||||
const { onChangeComplete } = this.props;
|
||||
const { onChangeComplete = noop } = this.props;
|
||||
this.setState({ color: color.hex });
|
||||
onChangeComplete(color.hex);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { small, position, presetColors } = this.props;
|
||||
const { small, position = 'bottom', presetColors } = this.props;
|
||||
const { color, displayColorPicker } = this.state;
|
||||
const width = small ? 80 : 120;
|
||||
const styles = {
|
||||
const styles: Record<PropertyKey, React.CSSProperties> = {
|
||||
color: {
|
||||
width: `${width}px`,
|
||||
height: small ? '16px' : '24px',
|
||||
borderRadius: '2px',
|
||||
background: color,
|
||||
} as React.CSSProperties,
|
||||
},
|
||||
swatch: {
|
||||
padding: '4px',
|
||||
background: '#fff',
|
||||
@ -72,22 +66,22 @@ export default class ColorPicker extends Component<ColorPickerProps> {
|
||||
boxShadow: '0 0 0 1px rgba(0,0,0,.1)',
|
||||
display: 'inline-block',
|
||||
cursor: 'pointer',
|
||||
} as React.CSSProperties,
|
||||
},
|
||||
popover: {
|
||||
position: 'absolute',
|
||||
zIndex: 10,
|
||||
} as React.CSSProperties,
|
||||
},
|
||||
cover: {
|
||||
position: 'fixed',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
left: '0px',
|
||||
} as React.CSSProperties,
|
||||
},
|
||||
wrapper: {
|
||||
position: 'inherit',
|
||||
zIndex: 100,
|
||||
} as React.CSSProperties,
|
||||
},
|
||||
};
|
||||
|
||||
if (position === 'top') {
|
||||
|
@ -64,10 +64,12 @@ class Demo extends React.Component {
|
||||
const { codeType } = this.state;
|
||||
if (typeof document !== 'undefined') {
|
||||
const div = document.createElement('div');
|
||||
const divJSX = document.createElement('div');
|
||||
div.innerHTML = highlightedCodes[codeType] || highlightedCodes.jsx;
|
||||
return div.textContent;
|
||||
divJSX.innerHTML = highlightedCodes.jsx;
|
||||
return [divJSX.textContent, div.textContent];
|
||||
}
|
||||
return '';
|
||||
return ['', ''];
|
||||
}
|
||||
|
||||
handleCodeExpand = demo => {
|
||||
@ -181,7 +183,7 @@ class Demo extends React.Component {
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const sourceCode = this.getSourceCode();
|
||||
const [sourceCode, sourceCodeTyped] = this.getSourceCode();
|
||||
|
||||
const dependencies = sourceCode.split('\n').reduce(
|
||||
(acc, line) => {
|
||||
@ -422,7 +424,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
|
||||
<ThunderboltOutlined className="code-box-stackblitz" />
|
||||
</span>
|
||||
</Tooltip>
|
||||
<CopyToClipboard text={sourceCode} onCopy={() => this.handleCodeCopied(meta.id)}>
|
||||
<CopyToClipboard text={sourceCodeTyped} onCopy={() => this.handleCodeCopied(meta.id)}>
|
||||
<Tooltip
|
||||
open={copyTooltipOpen}
|
||||
onOpenChange={this.onCopyTooltipOpenChange}
|
||||
|
@ -3,7 +3,8 @@
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"antd": ["components/index.tsx"],
|
||||
"antd/es/*": ["components/*"]
|
||||
"antd/es/*": ["components/*"],
|
||||
"antd/lib/*": ["components/*"]
|
||||
},
|
||||
"strictNullChecks": true,
|
||||
"module": "esnext",
|
||||
|
Loading…
Reference in New Issue
Block a user