Skip to content

Commit

Permalink
feat(pro:form): add ProForm component
Browse files Browse the repository at this point in the history
  • Loading branch information
danranVm committed Jul 20, 2022
1 parent bdb96df commit 30e56aa
Show file tree
Hide file tree
Showing 48 changed files with 1,253 additions and 41 deletions.
2 changes: 1 addition & 1 deletion packages/cdk/forms/src/messages/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const enUSMessages = {
return `Please enter a number no greater than ${err.max}`
},
range: (err: Omit<ValidateError, 'message'>, __: AbstractControl): string => {
return `Please enter a number between ${err.min - 1}-${err.max + 1}`
return `Please enter a number between ${err.min}-${err.max}`
},
minLength: (err: Omit<ValidateError, 'message'>, __: AbstractControl): string => {
const { minLength, isArray } = err
Expand Down
6 changes: 3 additions & 3 deletions packages/cdk/forms/src/messages/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ export const zhCNMessages = {
return `请输入正确的邮箱格式${example ? ', 例: ' + example : ''}`
},
min: (err: Omit<ValidateError, 'message'>, __: AbstractControl): string => {
return `请输入一个不小于 ${err.min} 的数字`
return `请输入不小于 ${err.min} 的数字`
},
max: (err: Omit<ValidateError, 'message'>, __: AbstractControl): string => {
return `请输入一个不大于 ${err.max} 的数字`
return `请输入不大于 ${err.max} 的数字`
},
range: (err: Omit<ValidateError, 'message'>, __: AbstractControl): string => {
return `请输入一个 ${err.min - 1}-${err.max + 1} 之间的数字`
return `请输入 ${err.min}-${err.max} 之间的数字`
},
minLength: (err: Omit<ValidateError, 'message'>, __: AbstractControl): string => {
const { minLength, isArray } = err
Expand Down
7 changes: 6 additions & 1 deletion packages/cdk/forms/src/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
AsyncValidatorFn,
ValidateError,
ValidateErrors,
ValidateMessage,
ValidateMessageFn,
ValidateMessages,
ValidatorFn,
Expand All @@ -35,6 +36,10 @@ export class Validators {
Validators.messages = { ...Validators.messages, ...messages }
}

static getMessage(key: string): ValidateMessage | undefined {
return Validators.messages[key]
}

static getError(
key: string,
control: AbstractControl,
Expand Down Expand Up @@ -207,7 +212,7 @@ export class Validators {

/** checks whether string or array is empty */
function isEmpty(val: any): boolean {
return isNil(val) || (val.length === 0 && (isString(val) || isArray(val)))
return isNil(val) || ((isString(val) || isArray(val)) && val.length === 0)
}

/** checks whether variable has length props */
Expand Down
4 changes: 2 additions & 2 deletions packages/components/checkbox/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ export const checkboxProps = {
size: { type: String as PropType<FormSize>, default: undefined },

// events
'onUpdate:checked': { type: [Function, Array] as PropType<MaybeArray<<K = CheckValue>(checked: K) => void>> },
'onUpdate:checked': { type: [Function, Array] as PropType<MaybeArray<(checked: any) => void>> },
onChange: {
type: [Function, Array] as PropType<MaybeArray<<K = CheckValue>(newChecked: K, oldChecked: K) => void>>,
type: [Function, Array] as PropType<MaybeArray<(newChecked: any, oldChecked: any) => void>>,
},
onBlur: { type: [Function, Array] as PropType<MaybeArray<(evt: FocusEvent) => void>> },
onFocus: { type: [Function, Array] as PropType<MaybeArray<(evt: FocusEvent) => void>> },
Expand Down
2 changes: 1 addition & 1 deletion packages/components/config/__tests__/globalConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const ParentComponent = {
components: { ChildComponent },
template: `<ChildComponent />`,
setup() {
const [, changeConfig] = useGlobalConfig('form', true)
const [, changeConfig] = useGlobalConfig('form', {})
return { changeConfig }
},
}
Expand Down
6 changes: 2 additions & 4 deletions packages/components/form/demo/CustomizedValidation.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ order: 7

## zh

我们提供了 `status`, `hasFeedback` `message` 等属性,你可以不通过 `control` 来设置验证状态和提示信息。
我们提供了 `status`, 和 `message` 等属性,你可以不通过 `control` 来设置验证状态和提示信息。

- `status`: 校验状态, 共3种: `valid`, `validating`, `invalid`
- `hasFeedback`: 添加反馈图标。
- `message`: 设置提示信息。

## en

We provide properties like `status`, `hasFeedback` and `message` to customize validate status and message, without using `control`.
We provide properties like `status` and `message` to customize validate status and message, without using `control`.

- `status`: validate status of form components which could be `valid`, `validating` and `invalid`.
- `hasFeedback`: display feed icon of input control
- `message`: display validate message.
11 changes: 6 additions & 5 deletions packages/components/form/demo/CustomizedValidation.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<template>
<IxForm class="demo-form" hasFeedback labelCol="6">
<IxFormItem label="Valid" status="valid" :message="messageMap">
<IxForm class="demo-form" labelCol="6">
<IxFormItem label="Valid" status="valid" :message="messageMap.valid">
<IxInput v-model:value="formValue.valid"></IxInput>
</IxFormItem>
<IxFormItem label="Validating" status="validating" :message="messageMap">
<IxFormItem label="Validating" status="validating" :message="messageMap.validating">
<IxInput v-model:value="formValue.validating"></IxInput>
</IxFormItem>
<IxFormItem label="Invalid" status="invalid" :message="messageMap">
<IxFormItem label="Invalid" status="invalid" :message="messageMap.invalid">
<IxInput v-model:value="formValue.invalid"></IxInput>
</IxFormItem>
<IxFormItem label="Dynamic" :status="status" :message="messageMap">
<IxFormItem label="Dynamic" :status="status" :message="getMessage">
<IxInput v-model:value="formValue.dynamic"></IxInput>
</IxFormItem>
<IxFormItem :controlCol="{ offset: 6 }">
Expand Down Expand Up @@ -39,6 +39,7 @@ const messageMap = {
validating: 'This is validating field!',
invalid: 'This is invalid field!',
}
const getMessage = () => messageMap[status.value]
const changeStatus = () => {
const currStatus = status.value
Expand Down
2 changes: 1 addition & 1 deletion packages/components/form/demo/DynamicItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { useFormArray, useFormControl, useFormGroup } from '@idux/cdk/forms'
interface FormValue {
array: string[]
[key: string]: any
[key: string]: unknown
}
const formArray = useFormArray([['']])
Expand Down
2 changes: 1 addition & 1 deletion packages/components/form/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ order: 0
| `labelTooltip` | 配置表单文本的提示信息 | `string \| #labelTooltip` | - | - | 通常用于对表单本文的解释说名 |
| `labelTooltipIcon` | 配置表单文本的提示信息icon | `string` | - | -| - |
| `required` | 必填样式设置 | `boolean` | `false` | - | 仅控制样式 |
| `message` | 手动指定表单项的校验提示 | `string \| (control?: AbstractControl) => string \| FormValidateMessage` | - | - | 传入 `string` 时,为 `invalid` 状态的提示 |
| `message` | 手动指定表单项的校验提示 | `string \| (control?: AbstractControl) => string` | - | - | 传入 `string` 时,为 `invalid` 状态的提示 |
| `status` | 手动指定表单项的校验状态 | `valid \| invalid \| validating` | - | - | - |

### IxFormWrapper
Expand Down
1 change: 0 additions & 1 deletion packages/components/form/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export type {
FormColType,
FormLabelAlign,
FormLayout,
FormValidateMessage,
FormSize,
} from './src/types'

Expand Down
10 changes: 5 additions & 5 deletions packages/components/form/src/FormItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ function renderControl(
extra: extraSlot,
extraMessage: extraMessageSlot,
description: descriptionSlot,
message: messageSlot,
} = slots
if (__DEV__ && (extra || extraSlot)) {
Logger.warn('components/form', '`extra` was deprecated, please use `description` instead.')
Expand All @@ -127,20 +128,19 @@ function renderControl(
<IxIcon name={statusIcon.value} />
</span>
)
const messageNode = message.value && <div class={`${prefixCls}-message`}>{message.value}</div>
const tooltipNode = renderTooltip(controlTooltipSlot, controlTooltip, controlTooltipIcon.value)
const messageNode = messageSlot ? messageSlot(message.value) : message.value
const _descriptionSlot = extraSlot || extraMessageSlot || descriptionSlot
const descriptionNode = _descriptionSlot ? _descriptionSlot() : extra || extraMessage || description
const descriptionWrapper = descriptionNode && <div class={`${prefixCls}-description`}>{descriptionNode}</div>
const tooltipNode = renderTooltip(controlTooltipSlot, controlTooltip, controlTooltipIcon.value)
return (
<IxCol class={`${prefixCls}-control`} {...controlColConfig.value}>
<div class={`${prefixCls}-control-input`}>
<div class={`${prefixCls}-control-input-content`}>{slots.default && slots.default()}</div>
{statusNode}
{tooltipNode && <span class={`${prefixCls}-control-tooltip`}>{tooltipNode}</span>}
</div>
{messageNode}
{descriptionWrapper}
{message.value && <div class={`${prefixCls}-message`}>{messageNode}</div>}
{descriptionNode && <div class={`${prefixCls}-description`}>{descriptionNode}</div>}
</IxCol>
)
}
Expand Down
3 changes: 3 additions & 0 deletions packages/components/form/src/composables/useFormItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ function useMessage(
status: ComputedRef<ValidateStatus | undefined>,
) {
const locale = useGlobalConfig('locale')
/**
* @deprecated
*/
const messages = computed(() => {
const message = props.message
return isString(message) || isFunction(message) ? { invalid: message } : message || {}
Expand Down
15 changes: 7 additions & 8 deletions packages/components/form/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import type { AbstractControl, ValidateStatus } from '@idux/cdk/forms'
import type { ExtractInnerPropTypes, ExtractPublicPropTypes } from '@idux/cdk/utils'
import type { ColProps } from '@idux/components/grid'
import type { ColProps, RowProps } from '@idux/components/grid'
import type { DefineComponent, HTMLAttributes, PropType } from 'vue'

const colProp = [Number, String, Object] as PropType<number | string | ColProps>
Expand Down Expand Up @@ -49,7 +49,6 @@ export type FormInstance = InstanceType<DefineComponent<FormProps>>
export type FormColType = number | string | ColProps
export type FormLabelAlign = 'start' | 'end'
export type FormLayout = 'horizontal' | 'vertical' | `inline`
export type FormValidateMessage = Partial<Record<ValidateStatus, string | ((control: AbstractControl) => string)>>
export type FormSize = 'sm' | 'md' | 'lg'

export const formItemProps = {
Expand Down Expand Up @@ -77,19 +76,17 @@ export const formItemProps = {
* @deprecated Use `description` instead.
*/
extraMessage: String,
label: String,
label: [String, Number] as PropType<string | number>,
labelAlign: String as PropType<FormLabelAlign>,
labelCol: colProp,
labelFor: String,
labelFor: [String, Number] as PropType<string | number>,
labelTooltip: String,
labelTooltipIcon: String,
required: {
type: Boolean,
default: false,
},
message: [String, Function, Object] as PropType<
string | ((control: AbstractControl) => string) | FormValidateMessage
>,
message: [String, Function, Object] as PropType<string | ((control: AbstractControl) => string)>,
status: String as PropType<ValidateStatus>,
/**
* @deprecated
Expand All @@ -105,7 +102,9 @@ export type FormItemPublicProps = Omit<
ExtractPublicPropTypes<typeof formItemProps>,
'extra' | 'extraMessage' | 'hasFeedback' | 'statusIcon'
>
export type FormItemComponent = DefineComponent<Omit<HTMLAttributes, keyof FormItemPublicProps> & FormItemPublicProps>
export type FormItemComponent = DefineComponent<
Omit<HTMLAttributes, keyof FormItemPublicProps> & FormItemPublicProps & RowProps
>
export type FormItemInstance = InstanceType<DefineComponent<FormItemProps>>

export const formWrapperProps = {
Expand Down
1 change: 1 addition & 0 deletions packages/components/modal/src/ModalWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ function watchVisibleChange(
wrapperRef: Ref<HTMLDivElement | undefined>,
sentinelStartRef: Ref<HTMLDivElement | undefined>,
mask: ComputedRef<boolean>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
draggableResult: Ref<any>,
) {
let lastOutSideActiveElement: HTMLElement | null = null
Expand Down
1 change: 0 additions & 1 deletion packages/components/table/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ export type TableColumn<T = any, V = any> =
| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `type` | 列类型 | `'expandable'` | - | - | 必填 |
| `disabled` | 设置是否允许行展开 | `(record:T) => boolean` | - | - | - |
| `icon` | 展开按钮图标 | `string` | `'right'` | ✅ | - |
| `indent` | 展示树形数据时,每层缩进的宽度 | `number` | `12` | - | - |
Expand Down
16 changes: 15 additions & 1 deletion packages/pro/config/src/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,28 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { IxInput } from '@idux/components/input'
import { type ProFormJsonSchema } from '@idux/pro/form'
import { zhCN } from '@idux/pro/locales'

import { type ProGlobalConfig } from './types'

export const defaultConfig: ProGlobalConfig = {
common: { prefixCls: 'ix-pro' },
locale: zhCN,

form: {
ajvOptions: {
allErrors: true,
loopEnum: 50,
},
autoId: true,
autoLabelFor: true,
formatComponents: {
default: { component: IxInput },
},
ignoreKeywords: ['type', 'enum'],
schemaFormatter: (fields, schema) => ({ fields: fields || {}, schema: schema || ({} as ProFormJsonSchema) }),
},
table: {
columnIndexable: {
align: 'center',
Expand Down
43 changes: 42 additions & 1 deletion packages/pro/config/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

/* eslint-disable @typescript-eslint/no-explicit-any */

import type { PortalTargetType } from '@idux/cdk/portal'
import type { ProFormSchemaFormatter } from '@idux/pro/form'
import type { ProLocale } from '@idux/pro/locales'
import type { ProTableColumnIndexable } from '@idux/pro/table'
import type { VNode } from 'vue'
import type { Options as AjvOptions } from 'ajv'
import type { Component, VNode } from 'vue'

export interface ProGlobalConfig {
common: ProCommonConfig
locale: ProLocale

form: ProFormConfig
table: ProTableConfig
tree: ProTreeConfig
search: ProSearchConfig
Expand All @@ -25,6 +30,42 @@ export interface ProCommonConfig {
prefixCls: string
}

interface ProFormConfigFormatComponent {
component?: string | Component
componentProps?: any
}

export interface ProFormConfig {
/**
* [ajv](https://ajv.js.org/options.html) 参数
*/
ajvOptions: AjvOptions
autoId: boolean
autoLabelFor: boolean
/**
* 根据 format 字段渲染的组件
*
* @example { 'date': { component: IxDatePicker, componentProps: { format: 'dd/MM/yyyy' }}}
*/
formatComponents: {
default?: ProFormConfigFormatComponent
data?: ProFormConfigFormatComponent
time?: ProFormConfigFormatComponent
'date-time'?: ProFormConfigFormatComponent
uri?: ProFormConfigFormatComponent
email?: ProFormConfigFormatComponent
ipv4?: ProFormConfigFormatComponent
ipv6?: ProFormConfigFormatComponent
[key: string]: ProFormConfigFormatComponent | undefined
}
/**
* 忽略某些数据类型 (type) 的校验, 默认:['type', 'enum']
*/
ignoreKeywords: string[]

schemaFormatter: ProFormSchemaFormatter
}

export interface ProTableConfig {
columnIndexable: Omit<ProTableColumnIndexable, 'type'>
}
Expand Down
4 changes: 3 additions & 1 deletion packages/pro/default.less
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
@import './form/style/themes/default.less';
@import './layout/style/themes/default.less';
@import './search/style/themes/default.less';
@import './table/style/themes/default.less';
@import './transfer/style/themes/default.less';
@import './tree/style/themes/default.less';
@import './search/style/themes/default.less';

25 changes: 25 additions & 0 deletions packages/pro/form/__tests__/proForm.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { MountingOptions, mount } from '@vue/test-utils'

import { renderWork } from '@tests'

import ProForm from '../src/ProForm'
import { ProFormProps } from '../src/types'

describe.skip('ProForm', () => {
const ProFormMount = (options?: MountingOptions<Partial<ProFormProps>>) =>
mount(ProForm, { ...(options as MountingOptions<ProFormProps>) })

renderWork<ProFormProps>(ProForm, {
props: {},
})

test('xxx work', async () => {
const wrapper = ProFormMount({ props: { xxx: 'Xxx' } })

expect(wrapper.classes()).toContain('ix-Xxx')

await wrapper.setProps({ xxx: 'Yyy' })

expect(wrapper.classes()).toContain('ix-Yyy')
})
})
Loading

0 comments on commit 30e56aa

Please sign in to comment.