Skip to content

refactor(flex): use SFC #8308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: feat/vapor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions apps/playground/src/pages/flex/basic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<template>
<a-flex gap="middle" vertical>
<label>
Select axis:
<select v-model="axis">
<option v-for="item in axisOptions" :key="item">{{ item }}</option>
</select>
</label>
<a-flex :vertical="axis === 'vertical'">
<div
v-for="(item, index) in new Array(4)"
:key="item"
:style="{ ...baseStyle, background: `${index % 2 ? '#1677ff' : '#1677ffbf'}` }"
/>
</a-flex>
<hr/>
<label>
Select justify:
<select v-model="justify">
<option v-for="item in justifyOptions" :key="item">{{ item }}</option>
</select>
</label>
<label>
Select align:
<select v-model="align">
<option v-for="item in alignOptions" :key="item">{{ item }}</option>
</select>
</label>
<a-flex :style="{ ...boxStyle }" :justify="justify" :align="align">
<a-button variant="solid">Primary</a-button>
<a-button variant="solid">Primary</a-button>
<a-button variant="solid">Primary</a-button>
<a-button variant="solid">Primary</a-button>
</a-flex>
<hr/>
<a-flex gap="middle" vertical>
<label>
Select gap size:
<select v-model="gapSize">
<option v-for="item in gapSizeOptions" :key="item">{{ item }}</option>
</select>
</label>
<a-flex :gap="gapSize">
<a-button variant="solid">Primary</a-button>
<a-button>Default</a-button>
<a-button variant="dashed">Dashed</a-button>
<a-button variant="link">Link</a-button>
</a-flex>
</a-flex>
<hr/>
<label>
Auto wrap:
</label>
<a-flex wrap="wrap" gap="small">
<a-button v-for="item in new Array(24)" :key="item" variant="solid">Button</a-button>
</a-flex>
</a-flex>
</template>

<script setup lang="ts">
import type { CSSProperties } from 'vue';
import { ref, reactive } from 'vue';

const baseStyle: CSSProperties = {
width: '25%',
height: '54px',
};
const boxStyle: CSSProperties = {
width: '100%',
height: '120px',
borderRadius: '6px',
border: '1px solid #40a9ff',
};

const axisOptions = reactive(['horizontal', 'vertical']);
const axis = ref(axisOptions[0]);

const justifyOptions = reactive([
'flex-start',
'center',
'flex-end',
'space-between',
'space-around',
'space-evenly',
]);
const justify = ref(justifyOptions[0]);

const alignOptions = reactive(['flex-start', 'center', 'flex-end']);
const align = ref(alignOptions[0]);

const gapSizeOptions = reactive(['small', 'middle', 'large']);
const gapSize = ref(gapSizeOptions[0]);
</script>
42 changes: 42 additions & 0 deletions packages/ui/src/components/flex/Flex.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup lang="ts">
import type { CSSProperties } from 'vue'
import { computed, withDefaults } from 'vue'
import { type FlexProps, flexDefaultProps } from './meta'
import { isPresetSize } from '@/utils/gapSize'
import createFlexClassNames from './utils'
defineOptions({ name: 'AFlex' })
const props = withDefaults(defineProps<FlexProps>(), flexDefaultProps)
const mergedCls = computed(() => [
createFlexClassNames(props.prefixCls, props),
{
'ant-flex': true,
'ant-flex-vertical': props.vertical,
'ant-flex-rtl': false,
[`ant-flex-gap-${props.gap}`]: isPresetSize(props.gap),
}
])
const mergedStyle = computed(() => {
const style: CSSProperties = {}
if (props.flex) {
style.flex = props.flex
}
if (props.gap && !isPresetSize(props.gap)) {
style.gap = `${props.gap}px`
}
return style
})
</script>

<template>
<component :is="componentTag" :class="[$attrs.class, mergedCls]" :style="[$attrs.style, mergedStyle]" v-bind="$attrs">
<slot />
</component>
</template>

<style scoped></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Flex > should render correctly 1`] = `
"<div class="ant-flex">
<div>test</div>
</div>"
`;
149 changes: 149 additions & 0 deletions packages/ui/src/components/flex/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { describe, expect, it, vi } from 'vitest'
import { Flex } from '@ant-design-vue/ui'
import { mount } from '@vue/test-utils'

describe('Flex', () => {
it('should render correctly', () => {
const wrapper = mount(Flex, {
slots: {
default: `<div>test</div>`,
},
});

expect(wrapper.html()).toMatchSnapshot();
});

it('Flex', () => {
const wrapper = mount(Flex, {
props: {
justify: 'center'
},
slots: {
default: `<div>test</div>`
},
});

const wrapper3 = mount(Flex, {
props: {
flex: '0 1 auto',
},
slots: {
default: `<div>test</div>`,
},
});

expect(wrapper.classes('ant-flex')).toBeTruthy();
expect(wrapper.find('.ant-flex-justify-center')).toBeTruthy();
expect(wrapper3.classes('ant-flex')).toBeTruthy();
expect(wrapper3.element.style.flex).toBe('0 1 auto');
});

describe('Props: gap', () => {
it('support string', () => {
const wrapper = mount(Flex, {
props: {
gap: 'inherit',
},
slots: {
default: `<div>test</div>`,
},
});
expect(wrapper.classes('ant-flex')).toBeTruthy();
expect(wrapper.element.style.gap).toBe('inherit');
});

it('support number', () => {
const wrapper = mount(Flex, {
props: {
gap: '100',
},
slots: {
default: `<div>test</div>`,
},
});
expect(wrapper.classes('ant-flex')).toBeTruthy();
expect(wrapper.element.style.gap).toBe('100px');
});

it('support preset size', () => {
const wrapper = mount(Flex, {
props: {
gap: 'small',
},
slots: {
default: `<div>test</div>`,
},
});

expect(wrapper.classes('ant-flex')).toBeTruthy();
expect(wrapper.classes('ant-flex-gap-small')).toBeTruthy();
});
});

it('Component work', () => {
const wrapper = mount(Flex, {
slots: {
default: `<div>test</div>`
},
});

const wrapper2 = mount(Flex, {
props: {
componentTag: 'span'
},
slots: {
default: `<div>test</div>`
},
});

expect(wrapper.find('.ant-flex').element.tagName).toBe('DIV');
expect(wrapper2.find('.ant-flex').element.tagName).toBe('SPAN');
});

it('when vertical=true should stretch work', () => {
const wrapper = mount(Flex, {
props: {
vertical: true
},
slots: {
default: `<div>test</div>`
},
});

const wrapper2 = mount(Flex, {
props: {
vertical: true,
align: 'center',
},
slots: {
default: `<div>test</div>`,
},
});

expect(wrapper.find('.ant-flex-align-stretch')).toBeTruthy();
expect(wrapper2.find('.ant-flex-align-center')).toBeTruthy();
});

it('wrap prop shouled support boolean', () => {
const wrapper = mount(Flex, {
props: {
wrap: 'wrap',
},
slots: {
default: `<div>test</div>`,
},
});

const wrapper2 = mount(Flex, {
props: {
wrap: true,
},
slots: {
default: `<div>test</div>`,
},
});

expect(wrapper.classes('ant-flex-wrap-wrap')).toBeTruthy();
expect(wrapper2.classes('ant-flex-wrap-wrap')).toBeTruthy();
})
})
14 changes: 14 additions & 0 deletions packages/ui/src/components/flex/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { App, Plugin } from 'vue'
import Flex from './Flex.vue'
import './style/index.css'


export { default as Flex } from './Flex.vue'
export * from './meta'

Flex.install = function (app: App) {
app.component('AFlex', Flex)
return app
}

export default Flex as typeof Flex & Plugin
20 changes: 20 additions & 0 deletions packages/ui/src/components/flex/meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { CSSProperties } from "vue"

type SizeType = 'small' | 'middle' | 'large' | undefined

export type FlexProps = {
prefixCls?: string
rootClassName?: string
vertical?: boolean
wrap?: CSSProperties['flexWrap'] | boolean
justify?: CSSProperties['justifyContent']
align?: CSSProperties['alignItems']
flex?: CSSProperties['flex']
gap?: CSSProperties['gap'] | SizeType
componentTag?: any
}

export const flexDefaultProps = {
prefixCls: 'ant-flex',
componentTag: 'div',
} as const
Loading
Loading