Skip to content

Commit

Permalink
feat(ConfigProvider): add icons config
Browse files Browse the repository at this point in the history
  • Loading branch information
guoyunhe committed Apr 12, 2024
1 parent ad988cf commit bb9b8c4
Show file tree
Hide file tree
Showing 46 changed files with 535 additions and 193 deletions.
7 changes: 4 additions & 3 deletions src/components/center-popup/center-popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import classNames from 'classnames'
import { NativeProps, withNativeProps } from '../../utils/native-props'
import { ShouldRender } from '../../utils/should-render'
import { useLockScroll } from '../../utils/use-lock-scroll'
import { CloseOutline } from 'antd-mobile-icons'
import {
defaultPopupBaseProps,
PopupBaseProps,
} from '../popup/popup-base-props'
import { useConfig } from '../config-provider'

const classPrefix = 'adm-center-popup'

Expand All @@ -38,7 +38,8 @@ const defaultProps = {
}

export const CenterPopup: FC<CenterPopupProps> = p => {
const props = mergeProps(defaultProps, p)
const { popup: componentConfig = {} } = useConfig()
const props = mergeProps(defaultProps, componentConfig, p)

const unmountedRef = useUnmountedRef()
const style = useSpring({
Expand Down Expand Up @@ -134,7 +135,7 @@ export const CenterPopup: FC<CenterPopupProps> = p => {
props.onClose?.()
}}
>
<CloseOutline />
{props.closeIcon}
</a>
)}
{body}
Expand Down
6 changes: 4 additions & 2 deletions src/components/check-list/check-list.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react'
import type { FC, ReactNode } from 'react'
import { CheckOutline } from 'antd-mobile-icons'
import { NativeProps, withNativeProps } from '../../utils/native-props'
import List, { ListProps } from '../list'
import { mergeProps } from '../../utils/with-default-props'
import { CheckListContext } from './context'
import { usePropsValue } from '../../utils/use-props-value'
import { CheckOutline } from 'antd-mobile-icons'
import { useConfig } from '../config-provider'

const classPrefix = 'adm-check-list'

Expand All @@ -30,7 +31,8 @@ const defaultProps = {
}

export const CheckList: FC<CheckListProps> = p => {
const props = mergeProps(defaultProps, p)
const { checkList: componentConfig = {} } = useConfig()
const props = mergeProps(defaultProps, componentConfig, p)

const [value, setValue] = usePropsValue(props)

Expand Down
52 changes: 31 additions & 21 deletions src/components/collapse/collapse.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { animated, useSpring } from '@react-spring/web'
import { useMount } from 'ahooks'
import classNames from 'classnames'
import type { FC, ReactElement, ReactNode } from 'react'
import React, { isValidElement, useRef } from 'react'
import type { FC, ReactNode, ReactElement } from 'react'
import { NativeProps, withNativeProps } from '../../utils/native-props'
import List from '../list'
import { DownOutline } from 'antd-mobile-icons'
import classNames from 'classnames'
import { useSpring, animated } from '@react-spring/web'
import { usePropsValue } from '../../utils/use-props-value'
import { useMount } from 'ahooks'
import { useShouldRender } from '../../utils/should-render'
import { useIsomorphicUpdateLayoutEffect } from '../../utils/use-isomorphic-update-layout-effect'
import { traverseReactNode } from '../../utils/traverse-react-node'
import { useIsomorphicUpdateLayoutEffect } from '../../utils/use-isomorphic-update-layout-effect'
import { usePropsValue } from '../../utils/use-props-value'
import { mergeProps } from '../../utils/with-default-props'
import { useConfig } from '../config-provider'
import List from '../list'

const classPrefix = `adm-collapse`

Expand All @@ -20,8 +21,12 @@ export type CollapsePanelProps = {
forceRender?: boolean
destroyOnClose?: boolean
onClick?: (event: React.MouseEvent<Element, MouseEvent>) => void
arrow?: ReactNode | ((active: boolean) => ReactNode)
arrowIcon?: ReactNode | ((active: boolean) => ReactNode)
children?: ReactNode
/**
* @deprecated use `arrowIcon` instead
*/
arrow?: ReactNode | ((active: boolean) => ReactNode)
} & NativeProps

export const CollapsePanel: FC<CollapsePanelProps> = () => {
Expand Down Expand Up @@ -106,6 +111,10 @@ type ValueProps<T> = {
activeKey?: T
defaultActiveKey?: T
onChange?: (activeKey: T) => void
arrowIcon?: ReactNode | ((active: boolean) => ReactNode)
/**
* @deprecated use `arrowIcon` instead
*/
arrow?: ReactNode | ((active: boolean) => ReactNode)
}

Expand All @@ -120,7 +129,11 @@ export type CollapseProps = (
children?: ReactNode
} & NativeProps

export const Collapse: FC<CollapseProps> = props => {
const defaultProps = {}

export const Collapse: FC<CollapseProps> = p => {
const { collapse: componentConfig = {} } = useConfig()
const props = mergeProps(defaultProps, componentConfig, p)
const panels: ReactElement<CollapsePanelProps>[] = []
traverseReactNode(props.children, child => {
if (!isValidElement<CollapsePanelProps>(child)) return
Expand Down Expand Up @@ -196,15 +209,13 @@ export const Collapse: FC<CollapseProps> = props => {
panel.props.onClick?.(event)
}

const renderArrow = () => {
let arrow: CollapseProps['arrow'] = <DownOutline />
if (props.arrow !== undefined) {
arrow = props.arrow
}
if (panel.props.arrow !== undefined) {
arrow = panel.props.arrow
}
return typeof arrow === 'function' ? (
const arrow =
panel.props.arrow ||
panel.props.arrowIcon ||
props.arrow ||
props.arrowIcon
const arrowIcon =
typeof arrow === 'function' ? (
arrow(active)
) : (
<div
Expand All @@ -215,7 +226,6 @@ export const Collapse: FC<CollapseProps> = props => {
{arrow}
</div>
)
}

return (
<React.Fragment key={key}>
Expand All @@ -225,7 +235,7 @@ export const Collapse: FC<CollapseProps> = props => {
className={`${classPrefix}-panel-header`}
onClick={handleClick}
disabled={panel.props.disabled}
arrow={renderArrow()}
arrowIcon={arrowIcon}
>
{panel.props.title}
</List.Item>
Expand Down
4 changes: 2 additions & 2 deletions src/components/collapse/index.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ A content area that can be collapsed/expanded.
| --- | --- | --- | --- |
| accordion | Whether to enable accordion mode | `boolean` | `false` |
| activeKey | The `key` of the currently expanded panel | accordion mode: `string \| null` <br/>non-accordion mode: `string[]` | - |
| arrow | Custom arrow. if you pass a ReactNode, antd-mobile will add a rotate animation for you. | `React.ReactNode \| ((active: boolean) => React.ReactNode)` | - |
| arrowIcon | Custom arrow icon. if you pass a ReactNode, antd-mobile will add a rotate animation for you. | `React.ReactNode \| ((active: boolean) => React.ReactNode)` | - |
| defaultActiveKey | The `key` of the expanded panel by default | accordion mode: `string \| null` <br/>non-accordion mode: `string[]` | - |
| onChange | Triggered when the panel is switched | accordion mode: `(activeKey: string \| null) => void` <br /> non-accordion mode: `(activeKey: string[]) => void` | - |

Expand All @@ -31,7 +31,7 @@ A content area that can be collapsed/expanded.

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| arrow | Custom arrow | `React.ReactNode \| ((active: boolean) => React.ReactNode)` | - |
| arrowIcon | Custom arrow icon | `React.ReactNode \| ((active: boolean) => React.ReactNode)` | - |
| destroyOnClose | Destroy `dom` when not visible | `boolean` | `false` |
| disabled | Whether disabled or not | `boolean` | `false` |
| forceRender | Whether to render the `DOM` structure when hidden | `boolean` | `false` |
Expand Down
4 changes: 2 additions & 2 deletions src/components/collapse/index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
| --- | --- | --- | --- |
| accordion | 是否开启手风琴模式 | `boolean` | `false` |
| activeKey | 当前展开面板的 `key` | 手风琴模式:`string \| null` <br/>非手风琴模式:`string[]` | - |
| arrow | 自定义箭头,如果是 ReactNode,那么 antd-mobile 会自动为你增加旋转动画效果 | `ReactNode \| ((active: boolean) => React.ReactNode)` | - |
| arrowIcon | 自定义箭头图标,如果是 ReactNode,那么 antd-mobile 会自动为你增加旋转动画效果 | `ReactNode \| ((active: boolean) => React.ReactNode)` | - |
| defaultActiveKey | 默认展开面板的 `key` | 手风琴模式:`string \| null` <br/>非手风琴模式:`string[]` | - |
| onChange | 切换面板时触发 | 手风琴模式:`(activeKey: string \| null) => void` <br /> 非手风琴模式:`(activeKey: string[]) => void` | - |

Expand All @@ -31,7 +31,7 @@

| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| arrow | 自定义箭头 | `ReactNode \| ((active: boolean) => React.ReactNode)` | - |
| arrowIcon | 自定义箭头图标 | `ReactNode \| ((active: boolean) => React.ReactNode)` | - |
| destroyOnClose | 不可见时是否销毁 `DOM` 结构 | `boolean` | `false` |
| disabled | 是否为禁用状态 | `boolean` | `false` |
| forceRender | 被隐藏时是否渲染 `DOM` 结构 | `boolean` | `false` |
Expand Down
81 changes: 80 additions & 1 deletion src/components/collapse/tests/collapse.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render, screen, testA11y, fireEvent, waitFor } from 'testing'
import * as React from 'react'
import { fireEvent, render, screen, testA11y, waitFor } from 'testing'
import Collapse from '../'

const classPrefix = `adm-collapse`
Expand Down Expand Up @@ -100,3 +100,82 @@ test('disabled', async () => {
expect(screen.queryByTestId('first')).toBeVisible()
expect(screen.queryByTestId('second')).toBe(null)
})

describe('arrow', () => {
it('arrow (deprecated) of collapse', async () => {
render(
<Collapse arrow='foobar'>
<Collapse.Panel key='1' title='第一项'>
<div data-testid='first'>这里是第一项的内容</div>
</Collapse.Panel>
</Collapse>
)
expect(screen.getByText('foobar')).toBeVisible()
})

it('arrow (deprecated) of panel', async () => {
render(
<Collapse>
<Collapse.Panel key='1' title='第一项' arrow='foobar'>
<div data-testid='first'>这里是第一项的内容</div>
</Collapse.Panel>
</Collapse>
)
expect(screen.getByText('foobar')).toBeVisible()
})

it('arrow (deprecated) of both collapse and pannel', async () => {
render(
<Collapse arrow='foo'>
<Collapse.Panel key='1' title='第一项' arrow='bar'>
<div data-testid='first'>这里是第一项的内容</div>
</Collapse.Panel>
</Collapse>
)
expect(screen.getByText('bar')).toBeVisible()
})

it('both arrow (deprecated) and arrowIcon of collapse', async () => {
render(
<Collapse arrow='foo' arrowIcon='bar'>
<Collapse.Panel key='1' title='第一项'>
<div data-testid='first'>这里是第一项的内容</div>
</Collapse.Panel>
</Collapse>
)
expect(screen.getByText('foo')).toBeVisible()
})

it('both (deprecated) arrow and arrowIcon of pannel', async () => {
render(
<Collapse>
<Collapse.Panel key='1' title='第一项' arrow='foo' arrowIcon='bar'>
<div data-testid='first'>这里是第一项的内容</div>
</Collapse.Panel>
</Collapse>
)
expect(screen.getByText('foo')).toBeVisible()
})

it('arrow (deprecated) of collapse and arrowIcon of panel', async () => {
render(
<Collapse arrow='foo'>
<Collapse.Panel key='1' title='第一项' arrowIcon='bar'>
<div data-testid='first'>这里是第一项的内容</div>
</Collapse.Panel>
</Collapse>
)
expect(screen.getByText('bar')).toBeVisible()
})

it('arrowIcon of collapse and arrow (deprecated) of panel', async () => {
render(
<Collapse arrowIcon='foo'>
<Collapse.Panel key='1' title='第一项' arrow='bar'>
<div data-testid='first'>这里是第一项的内容</div>
</Collapse.Panel>
</Collapse>
)
expect(screen.getByText('bar')).toBeVisible()
})
})
46 changes: 44 additions & 2 deletions src/components/config-provider/config-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,52 @@
import React, { useContext } from 'react'
import type { FC, ReactNode } from 'react'
import React, { FC, ReactNode, useContext } from 'react'
import { Locale } from '../../locales/base'
import zhCN from '../../locales/zh-CN'

type Config = {
locale: Locale
checkList?: {
activeIcon?: ReactNode
}
collapse?: {
arrowIcon?: ReactNode | ((active: boolean) => ReactNode)
}
dropdown?: {
arrowIcon?: ReactNode
}
form?: {
helpIcon?: ReactNode
}
input?: {
clearIcon?: ReactNode
}
list?: {
arrowIcon?: ReactNode
}
navBar?: {
backIcon?: ReactNode
}
noticeBar?: {
icon?: ReactNode
closeIcon?: ReactNode
}
popup?: {
closeIcon?: ReactNode
}
result?: {
successIcon?: ReactNode
errorIcon?: ReactNode
infoIcon?: ReactNode
waitingIcon?: ReactNode
warningIcon?: ReactNode
}
searchBar?: {
searchIcon?: ReactNode
}
toast?: {
successIcon?: ReactNode
errorIcon?: ReactNode
loadingIcon?: ReactNode
}
}

export const defaultConfigRef: {
Expand Down
29 changes: 29 additions & 0 deletions src/components/config-provider/index.en.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
# ConfigProvider

Configure locale messages and custom icons globally.

## When to use

- You want to use other languages than English
- You want to use your own icon set instead of `antd-mobile-icons`

## Demos

<code src="./demos/demo1.tsx" ></code>

<code src="./demos/demo2.tsx" ></code>

## ConfigProvider

### Props

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| locale | Locale messages | `Locale` | [zh-CN] |
| checkList | CheckList config | `{ activeIcon?: ReactNode }` | - |
| collapse | Collapse config | `{ arrowIcon?: ReactNode \| ((active: boolean) => ReactNode) }` | - |
| dropdown | Dropdown config | `{ arrowIcon?: ReactNode }` | - |
| form | Form config | `{ helpIcon?: ReactNode }` | - |
| input | Input config | `{ clearIcon?: ReactNode }` | - |
| list | List config | `{ arrowIcon?: ReactNode }` | - |
| navBar | NavBar config | `{ backIcon?: ReactNode }` | - |
| noticeBar | NoticeBar config | `{ icon?: ReactNode, closeIcon?: ReactNode }` | - |
| popup | Popup config | `{ closeIcon?: ReactNode }` | - |
| result | Result config | `{ successIcon?: ReactNode, errorIcon?: ReactNode, infoIcon?: ReactNode, waitingIcon?: ReactNode, warningIcon?: ReactNode }` | - |
| searchBar | SearchBar config | `{ icon?: ReactNode }` | - |
| toast | Toast config | `{ successIcon?: ReactNode, failIcon?: ReactNode, loadingIcon?: ReactNode }` | - |

[zh-CN]: https://github.com/ant-design/ant-design-mobile/blob/master/src/locales/zh-CN.ts
Loading

0 comments on commit bb9b8c4

Please sign in to comment.