feat(collapse-transition): new component, closes #829

This commit is contained in:
07akioni 2021-10-04 16:17:50 +08:00
parent c31442bdf7
commit 94693bb64a
21 changed files with 430 additions and 16 deletions

View File

@ -17,6 +17,7 @@
- `n-switch` add `unchecked-value` prop, closes [#1234](https://github.com/TuSimple/naive-ui/issues/1234).
- `n-checkbox` add `checked-value` prop, closes [#1234](https://github.com/TuSimple/naive-ui/issues/1234).
- `n-checkbox` add `unchecked-value` prop, closes [#1234](https://github.com/TuSimple/naive-ui/issues/1234).
- Add `n-collapse-transition` component, closes [#829](https://github.com/TuSimple/naive-ui/issues/829).
### Fixes

View File

@ -17,6 +17,7 @@
- `n-switch` 新增 `unchecked-value` 属性,关闭 [#1234](https://github.com/TuSimple/naive-ui/issues/1234)
- `n-checkbox` 新增 `checked-value` 属性,关闭 [#1234](https://github.com/TuSimple/naive-ui/issues/1234)
- `n-checkbox` 新增 `unchecked-value` 属性,关闭 [#1234](https://github.com/TuSimple/naive-ui/issues/1234)
- 新增 `n-collapse-transition` 组件,关闭 [#829](https://github.com/TuSimple/naive-ui/issues/829).
### Fixes

View File

@ -462,6 +462,11 @@ export const enComponentRoutes = [
{
path: 'carousel',
component: () => import('../../src/carousel/demos/enUS/index.demo-entry.md')
},
{
path: 'collapse-transition',
component: () =>
import('../../src/collapse-transition/demos/enUS/index.demo-entry.md')
}
]
@ -801,6 +806,11 @@ export const zhComponentRoutes = [
{
path: 'carousel',
component: () => import('../../src/carousel/demos/zhCN/index.demo-entry.md')
},
{
path: 'collapse-transition',
component: () =>
import('../../src/collapse-transition/demos/zhCN/index.demo-entry.md')
}
]

View File

@ -654,6 +654,19 @@ export function createComponentMenuOptions ({ lang, theme, mode }) {
}
]
}),
appendCounts({
zh: '工具组件',
en: 'Utility Components',
type: 'group',
children: [
{
en: 'Collapse Transition',
zh: '折叠动画',
enSuffix: true,
path: '/collapse-transition'
}
]
}),
appendCounts({
zh: '配置组件',
en: 'Config Components',

View File

@ -0,0 +1,27 @@
# Basic
```html
<n-space vertical>
<n-switch v-model:value="collapsed">
<template #checked>Collapsed</template>
<template #unchecked>Expanded</template>
</n-switch>
<n-collapse-transition :collapsed="collapsed">
"There is no single development, in either technology or management
technique, which by itself promises even one order of magnitude [tenfold]
improvement within a decade in productivity, in reliability, in simplicity."
</n-collapse-transition>
</n-space>
```
```js
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
collapsed: ref(true)
}
}
})
```

View File

@ -0,0 +1,23 @@
# Collapse Transition
A collapse item without any form of encapsulation.
## Demos
```demo
basic
```
## API
### CollapseTransition Props
| Name | Type | Default | Description |
| ------ | --------- | ------- | ------------------------------------------- |
| appear | `boolean` | `false` | Whether to play animation on first mounted. |
### CollapseTransition Slots
| Name | Parameters | Description |
| ------- | ---------- | ---------------------------------- |
| default | `()` | The content inside the transition. |

View File

@ -0,0 +1,25 @@
# 基本用法
```html
<n-space vertical>
<n-switch v-model:value="collapsed">
<template #checked>展开</template>
<template #unchecked>折叠</template>
</n-switch>
<n-collapse-transition :collapsed="collapsed">
感知度,方法论,组合拳,引爆点,点线面,精细化,差异化,平台化,结构化,影响力,耦合性,便捷性,一致性,端到端,短平快,护城河,体验感,颗粒度
</n-collapse-transition>
</n-space>
```
```js
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
collapsed: ref(true)
}
}
})
```

View File

@ -0,0 +1,23 @@
# 折叠渐变 Collapse Transition
一个没什么封装的 collapse item。
## 演示
```demo
basic
```
## API
### CollapseTransition Props
| 名称 | 类型 | 默认值 | 说明 |
| ------ | --------- | ------- | ------------------------ |
| appear | `boolean` | `false` | 是否在首次出现时播放动画 |
### CollapseTransition Slots
| 名称 | 参数 | 说明 |
| ------- | ---- | ---------- |
| default | `()` | 渐变的内容 |

View File

@ -0,0 +1 @@
export { default as NCollapseTransition } from './src/CollapseTransition'

View File

@ -0,0 +1,65 @@
import { computed, h, defineComponent, mergeProps } from 'vue'
import { useConfig, useTheme } from '../../_mixins'
import type { ThemeProps } from '../../_mixins'
import { ExtractPublicPropTypes } from '../../_utils'
import type { CollapseTransitionTheme } from '../styles'
import style from './styles/index.cssr'
import { collapseTransitionLight } from '../styles'
import { NFadeInExpandTransition } from '../../_internal'
const collapseProps = {
...(useTheme.props as ThemeProps<CollapseTransitionTheme>),
collapsed: Boolean,
appear: Boolean
} as const
export type CollapseProps = ExtractPublicPropTypes<typeof collapseProps>
export default defineComponent({
name: 'CollapseTransition',
props: collapseProps,
inheritAttrs: false,
setup (props) {
const { mergedClsPrefixRef } = useConfig(props)
const mergedThemeRef = useTheme(
'CollapseTransition',
'CollapseTransition',
style,
collapseTransitionLight,
props
)
return {
mergedClsPrefix: mergedClsPrefixRef,
cssVars: computed(() => {
const {
self: { bezier }
} = mergedThemeRef.value
return {
'--bezier': bezier
}
})
}
},
render () {
return (
<NFadeInExpandTransition appear={this.appear}>
{{
default: () =>
this.collapsed
? h(
'div', // Don't use jsx since it would cause useless spread in each rendering
mergeProps(
{
class: `${this.mergedClsPrefix}-collapse-transition`,
style: this.cssVars
},
this.$attrs
),
this.$slots
)
: null
}}
</NFadeInExpandTransition>
)
}
})

View File

@ -0,0 +1,8 @@
import { cB } from '../../../_utils/cssr'
import fadeInHeightExpandTransition from '../../../_styles/transitions/fade-in-height-expand.cssr'
export default cB('collapse-transition', {
width: '100%'
}, [
fadeInHeightExpandTransition()
])

View File

@ -0,0 +1,11 @@
import { commonDark } from '../../_styles/common'
import type { CollapseTransitionTheme } from './light'
import { self } from './light'
const collapseTransitionDark: CollapseTransitionTheme = {
name: 'CollapseTransition',
common: commonDark,
self
}
export default collapseTransitionDark

View File

@ -0,0 +1,6 @@
export { default as collapseTransitionDark } from './dark'
export { default as collapseTransitionLight } from './light'
export type {
CollapseTransitionTheme,
CollapseTransitionThemeVars
} from './light'

View File

@ -0,0 +1,24 @@
import { commonLight } from '../../_styles/common'
import type { ThemeCommonVars } from '../../_styles/common'
import { Theme } from '../../_mixins/use-theme'
export const self = (vars: ThemeCommonVars) => {
const { cubicBezierEaseInOut } = vars
return {
bezier: cubicBezierEaseInOut
}
}
export type CollapseTransitionThemeVars = ReturnType<typeof self>
const collapseTransitionLight: Theme<
'CollapseTransition',
CollapseTransitionThemeVars
> = {
name: 'CollapseTransition',
common: commonLight,
self
}
export default collapseTransitionLight
export type CollapseTransitionTheme = typeof collapseTransitionLight

View File

@ -0,0 +1,152 @@
import { h } from 'vue'
import { mount } from '@vue/test-utils'
import { NCollapse, NCollapseItem } from '../index'
describe('n-collapse', () => {
it('should work with import on demand', () => {
mount(NCollapse)
})
it('can customize icon', () => {
const wrapper = mount(() => {
return (
<NCollapse>
{{
arrow: () => <div class="my-icon"></div>,
default: () => <NCollapseItem name="1"></NCollapseItem>
}}
</NCollapse>
)
})
expect(wrapper.find('.my-icon').exists()).toEqual(true)
})
it('should work with `arrow-placement` prop', async () => {
const wrapper = mount(NCollapse, {
slots: {
default: () => <NCollapseItem name="1"></NCollapseItem>
}
})
expect(wrapper.find('.n-collapse-item').classes()).toContain(
'n-collapse-item--left-arrow-placement'
)
await wrapper.setProps({ arrowPlacement: 'right' })
expect(wrapper.find('.n-collapse-item').classes()).toContain(
'n-collapse-item--right-arrow-placement'
)
})
it('should work with nested structure', async () => {
mount(NCollapse, {
slots: {
default: () =>
h(
NCollapseItem,
{ name: '1', title: 'test1' },
{
default: () =>
h(NCollapse, null, {
default: () => h(NCollapseItem, { name: '2', title: 'test2' })
})
}
)
}
})
// todo: test display-directive
// I wanted to test this function, but I was bothered by the <transition-stub>
})
it('should work with `display-directive` prop', async () => {
mount(NCollapse, {
props: {
displayDirective: 'show'
},
slots: {
default: () =>
h(
NCollapseItem,
{ name: '1', title: 'test' },
{ default: () => h('div', null, { default: () => 'test' }) }
)
}
// todo: test display-directive
// I wanted to test this function, but I was bothered by the <transition-stub>
})
})
it('should work with `on-item-header-click` prop', async () => {
const onClick = jest.fn()
const wrapper = mount(NCollapse, {
props: {
onItemHeaderClick: onClick
},
slots: {
default: () => <NCollapseItem name="1"></NCollapseItem>
}
})
const triggerNodeWrapper = wrapper.find('.n-collapse-item__header-main')
await triggerNodeWrapper.trigger('click')
expect(onClick).toHaveBeenCalled()
})
it('should work with `slots` ', async () => {
const wrapper = mount(NCollapse, {
slots: {
header: () => 'header',
'header-extra': () => 'header-extra',
default: () => <NCollapseItem name="1"></NCollapseItem>,
arrow: () => 'arrow'
}
})
expect(wrapper.find('.n-collapse-item__header-main').exists()).toBe(true)
expect(wrapper.find('.n-collapse-item__header-main').text()).toBe('arrow')
expect(wrapper.find('.n-collapse-item__header-extra').exists()).toBe(true)
expect(wrapper.find('.n-collapse-item__header-extra').text()).toBe(
'header-extra'
)
expect(wrapper.find('.n-collapse-item-arrow').exists()).toBe(true)
expect(wrapper.find('.n-collapse-item-arrow').text()).toBe('arrow')
})
it('props.defaultExpandedNames', async () => {
let wrapper = mount(NCollapse, {
props: {
defaultExpandedNames: ['1']
},
slots: {
default: () => [
<NCollapseItem name="1">
{{ default: () => <div class="ci1"></div> }}
</NCollapseItem>,
<NCollapseItem name="2">
{{ default: () => <div class="ci2"></div> }}
</NCollapseItem>
]
}
})
expect(wrapper.find('.ci1').isVisible()).toEqual(true)
expect(wrapper.find('.ci2').exists()).toEqual(false)
wrapper = mount(NCollapse, {
props: {
accordion: true,
defaultExpandedNames: '1'
},
slots: {
default: () => [
<NCollapseItem name="1">
{{ default: () => <div class="ci1"></div> }}
</NCollapseItem>,
<NCollapseItem name="2">
{{ default: () => <div class="ci2"></div> }}
</NCollapseItem>
]
}
})
expect(wrapper.find('.ci1').isVisible()).toEqual(true)
expect(wrapper.find('.ci2').exists()).toEqual(false)
})
})

View File

@ -0,0 +1,19 @@
/**
* @jest-environment node
*/
import { h, createSSRApp } from 'vue'
import { renderToString } from '@vue/server-renderer'
import { setup } from '@css-render/vue3-ssr'
import { NCollapse } from '../..'
describe('SSR', () => {
it('works', async () => {
const app = createSSRApp(() => <NCollapse />)
setup(app)
try {
await renderToString(app)
} catch (e) {
expect(e).not.toBeTruthy()
}
})
})

View File

@ -15,6 +15,7 @@ export * from './cascader'
export * from './checkbox'
export * from './code'
export * from './collapse'
export * from './collapse-transition'
export * from './config-provider'
export * from './data-table'
export * from './date-picker'

View File

@ -85,6 +85,7 @@ import { DataTableRenderFilter, DataTableRenderSorter } from '../../data-table'
import { IconPlacement } from '../../dialog/src/interface'
import type { GlobalTheme, GlobalThemeOverrides } from './interface'
import type { EmptyProps } from '../../empty'
import { CollapseTransitionTheme } from '../../collapse-transition/styles'
export interface GlobalThemeWithoutCommon {
Alert?: AlertTheme
@ -102,6 +103,7 @@ export interface GlobalThemeWithoutCommon {
Checkbox?: CheckboxTheme
Code?: CodeTheme
Collapse?: CollapseTheme
CollapseTransition?: CollapseTransitionTheme
ColorPicker?: ColorPickerTheme
DataTable?: DataTableTheme
DatePicker?: DatePickerTheme

View File

@ -1,6 +1,6 @@
# 自定义字段
# Customizing Field
后端会传来各种各样的数据,你可以自定义 `key``label``children` 的字段。
Various data would come from backend, you can customize `key`, `label` and `children`'s field name.
```html
<n-layout has-sider>
@ -43,73 +43,73 @@ function renderIcon (icon) {
const menuOptions = [
{
whateverLabel: '且听风吟',
whateverLabel: 'Hear the Wind Sing',
whateverKey: 'hear-the-wind-sing',
icon: renderIcon(BookIcon)
},
{
whateverLabel: '1973年的弹珠玩具',
whateverLabel: 'Pinball 1973',
whateverKey: 'pinball-1973',
icon: renderIcon(BookIcon),
disabled: true,
whateverChildren: [
{
whateverLabel: '',
whateverLabel: 'Rat',
whateverKey: 'rat'
}
]
},
{
whateverLabel: '寻羊冒险记',
whateverLabel: 'A Wild Sheep Chase',
whateverKey: 'a-wild-sheep-chase',
disabled: true,
icon: renderIcon(BookIcon)
},
{
whateverLabel: '舞,舞,舞',
whateverKey: 'dance-dance-dance',
whateverLabel: 'Dance Dance Dance',
whateverKey: 'Dance Dance Dance',
icon: renderIcon(BookIcon),
whateverChildren: [
{
type: 'group',
whateverLabel: '人物',
whateverLabel: 'People',
whateverKey: 'people',
whateverChildren: [
{
whateverLabel: '叙事者',
whateverLabel: 'Narrator',
whateverKey: 'narrator',
icon: renderIcon(PersonIcon)
},
{
whateverLabel: '羊男',
whateverLabel: 'Sheep Man',
whateverKey: 'sheep-man',
icon: renderIcon(PersonIcon)
}
]
},
{
whateverLabel: '饮品',
whateverLabel: 'Beverage',
whateverKey: 'beverage',
icon: renderIcon(WineIcon),
whateverChildren: [
{
whateverLabel: '威士忌',
whateverLabel: 'Whisky',
whateverKey: 'whisky'
}
]
},
{
whateverLabel: '食物',
whateverLabel: 'Food',
whateverKey: 'food',
whateverChildren: [
{
whateverLabel: '三明治',
whateverLabel: 'Sandwich',
whateverKey: 'sandwich'
}
]
},
{
whateverLabel: '过去增多,未来减少',
whateverLabel: 'The past increases. The future recedes.',
whateverKey: 'the-past-increases-the-future-recedes'
}
]

View File

@ -16,6 +16,7 @@ collapse
inverted
long-label
accordion
customize-field
```
## API

View File

@ -68,6 +68,7 @@ export default cB('switch', `
bottom: 0;
display: flex;
align-items: center;
line-height: 1;
`),
cE('checked', `
right: 0;