mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-11-13 14:10:32 +00:00
feat(editor): 补全样式面板 CSS 枚举选项并补齐图标
- textAlign/display/justifyContent/alignItems/backgroundRepeat/fontWeight/borderStyle 按 CSS 规范补齐关键字 - 新增 15 个图标组件,新增项统一为图标按钮;textAlign 去掉不等宽文案,避免换行后边框错位 - 修正 opacity 标签由「透明度(%)」改为「透明度(0-1)」 - 新增 configs.spec.ts 并更新 Layout.spec.ts / icons.spec.ts
This commit is contained in:
parent
72664955bf
commit
0e8b676fe7
@ -8,14 +8,35 @@ import type { validateDataSourceFieldSelect } from '@editor/utils/type-match-rul
|
||||
|
||||
import BackgroundPosition from './components/BackgroundPosition.vue';
|
||||
import {
|
||||
AlignItemsBaseline,
|
||||
AlignItemsCenter,
|
||||
AlignItemsEnd,
|
||||
AlignItemsFlexEnd,
|
||||
AlignItemsFlexStart,
|
||||
AlignItemsNormal,
|
||||
AlignItemsSpaceAround,
|
||||
AlignItemsSpaceBetween,
|
||||
AlignItemsStart,
|
||||
AlignItemsStretch,
|
||||
} from './icons/align-items';
|
||||
import { BackgroundNoRepeat, BackgroundRepeat, BackgroundRepeatX, BackgroundRepeatY } from './icons/background-repeat';
|
||||
import { DisplayBlock, DisplayFlex, DisplayInline, DisplayInlineBlock, DisplayNone } from './icons/display';
|
||||
import {
|
||||
BackgroundNoRepeat,
|
||||
BackgroundRepeat,
|
||||
BackgroundRepeatRound,
|
||||
BackgroundRepeatSpace,
|
||||
BackgroundRepeatX,
|
||||
BackgroundRepeatY,
|
||||
} from './icons/background-repeat';
|
||||
import {
|
||||
DisplayBlock,
|
||||
DisplayFlex,
|
||||
DisplayGrid,
|
||||
DisplayInline,
|
||||
DisplayInlineBlock,
|
||||
DisplayInlineFlex,
|
||||
DisplayInlineGrid,
|
||||
DisplayNone,
|
||||
} from './icons/display';
|
||||
import {
|
||||
FlexDirectionColumn,
|
||||
FlexDirectionColumnReverse,
|
||||
@ -28,8 +49,17 @@ import {
|
||||
JustifyContentFlexStart,
|
||||
JustifyContentSpaceAround,
|
||||
JustifyContentSpaceBetween,
|
||||
JustifyContentSpaceEvenly,
|
||||
} from './icons/justify-content';
|
||||
import { AlignCenter, AlignLeft, AlignRight } from './icons/text-align';
|
||||
import {
|
||||
AlignCenter,
|
||||
AlignEnd,
|
||||
AlignJustify,
|
||||
AlignLeft,
|
||||
AlignMatchParent,
|
||||
AlignRight,
|
||||
AlignStart,
|
||||
} from './icons/text-align';
|
||||
|
||||
/**
|
||||
* 「布局」面板配置。
|
||||
@ -71,6 +101,9 @@ export const createLayoutConfig = (theme: string): FormConfig => [
|
||||
icon: markRaw(DisplayNone),
|
||||
tooltip: '隐藏 none',
|
||||
},
|
||||
{ value: 'inline-flex', icon: markRaw(DisplayInlineFlex), tooltip: '内联弹性布局 inline-flex' },
|
||||
{ value: 'grid', icon: markRaw(DisplayGrid), tooltip: '网格布局 grid' },
|
||||
{ value: 'inline-grid', icon: markRaw(DisplayInlineGrid), tooltip: '内联网格布局 inline-grid' },
|
||||
],
|
||||
}),
|
||||
defineFormItem({
|
||||
@ -121,6 +154,7 @@ export const createLayoutConfig = (theme: string): FormConfig => [
|
||||
icon: markRaw(JustifyContentSpaceAround),
|
||||
tooltip: '横向平分 space-around',
|
||||
},
|
||||
{ value: 'space-evenly', icon: markRaw(JustifyContentSpaceEvenly), tooltip: '等间距分布 space-evenly' },
|
||||
],
|
||||
display: (_mForm, { model }: { model: Record<any, any> }) => model.display === 'flex',
|
||||
}),
|
||||
@ -145,6 +179,11 @@ export const createLayoutConfig = (theme: string): FormConfig => [
|
||||
icon: markRaw(AlignItemsSpaceAround),
|
||||
tooltip: '横向平分 space-around',
|
||||
},
|
||||
{ value: 'stretch', icon: markRaw(AlignItemsStretch), tooltip: '拉伸填满 stretch' },
|
||||
{ value: 'baseline', icon: markRaw(AlignItemsBaseline), tooltip: '基线对齐 baseline' },
|
||||
{ value: 'normal', icon: markRaw(AlignItemsNormal), tooltip: '默认 normal' },
|
||||
{ value: 'start', icon: markRaw(AlignItemsStart), tooltip: '起始对齐 start' },
|
||||
{ value: 'end', icon: markRaw(AlignItemsEnd), tooltip: '结束对齐 end' },
|
||||
],
|
||||
display: (_mForm, { model }: { model: Record<any, any> }) => model.display === 'flex',
|
||||
}),
|
||||
@ -222,9 +261,9 @@ export const createLayoutConfig = (theme: string): FormConfig => [
|
||||
items: [
|
||||
{
|
||||
type: 'data-source-field-select',
|
||||
text: '透明度(%)',
|
||||
text: '透明度(0-1)',
|
||||
name: 'opacity',
|
||||
labelWidth: '90px',
|
||||
labelWidth: '100px',
|
||||
dataSourceFieldType: ['string', 'number'],
|
||||
fieldConfig: {
|
||||
type: 'text',
|
||||
@ -445,6 +484,8 @@ export const createBackgroundConfig = (): FormConfig => [
|
||||
icon: markRaw(BackgroundRepeat),
|
||||
tooltip: '垂直和水平方向重复 repeat',
|
||||
},
|
||||
{ value: 'space', icon: markRaw(BackgroundRepeatSpace), tooltip: '不裁剪、均匀留白 space' },
|
||||
{ value: 'round', icon: markRaw(BackgroundRepeatRound), tooltip: '缩放铺满 round' },
|
||||
],
|
||||
}),
|
||||
defineFormItem({
|
||||
@ -502,9 +543,9 @@ export const createFontConfig = (validateDataSourceField: typeof validateDataSou
|
||||
fieldConfig: {
|
||||
type: 'select',
|
||||
allowCreate: true,
|
||||
options: ['normal', 'bold']
|
||||
options: ['normal', 'bold', 'bolder', 'lighter']
|
||||
.concat(
|
||||
Array(7)
|
||||
Array(9)
|
||||
.fill(1)
|
||||
.map((x, i) => `${i + 1}00`),
|
||||
)
|
||||
@ -575,10 +616,16 @@ export const createFontConfig = (validateDataSourceField: typeof validateDataSou
|
||||
type: 'radioGroup',
|
||||
childType: 'button',
|
||||
labelWidth: '68px',
|
||||
// 7 个值统一用图标按钮:带中文文案会因字数不同撑出不同宽度,换行后列线和边框错开。
|
||||
// 中文说明放在 tooltip 里,与 display / justifyContent 等布局选项保持一致。
|
||||
options: [
|
||||
{ value: 'left', icon: markRaw(AlignLeft), tooltip: '左对齐 row', text: '左对齐' },
|
||||
{ value: 'center', icon: markRaw(AlignCenter), tooltip: '居中对齐 center', text: '居中对齐' },
|
||||
{ value: 'right', icon: markRaw(AlignRight), tooltip: '右对齐 right', text: '右对齐' },
|
||||
{ value: 'left', icon: markRaw(AlignLeft), tooltip: '左对齐 left' },
|
||||
{ value: 'center', icon: markRaw(AlignCenter), tooltip: '居中对齐 center' },
|
||||
{ value: 'right', icon: markRaw(AlignRight), tooltip: '右对齐 right' },
|
||||
{ value: 'start', icon: markRaw(AlignStart), tooltip: '起始对齐 start' },
|
||||
{ value: 'end', icon: markRaw(AlignEnd), tooltip: '结束对齐 end' },
|
||||
{ value: 'justify', icon: markRaw(AlignJustify), tooltip: '两端对齐 justify' },
|
||||
{ value: 'match-parent', icon: markRaw(AlignMatchParent), tooltip: '按父级方向对齐 match-parent' },
|
||||
],
|
||||
}),
|
||||
];
|
||||
@ -680,10 +727,12 @@ export const createBorderDirectionConfig = (direction: string) =>
|
||||
type: 'data-source-field-select',
|
||||
fieldConfig: {
|
||||
type: 'select',
|
||||
options: ['solid', 'dashed', 'dotted'].map((item) => ({
|
||||
value: item,
|
||||
text: item,
|
||||
})),
|
||||
options: ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'].map(
|
||||
(item) => ({
|
||||
value: item,
|
||||
text: item,
|
||||
}),
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<rect x="2" y="3" width="2.5" height="8" rx="0.5" />
|
||||
<rect x="6.75" y="6" width="2.5" height="5" rx="0.5" />
|
||||
<rect x="11.5" y="1" width="2.5" height="10" rx="0.5" />
|
||||
<rect x="0.75" y="11.25" width="14.5" height="0.75" rx="0.375" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<rect x="2" y="2" width="9" height="1" rx="0.5" />
|
||||
<path d="M11 0.75L14 2.5 11 4.25Z" />
|
||||
<rect x="3" y="10" width="10" height="4" rx="0.5" fill="none" stroke="currentColor" />
|
||||
<rect x="1" y="14.5" width="14" height="0.5" rx="0.25" fill-opacity="0.3" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<rect x="1" y="1" width="14" height="14" rx="1" fill="none" stroke="currentColor" stroke-opacity="0.3" />
|
||||
<rect x="3" y="5.5" width="10" height="5" rx="0.5" fill="none" stroke="currentColor" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<rect x="2" y="2" width="9" height="1" rx="0.5" />
|
||||
<path d="M11 0.75L14 2.5 11 4.25Z" />
|
||||
<rect x="3" y="6" width="10" height="4" rx="0.5" fill="none" stroke="currentColor" />
|
||||
<rect x="1" y="5" width="14" height="0.5" rx="0.25" fill-opacity="0.3" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<rect x="1" y="1" width="14" height="14" rx="1" fill="none" stroke="currentColor" stroke-opacity="0.3" />
|
||||
<rect x="3" y="2" width="10" height="12" rx="0.5" fill="none" stroke="currentColor" />
|
||||
<path d="M8 4.5L9.75 6.5H8.75V9.5H9.75L8 11.5 6.25 9.5H7.25V6.5H6.25Z" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -1,5 +1,10 @@
|
||||
export { default as AlignItemsBaseline } from './Baseline.vue';
|
||||
export { default as AlignItemsCenter } from './Center.vue';
|
||||
export { default as AlignItemsEnd } from './End.vue';
|
||||
export { default as AlignItemsFlexEnd } from './FlexEnd.vue';
|
||||
export { default as AlignItemsFlexStart } from './FlexStart.vue';
|
||||
export { default as AlignItemsNormal } from './Normal.vue';
|
||||
export { default as AlignItemsSpaceAround } from './SpaceAround.vue';
|
||||
export { default as AlignItemsSpaceBetween } from './SpaceBetween.vue';
|
||||
export { default as AlignItemsStart } from './Start.vue';
|
||||
export { default as AlignItemsStretch } from './Stretch.vue';
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<rect x="1" y="1" width="6.5" height="6.5" rx="0.5" />
|
||||
<rect x="8.5" y="1" width="6.5" height="6.5" rx="0.5" />
|
||||
<rect x="1" y="8.5" width="6.5" height="6.5" rx="0.5" />
|
||||
<rect x="8.5" y="8.5" width="6.5" height="6.5" rx="0.5" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<rect x="0.5" y="0.5" width="15" height="15" rx="1" fill="none" stroke="currentColor" stroke-opacity="0.3" />
|
||||
<rect x="3.5" y="3.5" width="3" height="3" rx="0.5" />
|
||||
<rect x="9.5" y="3.5" width="3" height="3" rx="0.5" />
|
||||
<rect x="3.5" y="9.5" width="3" height="3" rx="0.5" />
|
||||
<rect x="9.5" y="9.5" width="3" height="3" rx="0.5" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -2,3 +2,5 @@ export { default as BackgroundRepeat } from './Repeat.vue';
|
||||
export { default as BackgroundRepeatX } from './RepeatX.vue';
|
||||
export { default as BackgroundRepeatY } from './RepeatY.vue';
|
||||
export { default as BackgroundNoRepeat } from './NoRepeat.vue';
|
||||
export { default as BackgroundRepeatRound } from './Round.vue';
|
||||
export { default as BackgroundRepeatSpace } from './Space.vue';
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M960 128a64 64 0 0 1 64 64v640a64 64 0 0 1-64 64H64a64 64 0 0 1-64-64V192a64 64 0 0 1 64-64h896z m0 64H64v640h896V192zM416 256v512H368V256h48zM656 256v512H608V256h48zM768 368v48H256V368h512zM768 608v48H256V608h512z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M960 128a64 64 0 0 1 64 64v640a64 64 0 0 1-64 64H64a64 64 0 0 1-64-64V192a64 64 0 0 1 64-64h896z m0 64H64v640h896V192zM288 400v48H128V400h160zM288 576v48H128V576h160zM896 400v48H736V400h160zM896 576v48H736V576h160zM704 320v384H320V320h384zM656 368H368v288h288V368zM448 416v192H384V416h64zM544 416v192H480V416h64zM640 416v192H576V416h64z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M960 128a64 64 0 0 1 64 64v640a64 64 0 0 1-64 64H64a64 64 0 0 1-64-64V192a64 64 0 0 1 64-64h896z m0 64H64v640h896V192zM288 400v48H128V400h160zM288 576v48H128V576h160zM896 400v48H736V400h160zM896 576v48H736V576h160zM704 320v384H320V320h384zM656 368H368v288h288V368zM496 384v112H384V384h112zM640 384v112H528V384h112zM496 528v112H384V528h112zM640 528v112H528V528h112z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -1,5 +1,8 @@
|
||||
export { default as DisplayBlock } from './Block.vue';
|
||||
export { default as DisplayFlex } from './Flex.vue';
|
||||
export { default as DisplayGrid } from './Grid.vue';
|
||||
export { default as DisplayInline } from './Inline.vue';
|
||||
export { default as DisplayInlineBlock } from './InlineBlock.vue';
|
||||
export { default as DisplayInlineFlex } from './InlineFlex.vue';
|
||||
export { default as DisplayInlineGrid } from './InlineGrid.vue';
|
||||
export { default as DisplayNone } from './None.vue';
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
||||
<rect x="3" y="4.5" width="1.5" height="7" rx="0.25" fill="none" stroke="currentColor" />
|
||||
<rect x="7.5" y="4.5" width="1.5" height="7" rx="0.25" fill="none" stroke="currentColor" />
|
||||
<rect x="12" y="4.5" width="1.5" height="7" rx="0.25" fill="none" stroke="currentColor" />
|
||||
<rect x="1.25" y="2" width="0.5" height="12" rx="0.25" fill-opacity="0.3" />
|
||||
<rect x="6" y="2" width="0.5" height="12" rx="0.25" fill-opacity="0.3" />
|
||||
<rect x="10.5" y="2" width="0.5" height="12" rx="0.25" fill-opacity="0.3" />
|
||||
<rect x="15" y="2" width="0.5" height="12" rx="0.25" fill-opacity="0.3" />
|
||||
</svg>
|
||||
</template>
|
||||
@ -3,3 +3,4 @@ export { default as JustifyContentFlexEnd } from './FlexEnd.vue';
|
||||
export { default as JustifyContentFlexStart } from './FlexStart.vue';
|
||||
export { default as JustifyContentSpaceAround } from './SpaceAround.vue';
|
||||
export { default as JustifyContentSpaceBetween } from './SpaceBetween.vue';
|
||||
export { default as JustifyContentSpaceEvenly } from './SpaceEvenly.vue';
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M128 96h64v192H128zM224 160h544v64H224zM768 96L896 192 768 288zM256 384h640v64H256zM128 512h768v64H128zM384 640h512v64H384zM192 768h704v64H192z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M128 128h768v85.333333H128z m0 170.666667h768v85.333333H128z m0 170.666666h768v85.333334H128z m0 170.666667h768v85.333333H128z m0 170.666667h768v85.333333H128z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M64 64h896v48H64z m0 848h896v48H64z m0-800h48v800H64z m848 0h48v800h-48zM192 208h416v64H192z m416-48L768 240 608 320zM464 368h96v96h-96z m-64 96h224l-112 128zM192 608h640v56H192z m0 112h512v56H192z m0 112h576v56H192z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path
|
||||
d="M128 96h64v192H128zM224 160h544v64H224zM768 96L896 192 768 288zM128 384h640v64H128zM128 512h768v64H128zM128 640h512v64H128zM128 768h704v64H128z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -1,3 +1,7 @@
|
||||
export { default as AlignLeft } from './Left.vue';
|
||||
export { default as AlignCenter } from './Center.vue';
|
||||
export { default as AlignRight } from './Right.vue';
|
||||
export { default as AlignEnd } from './End.vue';
|
||||
export { default as AlignJustify } from './Justify.vue';
|
||||
export { default as AlignMatchParent } from './MatchParent.vue';
|
||||
export { default as AlignStart } from './Start.vue';
|
||||
|
||||
@ -103,14 +103,14 @@ describe('StyleSetter/Layout.vue', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('display 选项含 inline/flex/block/inline-block/none', () => {
|
||||
test('display 选项含常用布局模式', () => {
|
||||
const wrapper = mount(Layout, {
|
||||
props: { values: { position: 'static', display: 'flex' } } as any,
|
||||
});
|
||||
const configs = wrapper.findAllComponents({ name: 'FakeMContainer' }).map((c) => c.props('config') as any);
|
||||
const displayItem = configs.find((it: any) => it.name === 'display');
|
||||
const values = displayItem.options.map((o: any) => o.value);
|
||||
expect(values).toEqual(['inline', 'flex', 'block', 'inline-block', 'none']);
|
||||
expect(values).toEqual(['inline', 'flex', 'block', 'inline-block', 'none', 'inline-flex', 'grid', 'inline-grid']);
|
||||
});
|
||||
|
||||
test('lastValues/isCompare 透传到 MContainer 与 Box', () => {
|
||||
|
||||
255
packages/editor/tests/unit/fields/StyleSetter/configs.spec.ts
Normal file
255
packages/editor/tests/unit/fields/StyleSetter/configs.spec.ts
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
borderRadiusConfig,
|
||||
createBackgroundConfig,
|
||||
createBorderDirectionConfig,
|
||||
createFontConfig,
|
||||
createLayoutConfig,
|
||||
createPositionConfig,
|
||||
createStyleSetterConfig,
|
||||
transformConfig,
|
||||
} from '@editor/fields/StyleSetter/configs';
|
||||
|
||||
type Item = Record<string, any>;
|
||||
|
||||
const find = (config: Item[], name: string): Item => {
|
||||
const item = config.find((it) => it.name === name);
|
||||
if (!item) throw new Error(`missing config: ${name}`);
|
||||
return item;
|
||||
};
|
||||
|
||||
const values = (options: Item[] = []) => options.map((option) => option.value);
|
||||
|
||||
describe('StyleSetter configs CSS 关键字', () => {
|
||||
const validateDataSourceField = vi.fn(() => undefined);
|
||||
const layout = createLayoutConfig('default') as Item[];
|
||||
const background = createBackgroundConfig() as Item[];
|
||||
const font = createFontConfig(validateDataSourceField as any) as Item[];
|
||||
|
||||
test('textAlign 覆盖 CSS 对齐关键字', () => {
|
||||
expect(values(find(font, 'textAlign').options)).toEqual([
|
||||
'left',
|
||||
'center',
|
||||
'right',
|
||||
'start',
|
||||
'end',
|
||||
'justify',
|
||||
'match-parent',
|
||||
]);
|
||||
|
||||
// 只保留图标,避免字数不同的文案撑出不等宽按钮、换行后边框错位;中文说明留在 tooltip
|
||||
const { options } = find(font, 'textAlign');
|
||||
expect(options.every((option: Item) => Boolean(option.icon) && !option.text && Boolean(option.tooltip))).toBe(true);
|
||||
});
|
||||
|
||||
test('display 覆盖常用布局模式且每项都有图标', () => {
|
||||
expect(find(layout, 'display').options.every((option: Item) => Boolean(option.icon))).toBe(true);
|
||||
expect(values(find(layout, 'display').options)).toEqual([
|
||||
'inline',
|
||||
'flex',
|
||||
'block',
|
||||
'inline-block',
|
||||
'none',
|
||||
'inline-flex',
|
||||
'grid',
|
||||
'inline-grid',
|
||||
]);
|
||||
});
|
||||
|
||||
test('新增的对齐 / 重复选项都使用图标按钮', () => {
|
||||
const fields: [Item[], string][] = [
|
||||
[layout, 'justifyContent'],
|
||||
[layout, 'alignItems'],
|
||||
[background, 'backgroundRepeat'],
|
||||
[font, 'textAlign'],
|
||||
];
|
||||
|
||||
fields.forEach(([config, name]) => {
|
||||
expect(find(config, name).options.every((option: Item) => Boolean(option.icon))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('justifyContent 补 space-evenly,alignItems 补合法对齐值', () => {
|
||||
expect(values(find(layout, 'justifyContent').options)).toEqual([
|
||||
'flex-start',
|
||||
'flex-end',
|
||||
'center',
|
||||
'space-between',
|
||||
'space-around',
|
||||
'space-evenly',
|
||||
]);
|
||||
expect(values(find(layout, 'alignItems').options)).toEqual([
|
||||
'flex-start',
|
||||
'flex-end',
|
||||
'center',
|
||||
'space-between',
|
||||
'space-around',
|
||||
'stretch',
|
||||
'baseline',
|
||||
'normal',
|
||||
'start',
|
||||
'end',
|
||||
]);
|
||||
});
|
||||
|
||||
test('flexDirection / flexWrap 保持完整', () => {
|
||||
expect(values(find(layout, 'flexDirection').options)).toEqual(['row', 'row-reverse', 'column', 'column-reverse']);
|
||||
expect(values(find(layout, 'flexWrap').options)).toEqual(['nowrap', 'wrap', 'wrap-reverse']);
|
||||
});
|
||||
|
||||
test('backgroundRepeat 补 space / round', () => {
|
||||
expect(values(find(background, 'backgroundRepeat').options)).toEqual([
|
||||
'no-repeat',
|
||||
'repeat-x',
|
||||
'repeat-y',
|
||||
'repeat',
|
||||
'space',
|
||||
'round',
|
||||
]);
|
||||
expect(values(find(background, 'backgroundSize').options)).toEqual(['auto', 'contain', 'cover']);
|
||||
});
|
||||
|
||||
test('fontWeight 覆盖 100-900 与相对关键字', () => {
|
||||
expect(values(find(font, 'fontWeight').fieldConfig.options)).toEqual([
|
||||
'normal',
|
||||
'bold',
|
||||
'bolder',
|
||||
'lighter',
|
||||
...Array(9)
|
||||
.fill(1)
|
||||
.map((_x, i) => `${i + 1}00`),
|
||||
]);
|
||||
});
|
||||
|
||||
test('borderStyle 覆盖全部 CSS 线型', () => {
|
||||
const border = createBorderDirectionConfig('') as Item;
|
||||
const styleItem = border.items.find((it: Item) => it.name === 'borderStyle');
|
||||
expect(values(styleItem.fieldConfig.options)).toEqual([
|
||||
'none',
|
||||
'hidden',
|
||||
'dotted',
|
||||
'dashed',
|
||||
'solid',
|
||||
'double',
|
||||
'groove',
|
||||
'ridge',
|
||||
'inset',
|
||||
'outset',
|
||||
]);
|
||||
});
|
||||
|
||||
test('overflow / position 的关键字保持完整', () => {
|
||||
const overflow = layout.find((it) => it.type === 'row' && it.items?.[0]?.name === 'overflow');
|
||||
expect(values(overflow.items[0].fieldConfig.options)).toEqual([
|
||||
'visible',
|
||||
'hidden',
|
||||
'clip',
|
||||
'scroll',
|
||||
'auto',
|
||||
'overlay',
|
||||
'initial',
|
||||
]);
|
||||
|
||||
const position = createPositionConfig({ position: 'static' }) as Item[];
|
||||
expect(values(find(position, 'position').fieldConfig.options)).toEqual([
|
||||
'static',
|
||||
'relative',
|
||||
'absolute',
|
||||
'fixed',
|
||||
'sticky',
|
||||
]);
|
||||
});
|
||||
|
||||
test('flex 相关字段仅在 display 为 flex 时展示', () => {
|
||||
['flexDirection', 'justifyContent', 'alignItems', 'flexWrap'].forEach((name) => {
|
||||
expect(find(layout, name).display(null, { model: { display: 'flex' } })).toBe(true);
|
||||
expect(find(layout, name).display(null, { model: { display: 'block' } })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('position 为 static 时隐藏方位字段', () => {
|
||||
const position = createPositionConfig({ position: 'static' }) as Item[];
|
||||
expect(position.filter((it) => it.type === 'row').every((it) => it.display() === false)).toBe(true);
|
||||
});
|
||||
|
||||
test('backgroundSize 校验覆盖空值、关键字、多值与非法值', () => {
|
||||
const { validator } = find(background, 'backgroundSize').rules[1];
|
||||
const check = (value: any) => {
|
||||
const callback = vi.fn();
|
||||
validator({ value, callback }, {}, null);
|
||||
return callback;
|
||||
};
|
||||
|
||||
expect(check('')).toHaveBeenCalledWith();
|
||||
expect(check(null)).toHaveBeenCalledWith();
|
||||
expect(check('cover')).toHaveBeenCalledWith();
|
||||
expect(check('inherit')).toHaveBeenCalledWith();
|
||||
expect(check('100px 50%')).toHaveBeenCalledWith();
|
||||
expect(check('cover contain')).toHaveBeenCalledWith('cover/contain 不能与其他值组合');
|
||||
expect(check('10px 20px 30px')).toHaveBeenCalledWith('backgroundSize 最多支持两个值');
|
||||
expect(check('foo')).toHaveBeenCalledWith('backgroundSize 值不合法');
|
||||
});
|
||||
|
||||
test('fontWeight 校验委托给 validateDataSourceField 并校验普通值', () => {
|
||||
const validate = vi.fn((value, _ctx, opts) => opts.validatePlainValue(value));
|
||||
const { validator } = find(createFontConfig(validate as any) as Item[], 'fontWeight').rules[1];
|
||||
const ctx = { config: {}, model: {}, prop: '' };
|
||||
const check = (value: any) => {
|
||||
const callback = vi.fn();
|
||||
validator({ value, callback }, ctx, null);
|
||||
return callback;
|
||||
};
|
||||
|
||||
expect(check('')).toHaveBeenCalledWith();
|
||||
expect(check('700')).toHaveBeenCalledWith(undefined);
|
||||
expect(check(700)).toHaveBeenCalledWith(undefined);
|
||||
expect(check({})).toHaveBeenCalledWith('字重应为字符串或数字');
|
||||
expect(validate).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test('fontWeight 校验支持异步校验结果', async () => {
|
||||
const validate = vi.fn().mockResolvedValueOnce(undefined).mockRejectedValueOnce('数据源错误');
|
||||
const { validator } = find(createFontConfig(validate as any) as Item[], 'fontWeight').rules[1];
|
||||
const ctx = { config: {}, model: {}, prop: '' };
|
||||
|
||||
const resolved = vi.fn();
|
||||
validator({ value: '700', callback: resolved }, ctx, null);
|
||||
await vi.waitFor(() => expect(resolved).toHaveBeenCalledWith(undefined));
|
||||
|
||||
const rejected = vi.fn();
|
||||
validator({ value: '700', callback: rejected }, ctx, null);
|
||||
await vi.waitFor(() => expect(rejected).toHaveBeenCalledWith('数据源错误'));
|
||||
});
|
||||
|
||||
test('合成配置按面板顺序包含各字段', () => {
|
||||
const config = createStyleSetterConfig({}, 'default', validateDataSourceField as any) as Item[];
|
||||
const names = config.flatMap((it) => (it.name ? [it.name] : (it.items || []).map((sub: Item) => sub.name)));
|
||||
|
||||
expect(names).toContain('display');
|
||||
expect(names).toContain('opacity');
|
||||
expect(names).toContain('position');
|
||||
expect(names).toContain('backgroundRepeat');
|
||||
expect(names).toContain('fontWeight');
|
||||
expect(names).toContain('textAlign');
|
||||
expect(borderRadiusConfig.name).toBe('borderRadius');
|
||||
expect(transformConfig.items.map((it: Item) => it.name)).toEqual(['rotate', 'scale']);
|
||||
});
|
||||
});
|
||||
@ -6,6 +6,11 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
|
||||
import AiBaseline from '@editor/fields/StyleSetter/icons/align-items/Baseline.vue';
|
||||
import AiEnd from '@editor/fields/StyleSetter/icons/align-items/End.vue';
|
||||
import AiNormal from '@editor/fields/StyleSetter/icons/align-items/Normal.vue';
|
||||
import AiStart from '@editor/fields/StyleSetter/icons/align-items/Start.vue';
|
||||
import AiStretch from '@editor/fields/StyleSetter/icons/align-items/Stretch.vue';
|
||||
import BgPosLeftBottom from '@editor/fields/StyleSetter/icons/background-position/LeftBottom.vue';
|
||||
import BgPosLeftCenter from '@editor/fields/StyleSetter/icons/background-position/LeftCenter.vue';
|
||||
import BgPosLeftTop from '@editor/fields/StyleSetter/icons/background-position/LeftTop.vue';
|
||||
@ -13,10 +18,15 @@ import NoRepeat from '@editor/fields/StyleSetter/icons/background-repeat/NoRepea
|
||||
import Repeat from '@editor/fields/StyleSetter/icons/background-repeat/Repeat.vue';
|
||||
import RepeatX from '@editor/fields/StyleSetter/icons/background-repeat/RepeatX.vue';
|
||||
import RepeatY from '@editor/fields/StyleSetter/icons/background-repeat/RepeatY.vue';
|
||||
import BgRound from '@editor/fields/StyleSetter/icons/background-repeat/Round.vue';
|
||||
import BgSpace from '@editor/fields/StyleSetter/icons/background-repeat/Space.vue';
|
||||
import DisplayBlock from '@editor/fields/StyleSetter/icons/display/Block.vue';
|
||||
import DisplayFlex from '@editor/fields/StyleSetter/icons/display/Flex.vue';
|
||||
import DisplayGrid from '@editor/fields/StyleSetter/icons/display/Grid.vue';
|
||||
import DisplayInline from '@editor/fields/StyleSetter/icons/display/Inline.vue';
|
||||
import DisplayInlineBlock from '@editor/fields/StyleSetter/icons/display/InlineBlock.vue';
|
||||
import DisplayInlineFlex from '@editor/fields/StyleSetter/icons/display/InlineFlex.vue';
|
||||
import DisplayInlineGrid from '@editor/fields/StyleSetter/icons/display/InlineGrid.vue';
|
||||
import DisplayNone from '@editor/fields/StyleSetter/icons/display/None.vue';
|
||||
import FdColumn from '@editor/fields/StyleSetter/icons/flex-direction/Column.vue';
|
||||
import FdColumnReverse from '@editor/fields/StyleSetter/icons/flex-direction/ColumnReverse.vue';
|
||||
@ -27,12 +37,22 @@ import JcFlexEnd from '@editor/fields/StyleSetter/icons/justify-content/FlexEnd.
|
||||
import JcFlexStart from '@editor/fields/StyleSetter/icons/justify-content/FlexStart.vue';
|
||||
import JcSpaceAround from '@editor/fields/StyleSetter/icons/justify-content/SpaceAround.vue';
|
||||
import JcSpaceBetween from '@editor/fields/StyleSetter/icons/justify-content/SpaceBetween.vue';
|
||||
import JcSpaceEvenly from '@editor/fields/StyleSetter/icons/justify-content/SpaceEvenly.vue';
|
||||
import TaCenter from '@editor/fields/StyleSetter/icons/text-align/Center.vue';
|
||||
import TaEnd from '@editor/fields/StyleSetter/icons/text-align/End.vue';
|
||||
import TaJustify from '@editor/fields/StyleSetter/icons/text-align/Justify.vue';
|
||||
import TaLeft from '@editor/fields/StyleSetter/icons/text-align/Left.vue';
|
||||
import TaMatchParent from '@editor/fields/StyleSetter/icons/text-align/MatchParent.vue';
|
||||
import TaRight from '@editor/fields/StyleSetter/icons/text-align/Right.vue';
|
||||
import TaStart from '@editor/fields/StyleSetter/icons/text-align/Start.vue';
|
||||
|
||||
describe('StyleSetter icons', () => {
|
||||
const icons = [
|
||||
['AiBaseline', AiBaseline],
|
||||
['AiEnd', AiEnd],
|
||||
['AiNormal', AiNormal],
|
||||
['AiStart', AiStart],
|
||||
['AiStretch', AiStretch],
|
||||
['BgPosLeftBottom', BgPosLeftBottom],
|
||||
['BgPosLeftCenter', BgPosLeftCenter],
|
||||
['BgPosLeftTop', BgPosLeftTop],
|
||||
@ -40,10 +60,15 @@ describe('StyleSetter icons', () => {
|
||||
['Repeat', Repeat],
|
||||
['RepeatX', RepeatX],
|
||||
['RepeatY', RepeatY],
|
||||
['BgRound', BgRound],
|
||||
['BgSpace', BgSpace],
|
||||
['DisplayBlock', DisplayBlock],
|
||||
['DisplayFlex', DisplayFlex],
|
||||
['DisplayGrid', DisplayGrid],
|
||||
['DisplayInline', DisplayInline],
|
||||
['DisplayInlineBlock', DisplayInlineBlock],
|
||||
['DisplayInlineFlex', DisplayInlineFlex],
|
||||
['DisplayInlineGrid', DisplayInlineGrid],
|
||||
['DisplayNone', DisplayNone],
|
||||
['FdColumn', FdColumn],
|
||||
['FdColumnReverse', FdColumnReverse],
|
||||
@ -54,9 +79,14 @@ describe('StyleSetter icons', () => {
|
||||
['JcFlexStart', JcFlexStart],
|
||||
['JcSpaceAround', JcSpaceAround],
|
||||
['JcSpaceBetween', JcSpaceBetween],
|
||||
['JcSpaceEvenly', JcSpaceEvenly],
|
||||
['TaCenter', TaCenter],
|
||||
['TaEnd', TaEnd],
|
||||
['TaJustify', TaJustify],
|
||||
['TaLeft', TaLeft],
|
||||
['TaMatchParent', TaMatchParent],
|
||||
['TaRight', TaRight],
|
||||
['TaStart', TaStart],
|
||||
];
|
||||
|
||||
test.each(icons)('%s 渲染 svg', (_name, comp) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user