Skip to content

Commit a15bb9c

Browse files
authored
refactor(flex): use SFC (#8308)
* refactor(flex): use SFC * test: add unit tests
1 parent 5cc4a63 commit a15bb9c

File tree

12 files changed

+535
-0
lines changed

12 files changed

+535
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<template>
2+
<a-flex gap="middle" vertical>
3+
<label>
4+
Select axis:
5+
<select v-model="axis">
6+
<option v-for="item in axisOptions" :key="item">{{ item }}</option>
7+
</select>
8+
</label>
9+
<a-flex :vertical="axis === 'vertical'">
10+
<div
11+
v-for="(item, index) in new Array(4)"
12+
:key="item"
13+
:style="{ ...baseStyle, background: `${index % 2 ? '#1677ff' : '#1677ffbf'}` }"
14+
/>
15+
</a-flex>
16+
<hr/>
17+
<label>
18+
Select justify:
19+
<select v-model="justify">
20+
<option v-for="item in justifyOptions" :key="item">{{ item }}</option>
21+
</select>
22+
</label>
23+
<label>
24+
Select align:
25+
<select v-model="align">
26+
<option v-for="item in alignOptions" :key="item">{{ item }}</option>
27+
</select>
28+
</label>
29+
<a-flex :style="{ ...boxStyle }" :justify="justify" :align="align">
30+
<a-button variant="solid">Primary</a-button>
31+
<a-button variant="solid">Primary</a-button>
32+
<a-button variant="solid">Primary</a-button>
33+
<a-button variant="solid">Primary</a-button>
34+
</a-flex>
35+
<hr/>
36+
<a-flex gap="middle" vertical>
37+
<label>
38+
Select gap size:
39+
<select v-model="gapSize">
40+
<option v-for="item in gapSizeOptions" :key="item">{{ item }}</option>
41+
</select>
42+
</label>
43+
<a-flex :gap="gapSize">
44+
<a-button variant="solid">Primary</a-button>
45+
<a-button>Default</a-button>
46+
<a-button variant="dashed">Dashed</a-button>
47+
<a-button variant="link">Link</a-button>
48+
</a-flex>
49+
</a-flex>
50+
<hr/>
51+
<label>
52+
Auto wrap:
53+
</label>
54+
<a-flex wrap="wrap" gap="small">
55+
<a-button v-for="item in new Array(24)" :key="item" variant="solid">Button</a-button>
56+
</a-flex>
57+
</a-flex>
58+
</template>
59+
60+
<script setup lang="ts">
61+
import type { CSSProperties } from 'vue';
62+
import { ref, reactive } from 'vue';
63+
64+
const baseStyle: CSSProperties = {
65+
width: '25%',
66+
height: '54px',
67+
};
68+
const boxStyle: CSSProperties = {
69+
width: '100%',
70+
height: '120px',
71+
borderRadius: '6px',
72+
border: '1px solid #40a9ff',
73+
};
74+
75+
const axisOptions = reactive(['horizontal', 'vertical']);
76+
const axis = ref(axisOptions[0]);
77+
78+
const justifyOptions = reactive([
79+
'flex-start',
80+
'center',
81+
'flex-end',
82+
'space-between',
83+
'space-around',
84+
'space-evenly',
85+
]);
86+
const justify = ref(justifyOptions[0]);
87+
88+
const alignOptions = reactive(['flex-start', 'center', 'flex-end']);
89+
const align = ref(alignOptions[0]);
90+
91+
const gapSizeOptions = reactive(['small', 'middle', 'large']);
92+
const gapSize = ref(gapSizeOptions[0]);
93+
</script>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script setup lang="ts">
2+
import type { CSSProperties } from 'vue'
3+
import { computed, withDefaults } from 'vue'
4+
import { type FlexProps, flexDefaultProps } from './meta'
5+
import { isPresetSize } from '@/utils/gapSize'
6+
import createFlexClassNames from './utils'
7+
8+
defineOptions({ name: 'AFlex' })
9+
const props = withDefaults(defineProps<FlexProps>(), flexDefaultProps)
10+
11+
const mergedCls = computed(() => [
12+
createFlexClassNames(props.prefixCls, props),
13+
{
14+
'ant-flex': true,
15+
'ant-flex-vertical': props.vertical,
16+
'ant-flex-rtl': false,
17+
[`ant-flex-gap-${props.gap}`]: isPresetSize(props.gap),
18+
}
19+
])
20+
21+
const mergedStyle = computed(() => {
22+
const style: CSSProperties = {}
23+
24+
if (props.flex) {
25+
style.flex = props.flex
26+
}
27+
28+
if (props.gap && !isPresetSize(props.gap)) {
29+
style.gap = `${props.gap}px`
30+
}
31+
32+
return style
33+
})
34+
</script>
35+
36+
<template>
37+
<component :is="componentTag" :class="[$attrs.class, mergedCls]" :style="[$attrs.style, mergedStyle]" v-bind="$attrs">
38+
<slot />
39+
</component>
40+
</template>
41+
42+
<style scoped></style>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`Flex > should render correctly 1`] = `
4+
"<div class="ant-flex">
5+
<div>test</div>
6+
</div>"
7+
`;
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import { describe, expect, it, vi } from 'vitest'
2+
import { Flex } from '@ant-design-vue/ui'
3+
import { mount } from '@vue/test-utils'
4+
5+
describe('Flex', () => {
6+
it('should render correctly', () => {
7+
const wrapper = mount(Flex, {
8+
slots: {
9+
default: `<div>test</div>`,
10+
},
11+
});
12+
13+
expect(wrapper.html()).toMatchSnapshot();
14+
});
15+
16+
it('Flex', () => {
17+
const wrapper = mount(Flex, {
18+
props: {
19+
justify: 'center'
20+
},
21+
slots: {
22+
default: `<div>test</div>`
23+
},
24+
});
25+
26+
const wrapper3 = mount(Flex, {
27+
props: {
28+
flex: '0 1 auto',
29+
},
30+
slots: {
31+
default: `<div>test</div>`,
32+
},
33+
});
34+
35+
expect(wrapper.classes('ant-flex')).toBeTruthy();
36+
expect(wrapper.find('.ant-flex-justify-center')).toBeTruthy();
37+
expect(wrapper3.classes('ant-flex')).toBeTruthy();
38+
expect(wrapper3.element.style.flex).toBe('0 1 auto');
39+
});
40+
41+
describe('Props: gap', () => {
42+
it('support string', () => {
43+
const wrapper = mount(Flex, {
44+
props: {
45+
gap: 'inherit',
46+
},
47+
slots: {
48+
default: `<div>test</div>`,
49+
},
50+
});
51+
expect(wrapper.classes('ant-flex')).toBeTruthy();
52+
expect(wrapper.element.style.gap).toBe('inherit');
53+
});
54+
55+
it('support number', () => {
56+
const wrapper = mount(Flex, {
57+
props: {
58+
gap: '100',
59+
},
60+
slots: {
61+
default: `<div>test</div>`,
62+
},
63+
});
64+
expect(wrapper.classes('ant-flex')).toBeTruthy();
65+
expect(wrapper.element.style.gap).toBe('100px');
66+
});
67+
68+
it('support preset size', () => {
69+
const wrapper = mount(Flex, {
70+
props: {
71+
gap: 'small',
72+
},
73+
slots: {
74+
default: `<div>test</div>`,
75+
},
76+
});
77+
78+
expect(wrapper.classes('ant-flex')).toBeTruthy();
79+
expect(wrapper.classes('ant-flex-gap-small')).toBeTruthy();
80+
});
81+
});
82+
83+
it('Component work', () => {
84+
const wrapper = mount(Flex, {
85+
slots: {
86+
default: `<div>test</div>`
87+
},
88+
});
89+
90+
const wrapper2 = mount(Flex, {
91+
props: {
92+
componentTag: 'span'
93+
},
94+
slots: {
95+
default: `<div>test</div>`
96+
},
97+
});
98+
99+
expect(wrapper.find('.ant-flex').element.tagName).toBe('DIV');
100+
expect(wrapper2.find('.ant-flex').element.tagName).toBe('SPAN');
101+
});
102+
103+
it('when vertical=true should stretch work', () => {
104+
const wrapper = mount(Flex, {
105+
props: {
106+
vertical: true
107+
},
108+
slots: {
109+
default: `<div>test</div>`
110+
},
111+
});
112+
113+
const wrapper2 = mount(Flex, {
114+
props: {
115+
vertical: true,
116+
align: 'center',
117+
},
118+
slots: {
119+
default: `<div>test</div>`,
120+
},
121+
});
122+
123+
expect(wrapper.find('.ant-flex-align-stretch')).toBeTruthy();
124+
expect(wrapper2.find('.ant-flex-align-center')).toBeTruthy();
125+
});
126+
127+
it('wrap prop shouled support boolean', () => {
128+
const wrapper = mount(Flex, {
129+
props: {
130+
wrap: 'wrap',
131+
},
132+
slots: {
133+
default: `<div>test</div>`,
134+
},
135+
});
136+
137+
const wrapper2 = mount(Flex, {
138+
props: {
139+
wrap: true,
140+
},
141+
slots: {
142+
default: `<div>test</div>`,
143+
},
144+
});
145+
146+
expect(wrapper.classes('ant-flex-wrap-wrap')).toBeTruthy();
147+
expect(wrapper2.classes('ant-flex-wrap-wrap')).toBeTruthy();
148+
})
149+
})
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { App, Plugin } from 'vue'
2+
import Flex from './Flex.vue'
3+
import './style/index.css'
4+
5+
6+
export { default as Flex } from './Flex.vue'
7+
export * from './meta'
8+
9+
Flex.install = function (app: App) {
10+
app.component('AFlex', Flex)
11+
return app
12+
}
13+
14+
export default Flex as typeof Flex & Plugin
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { CSSProperties } from "vue"
2+
3+
type SizeType = 'small' | 'middle' | 'large' | undefined
4+
5+
export type FlexProps = {
6+
prefixCls?: string
7+
rootClassName?: string
8+
vertical?: boolean
9+
wrap?: CSSProperties['flexWrap'] | boolean
10+
justify?: CSSProperties['justifyContent']
11+
align?: CSSProperties['alignItems']
12+
flex?: CSSProperties['flex']
13+
gap?: CSSProperties['gap'] | SizeType
14+
componentTag?: any
15+
}
16+
17+
export const flexDefaultProps = {
18+
prefixCls: 'ant-flex',
19+
componentTag: 'div',
20+
} as const

0 commit comments

Comments
 (0)