Skip to content

Commit

Permalink
feat(comp:form): support for displaying message in tooltip (#1047)
Browse files Browse the repository at this point in the history
* feat(comp:form): support message in tooltip

* test(comp:form): update test
  • Loading branch information
danranVm committed Jul 29, 2022
1 parent 0057697 commit 67222d1
Show file tree
Hide file tree
Showing 24 changed files with 380 additions and 147 deletions.
6 changes: 3 additions & 3 deletions packages/cdk/popper/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ export type PopperTrigger = 'click' | 'hover' | 'focus' | 'contextmenu' | 'manua
| `destroy` | 销毁浮层 | `(): void` | - | - | - |
| `visibility` | 浮层显示状态 | `ComputedRef<boolean>` | -| - | - |
| `placement` | 浮层位置 | `ComputedRef<PopperPlacement>` | - | - | - |
| `triggerRef` | 浮层的触发元素 | `Ref<TE \| null>` | - | - |
| `triggerRef` | 浮层的触发元素 | `Ref<TE \| undefined>` | - | - |
| `triggerEvents` | 浮层触发元素的事件 | `ComputedRef<PopperTriggerEvents>` | - | - | 需要手动绑定到触发元素上 |
| `popperRef` | 浮层的容器元素 | `Ref<TE \| null>` | - | - |
| `popperRef` | 浮层的容器元素 | `Ref<TE \| undefined>` | - | - |
| `popperEvents` | 浮层容器容器的事件 | `ComputedRef<PopperEvents>` | - | - | 需要手动绑定到浮层容器元素上 |
| `arrowRef` | 浮层的箭头元素 | `Ref<HTMLElement \| null>` | - | - | - |
| `arrowRef` | 浮层的箭头元素 | `Ref<HTMLElement \| undefined>` | - | - | - |

```ts
Expand Down
9 changes: 2 additions & 7 deletions packages/cdk/popper/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,15 @@
*/

import type { PopperEvents, PopperOptions, PopperPlacement, PopperTriggerEvents } from './types'
import type { ComputedRef, Ref } from 'vue'
import type { ComputedRef } from 'vue'

import { computed, reactive, ref, watch } from 'vue'

import { NoopFunction, NoopObject } from '@idux/cdk/utils'

export function useElement<T>(): Ref<T | null> {
const element: Ref<T | null> = ref(null)
return element
}

const defaultDelay = 0

export function useState(options: PopperOptions): Required<PopperOptions> {
export function useReactiveOptions(options: PopperOptions): Required<PopperOptions> {
const {
allowEnter = true,
autoAdjust = true,
Expand Down
6 changes: 3 additions & 3 deletions packages/cdk/popper/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export interface PopperInstance<TE extends PopperElement = PopperElement, PE ext
* The truth DOM node of the trigger.
* The caller needs to bind the variable to the view.
*/
triggerRef: Ref<TE | null>
triggerRef: Ref<TE | undefined>
/**
* Manually bind to the event on the trigger.
*/
Expand All @@ -145,7 +145,7 @@ export interface PopperInstance<TE extends PopperElement = PopperElement, PE ext
* The truth DOM node of the popper.
* The caller needs to bind the variable to the view.
*/
popperRef: Ref<PE | null>
popperRef: Ref<PE | undefined>
/**
* Manually bind to events on the popper.
*/
Expand All @@ -154,5 +154,5 @@ export interface PopperInstance<TE extends PopperElement = PopperElement, PE ext
* The truth DOM node of the arrow.
* The caller needs to bind the variable to the view.
*/
arrowRef: Ref<HTMLElement | null>
arrowRef: Ref<HTMLElement | undefined>
}
33 changes: 17 additions & 16 deletions packages/cdk/popper/src/usePopper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,36 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { PopperElement, PopperInstance, PopperOptions } from './types'
import type { Instance } from '@popperjs/core'
import { type WatchStopHandle, ref, watch } from 'vue'

import { WatchStopHandle, watch } from 'vue'

import { createPopper } from '@popperjs/core'
import { type Instance, createPopper } from '@popperjs/core'
import { isEqual } from 'lodash-es'

import { convertElement } from '@idux/cdk/utils'

import {
useBaseOptions,
useDelay,
useElement,
usePlacement,
usePopperEvents,
useState,
useReactiveOptions,
useTimer,
useTriggerEvents,
useVisibility,
} from './hooks'
import { type PopperElement, type PopperInstance, type PopperOptions } from './types'
import { convertOptions } from './utils'

export function usePopper<TE extends PopperElement = PopperElement, PE extends PopperElement = PopperElement>(
options: PopperOptions = {},
): PopperInstance<TE, PE> {
let popperInstance: Instance | null = null

const triggerRef = useElement<TE>()
const popperRef = useElement<PE>()
const arrowRef = useElement<HTMLElement>()
const triggerRef = ref<TE>()
const popperRef = ref<PE>()
const arrowRef = ref<HTMLElement>()

const state = useState(options)
const state = useReactiveOptions(options)
const baseOptions = useBaseOptions(state)
const visibility = useVisibility(state)
const { placement, updatePlacement } = usePlacement(state)
Expand Down Expand Up @@ -126,11 +123,15 @@ export function usePopper<TE extends PopperElement = PopperElement, PE extends P
}
})

watch([baseOptions, arrowRef], ([currBaseOptions, arrowElement]) => {
popperInstance?.setOptions(
convertOptions(currBaseOptions, { arrowElement: convertElement(arrowElement), updatePlacement }),
)
})
watch(
[baseOptions, arrowRef],
([currBaseOptions, arrowElement]) => {
popperInstance?.setOptions(
convertOptions(currBaseOptions, { arrowElement: convertElement(arrowElement), updatePlacement }),
)
},
{ flush: 'post' },
)

return {
visibility,
Expand Down
20 changes: 14 additions & 6 deletions packages/components/_private/overlay/src/Overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,24 @@ export default defineComponent({
}

return () => {
const triggerNode = getFirstValidNode(slots.default?.())
if (!triggerNode) {
const triggerNodes = slots.default?.()
const triggerNode = getFirstValidNode(triggerNodes)
if (!triggerNode || triggerNodes!.length > 1) {
__DEV__ && Logger.warn('components/overlay', 'Trigger must is single rooted node')
return null
}
const trigger = renderTrigger(props, triggerNode, { ref: triggerRef, ...triggerEvents.value }, handleClickOutside)
const contentNode = slots.content?.()
if (!getFirstValidNode(contentNode)) {
return triggerNode
// 避免没有 content 时, trigger 被重新创建
return (
<>
{trigger}
<CdkPortal target={props.target} load={false}></CdkPortal>
</>
)
}
const trigger = renderTrigger(props, triggerNode, { ref: triggerRef, ...triggerEvents.value }, handleClickOutside)

const content = renderContent(
props,
mergedPrefixCls,
Expand Down Expand Up @@ -141,8 +149,8 @@ function renderContent(
visibility: ComputedRef<boolean>,
currentZIndex: ComputedRef<number>,
contentNode: VNode[],
arrowRef: Ref<PopperElement | null>,
popperRef: Ref<PopperElement | null>,
arrowRef: Ref<PopperElement | undefined>,
popperRef: Ref<PopperElement | undefined>,
popperEvents: ComputedRef<PopperEvents>,
attrs: Record<string, unknown>,
) {
Expand Down
8 changes: 4 additions & 4 deletions packages/components/_private/overlay/style/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
bottom: 0;

&::before {
bottom: -@overlay-arrow-size + 1px;
bottom: -@overlay-arrow-size;
left: 0;
border-width: @overlay-arrow-size @overlay-arrow-size 0;
border-top-color: initial;
Expand All @@ -31,7 +31,7 @@
top: 0;

&::before {
top: -@overlay-arrow-size + 1px;
top: -@overlay-arrow-size;
left: 0;
border-width: 0 @overlay-arrow-size @overlay-arrow-size;
border-bottom-color: initial;
Expand All @@ -47,7 +47,7 @@
&::before {
border-width: @overlay-arrow-size 0 @overlay-arrow-size @overlay-arrow-size;
border-left-color: initial;
right: -@overlay-arrow-size + 1px;
right: -@overlay-arrow-size;
transform-origin: center left;
}
}
Expand All @@ -58,7 +58,7 @@
left: 0;

&::before {
left: -@overlay-arrow-size + 1px;
left: -@overlay-arrow-size;
border-width: @overlay-arrow-size @overlay-arrow-size @overlay-arrow-size 0;
border-right-color: initial;
transform-origin: center right;
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
@overlay-arrow-size: 8px;
@overlay-arrow-size: 6px;
2 changes: 1 addition & 1 deletion packages/components/dropdown/style/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
cursor: pointer;
}

.@{idux-prefix}-overlay-arrow {
.@{overlay-prefix}-arrow {
color: @dropdown-overlay-arrow-color;
}
}
Expand Down
56 changes: 40 additions & 16 deletions packages/components/form/__tests__/__snapshots__/form.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ exports[`Form > basic work > render work 1`] = `
<!---->
<!---->
</div>
<!---->
<!---->
<div class=\\"ix-form-item-message\\">
<transition-stub>
<!---->
</transition-stub>
</div>
</div>
</div>
<div class=\\"ix-row ix-form-item\\">
Expand All @@ -26,8 +29,11 @@ exports[`Form > basic work > render work 1`] = `
<!---->
<!---->
</div>
<!---->
<!---->
<div class=\\"ix-form-item-message\\">
<transition-stub>
<!---->
</transition-stub>
</div>
</div>
</div>
<div class=\\"ix-row ix-form-item\\">
Expand All @@ -43,8 +49,11 @@ exports[`Form > basic work > render work 1`] = `
<!---->
<!---->
</div>
<!---->
<!---->
<div class=\\"ix-form-item-message\\">
<transition-stub>
<!---->
</transition-stub>
</div>
</div>
</div>
<div class=\\"ix-row ix-form-item\\">
Expand All @@ -55,8 +64,11 @@ exports[`Form > basic work > render work 1`] = `
<!---->
<!---->
</div>
<!---->
<!---->
<div class=\\"ix-form-item-message\\">
<transition-stub>
<!---->
</transition-stub>
</div>
</div>
</div>
</form>"
Expand All @@ -73,8 +85,11 @@ exports[`Form > item work > render work 1`] = `
<!---->
<!---->
</div>
<!---->
<!---->
<div class=\\"ix-form-item-message\\">
<transition-stub>
<!---->
</transition-stub>
</div>
</div>
</div>"
`;
Expand All @@ -90,8 +105,11 @@ exports[`Form > wrapper work > render work 1`] = `
<!---->
<!---->
</div>
<!---->
<!---->
<div class=\\"ix-form-item-message\\">
<transition-stub>
<!---->
</transition-stub>
</div>
</div>
</div>
<div class=\\"ix-row ix-form-item\\">
Expand All @@ -104,8 +122,11 @@ exports[`Form > wrapper work > render work 1`] = `
<!---->
<!---->
</div>
<!---->
<!---->
<div class=\\"ix-form-item-message\\">
<transition-stub>
<!---->
</transition-stub>
</div>
</div>
</div>
<div class=\\"ix-row ix-form-item\\">
Expand All @@ -121,8 +142,11 @@ exports[`Form > wrapper work > render work 1`] = `
<!---->
<!---->
</div>
<!---->
<!---->
<div class=\\"ix-form-item-message\\">
<transition-stub>
<!---->
</transition-stub>
</div>
</div>
</div>"
`;
14 changes: 7 additions & 7 deletions packages/components/form/__tests__/form.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,33 +474,33 @@ describe('Form', () => {
slots: { default: () => h(IxInput) },
})

expect(wrapper.find('.ix-form-item-message').exists()).toBe(false)
expect(wrapper.find('.ix-form-item-message-valid').exists()).toBe(false)

await wrapper.setProps({ status: 'valid' })

expect(wrapper.find('.ix-form-item-message').exists()).toBe(false)
expect(wrapper.find('.ix-form-item-message-valid').exists()).toBe(false)

await wrapper.setProps({ status: 'invalid' })

expect(wrapper.find('.ix-form-item-message').text()).toBe(message)
expect(wrapper.find('.ix-form-item-message-invalid').text()).toBe(message)

message = 'message2'
await wrapper.setProps({ message })

expect(wrapper.find('.ix-form-item-message').text()).toBe(message)
expect(wrapper.find('.ix-form-item-message-invalid').text()).toBe(message)

message = { valid: 'valid message', invalid: 'invalid message', validating: 'validating message' }
await wrapper.setProps({ message })

expect(wrapper.find('.ix-form-item-message').text()).toBe(message.invalid)
expect(wrapper.find('.ix-form-item-message-invalid').text()).toBe(message.invalid)

await wrapper.setProps({ status: 'valid' })

expect(wrapper.find('.ix-form-item-message').text()).toBe(message.valid)
expect(wrapper.find('.ix-form-item-message-valid').text()).toBe(message.valid)

await wrapper.setProps({ status: 'validating' })

expect(wrapper.find('.ix-form-item-message').text()).toBe(message.validating)
expect(wrapper.find('.ix-form-item-message-validating').text()).toBe(message.validating)
})

test('status work', async () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/components/form/demo/CustomizedValidation.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<template>
<IxForm class="demo-form" labelCol="6">
<IxFormItem label="Valid" status="valid" :message="messageMap.valid">
<IxFormItem label="Valid" status="valid" :message="messageMap">
<IxInput v-model:value="formValue.valid"></IxInput>
</IxFormItem>
<IxFormItem label="Validating" status="validating" :message="messageMap.validating">
<IxFormItem label="Validating" status="validating" :message="messageMap">
<IxInput v-model:value="formValue.validating"></IxInput>
</IxFormItem>
<IxFormItem label="Invalid" status="invalid" :message="messageMap.invalid">
<IxFormItem label="Invalid" status="invalid" :message="messageMap">
<IxInput v-model:value="formValue.invalid"></IxInput>
</IxFormItem>
<IxFormItem label="Dynamic" :status="status" :message="getMessage">
Expand Down

0 comments on commit 67222d1

Please sign in to comment.