Skip to content

Commit 41324dc

Browse files
committed
Merge branch 'v3-button' into next
2 parents e85d436 + 202f341 commit 41324dc

22 files changed

+735
-293
lines changed

antd-tools/getTSCommonConfig.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const fs = require('fs');
44
const assign = require('object-assign');
55
const { getProjectPath } = require('./utils/projectHelper');
66

7-
module.exports = function () {
7+
module.exports = function() {
88
let my = {};
99
if (fs.existsSync(getProjectPath('tsconfig.json'))) {
1010
my = require(getProjectPath('tsconfig.json'));

antd-tools/gulpfile.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function compileTs(stream) {
8080
return stream
8181
.pipe(ts(tsConfig))
8282
.js.pipe(
83-
through2.obj(function (file, encoding, next) {
83+
through2.obj(function(file, encoding, next) {
8484
// console.log(file.path, file.base);
8585
file.path = file.path.replace(/\.[jt]sx$/, '.js');
8686
this.push(file);
@@ -146,7 +146,7 @@ function compile(modules) {
146146
const less = gulp
147147
.src(['components/**/*.less'])
148148
.pipe(
149-
through2.obj(function (file, encoding, next) {
149+
through2.obj(function(file, encoding, next) {
150150
this.push(file.clone());
151151
if (
152152
file.path.match(/\/style\/index\.less$/) ||

antd-tools/utils/getChangelog.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
const fs = require('fs');
22

33
module.exports = function getChangelog(file, version) {
4-
const lines = fs.readFileSync(file).toString().split('\n');
4+
const lines = fs
5+
.readFileSync(file)
6+
.toString()
7+
.split('\n');
58
const changeLog = [];
69
const startPattern = new RegExp(`^## ${version}`);
710
const stopPattern = /^## /; // 前一个版本

antd-tools/utils/getRunCmdEnv.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ module.exports = function getRunCmdEnv() {
1111
const nodeModulesBinDir = path.join(__dirname, '../../node_modules/.bin');
1212

1313
Object.entries(env)
14-
.filter(v => v.slice(0, 1).pop().toLowerCase() === 'path')
14+
.filter(
15+
v =>
16+
v
17+
.slice(0, 1)
18+
.pop()
19+
.toLowerCase() === 'path',
20+
)
1521
.forEach(v => {
1622
const key = v.slice(0, 1).pop();
1723
env[key] = env[key] ? `${nodeModulesBinDir}:${env[key]}` : nodeModulesBinDir;

antd-tools/utils/projectHelper.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ function injectRequire() {
2020
const Module = require('module');
2121

2222
const oriRequire = Module.prototype.require;
23-
Module.prototype.require = function (...args) {
23+
Module.prototype.require = function(...args) {
2424
const moduleName = args[0];
2525
try {
2626
return oriRequire.apply(this, args);

components/_util/hooks/useConfigInject.ts

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default (
1818
form?: ComputedRef<{
1919
requiredMark?: RequiredMark;
2020
}>;
21+
autoInsertSpaceInButton: ComputedRef<Boolean>;
2122
renderEmpty?: ComputedRef<(componentName?: string) => VNodeChild | JSX.Element>;
2223
} => {
2324
const configProvider = inject<UnwrapRef<ConfigProviderProps>>(
@@ -26,6 +27,7 @@ export default (
2627
);
2728
const prefixCls = computed(() => configProvider.getPrefixCls(name, props.prefixCls));
2829
const direction = computed(() => configProvider.direction);
30+
const autoInsertSpaceInButton = computed(() => configProvider.autoInsertSpaceInButton);
2931
const renderEmpty = computed(() => configProvider.renderEmpty);
3032
const space = computed(() => configProvider.space);
3133
const pageHeader = computed(() => configProvider.pageHeader);
@@ -41,6 +43,7 @@ export default (
4143
space,
4244
pageHeader,
4345
form,
46+
autoInsertSpaceInButton,
4447
renderEmpty,
4548
};
4649
};

components/button/__tests__/__snapshots__/index.test.js.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ exports[`Button should not render as link button when href is undefined 1`] = `
5959
`;
6060

6161
exports[`Button should support link button 1`] = `
62-
<a target="_blank" class="ant-btn" href="http://ant.design">
62+
<a class="ant-btn" href="http://ant.design" target="_blank">
6363
<!----><span>link button</span>
6464
</a>
6565
`;

components/button/__tests__/index.test.js

+51-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import Button from '../index';
22
import SearchOutlined from '@ant-design/icons-vue/SearchOutlined';
33
import { mount } from '@vue/test-utils';
44
import { nextTick } from 'vue';
5-
import { asyncExpect } from '@/tests/utils';
6-
import { sleep } from '../../../tests/utils';
7-
import mountTest from '../../../tests/shared/mountTest';
5+
import { asyncExpect, sleep } from '@/tests/utils';
6+
import mountTest from '@/tests/shared/mountTest';
7+
import { resetWarned } from '../../_util/warning';
88

99
describe('Button', () => {
1010
mountTest(Button);
@@ -27,7 +27,7 @@ describe('Button', () => {
2727
expect(wrapper.find('.ant-btn-primary').exists()).toBe(true);
2828
});
2929

30-
it('renders Chinese characters correctly', done => {
30+
it('renders Chinese characters correctly', (done) => {
3131
const wrapper = mount({
3232
render() {
3333
return <Button>按钮</Button>;
@@ -247,4 +247,51 @@ describe('Button', () => {
247247
wrapper.unmount();
248248
}).not.toThrow();
249249
});
250+
251+
it('should warning when pass type=link and ghost=true', () => {
252+
resetWarned();
253+
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
254+
mount({
255+
render() {
256+
return <Button type="link" ghost />;
257+
},
258+
});
259+
expect(warnSpy).toHaveBeenCalledWith(
260+
"Warning: [ant-design-vue: Button] `link` or `text` button can't be a `ghost` button.",
261+
);
262+
warnSpy.mockRestore();
263+
});
264+
265+
it('should warning when pass type=text and ghost=true', () => {
266+
resetWarned();
267+
const warnSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
268+
mount({
269+
render() {
270+
return <Button type="text" ghost />;
271+
},
272+
});
273+
expect(warnSpy).toHaveBeenCalledWith(
274+
"Warning: [ant-design-vue: Button] `link` or `text` button can't be a `ghost` button.",
275+
);
276+
warnSpy.mockRestore();
277+
});
278+
279+
it('should not redirect when button is disabled', async () => {
280+
const onClick = jest.fn();
281+
const wrapper = mount({
282+
render() {
283+
return (
284+
<Button href="https://ant.design" onClick={onClick} disabled>
285+
click me
286+
</Button>
287+
);
288+
},
289+
});
290+
await asyncExpect(() => {
291+
wrapper.trigger('click');
292+
});
293+
await asyncExpect(() => {
294+
expect(onClick).not.toHaveBeenCalled();
295+
});
296+
});
250297
});
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import Button from '../index';
2+
import { mount } from '@vue/test-utils';
3+
import { asyncExpect, sleep } from '@/tests/utils';
4+
5+
describe('click wave effect', () => {
6+
async function clickButton(wrapper) {
7+
await asyncExpect(() => {
8+
wrapper.find('.ant-btn').trigger('click');
9+
});
10+
wrapper.find('.ant-btn').element.dispatchEvent(new Event('transitionstart'));
11+
await sleep(20);
12+
wrapper.find('.ant-btn').element.dispatchEvent(new Event('animationend'));
13+
await sleep(20);
14+
}
15+
16+
it('should have click wave effect for primary button', async () => {
17+
const wrapper = mount({
18+
render() {
19+
return <Button type="primary">button</Button>;
20+
},
21+
});
22+
await clickButton(wrapper);
23+
expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe(
24+
'true',
25+
);
26+
});
27+
28+
it('should have click wave effect for default button', async () => {
29+
const wrapper = mount({
30+
render() {
31+
return <Button>button</Button>;
32+
},
33+
});
34+
await clickButton(wrapper);
35+
expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe(
36+
'true',
37+
);
38+
});
39+
40+
it('should not have click wave effect for link type button', async () => {
41+
const wrapper = mount({
42+
render() {
43+
return <Button type="link">button</Button>;
44+
},
45+
});
46+
await clickButton(wrapper);
47+
expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe(
48+
undefined,
49+
);
50+
});
51+
52+
it('should not have click wave effect for text type button', async () => {
53+
const wrapper = mount({
54+
render() {
55+
return <Button type="text">button</Button>;
56+
},
57+
});
58+
await clickButton(wrapper);
59+
expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe(
60+
undefined,
61+
);
62+
});
63+
64+
it('should handle transitionstart', async () => {
65+
const wrapper = mount({
66+
render() {
67+
return <Button type="primary">button</Button>;
68+
},
69+
});
70+
await clickButton(wrapper);
71+
const buttonNode = wrapper.find('.ant-btn').element;
72+
buttonNode.dispatchEvent(new Event('transitionstart'));
73+
expect(wrapper.find('.ant-btn').attributes('ant-click-animating-without-extra-node')).toBe(
74+
'true',
75+
);
76+
wrapper.unmount();
77+
buttonNode.dispatchEvent(new Event('transitionstart'));
78+
});
79+
});

components/button/button-group.tsx

+40-44
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,49 @@
1-
import { defineComponent, inject } from 'vue';
2-
import { filterEmpty, getSlot } from '../_util/props-util';
1+
import { computed, defineComponent } from 'vue';
2+
import { flattenChildren } from '../_util/props-util';
33
import PropTypes from '../_util/vue-types';
4-
import { defaultConfigProvider } from '../config-provider';
5-
import { tuple } from '../_util/type';
4+
import useConfigInject from '../_util/hooks/useConfigInject';
65

7-
const ButtonGroupProps = {
6+
import type { ExtractPropTypes, PropType } from 'vue';
7+
import type { SizeType } from '../config-provider';
8+
9+
const buttonGroupProps = {
810
prefixCls: PropTypes.string,
9-
size: PropTypes.oneOf(tuple('small', 'large', 'default')),
11+
size: {
12+
type: String as PropType<SizeType>,
13+
},
1014
};
11-
export { ButtonGroupProps };
15+
export { buttonGroupProps };
16+
17+
export type ButtonGroupProps = Partial<ExtractPropTypes<typeof buttonGroupProps>>;
18+
1219
export default defineComponent({
1320
name: 'AButtonGroup',
14-
props: ButtonGroupProps,
15-
setup() {
16-
const configProvider = inject('configProvider', defaultConfigProvider);
17-
return {
18-
configProvider,
19-
};
20-
},
21-
data() {
22-
return {
23-
sizeMap: {
24-
large: 'lg',
25-
small: 'sm',
26-
},
27-
};
28-
},
29-
render() {
30-
const { prefixCls: customizePrefixCls, size } = this;
31-
const getPrefixCls = this.configProvider.getPrefixCls;
32-
const prefixCls = getPrefixCls('btn-group', customizePrefixCls);
33-
34-
// large => lg
35-
// small => sm
36-
let sizeCls = '';
37-
switch (size) {
38-
case 'large':
39-
sizeCls = 'lg';
40-
break;
41-
case 'small':
42-
sizeCls = 'sm';
43-
break;
44-
default:
45-
break;
46-
}
47-
const classes = {
48-
[`${prefixCls}`]: true,
49-
[`${prefixCls}-${sizeCls}`]: sizeCls,
21+
props: buttonGroupProps,
22+
setup(props, { slots }) {
23+
const { prefixCls, direction } = useConfigInject('btn-group', props);
24+
const classes = computed(() => {
25+
const { size } = props;
26+
// large => lg
27+
// small => sm
28+
let sizeCls = '';
29+
switch (size) {
30+
case 'large':
31+
sizeCls = 'lg';
32+
break;
33+
case 'small':
34+
sizeCls = 'sm';
35+
break;
36+
default:
37+
break;
38+
}
39+
return {
40+
[`${prefixCls.value}`]: true,
41+
[`${prefixCls.value}-${sizeCls}`]: sizeCls,
42+
[`${prefixCls.value}-rtl`]: direction.value === 'rtl',
43+
};
44+
});
45+
return () => {
46+
return <div class={classes.value}>{flattenChildren(slots.default?.())}</div>;
5047
};
51-
return <div class={classes}>{filterEmpty(getSlot(this))}</div>;
5248
},
5349
});

0 commit comments

Comments
 (0)