diff --git a/.github/workflows/mock-project-build.yml b/.github/workflows/mock-project-build.yml
index 1c7f3342801d..e7e934a08fd7 100644
--- a/.github/workflows/mock-project-build.yml
+++ b/.github/workflows/mock-project-build.yml
@@ -18,7 +18,7 @@ jobs:
name: Build Project
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
diff --git a/.github/workflows/pr-contributor-welcome.yml b/.github/workflows/pr-contributor-welcome.yml
index b8610144b645..3175d4be2072 100644
--- a/.github/workflows/pr-contributor-welcome.yml
+++ b/.github/workflows/pr-contributor-welcome.yml
@@ -15,7 +15,7 @@ jobs:
require-result: ${{ steps.contributors.outputs.content }}
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Read contributors.json
id: contributors
uses: juliangruber/read-file-action@v1
diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml
index 21b7ba9f0794..65f37668f781 100644
--- a/.github/workflows/preview-build.yml
+++ b/.github/workflows/preview-build.yml
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: cache package-lock.json
uses: actions/cache@v3
@@ -55,7 +55,7 @@ jobs:
needs: setup
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: restore cache from package-lock.json
uses: actions/cache@v3
diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml
index 9da6d38ca269..c0fba0dec737 100644
--- a/.github/workflows/rebase.yml
+++ b/.github/workflows/rebase.yml
@@ -16,7 +16,7 @@ jobs:
if: github.event.issue.pull_request != '' && (contains(github.event.comment.body, '/rebase') || contains(github.event.comment.body, '\rebase'))
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Automatic Rebase
diff --git a/.github/workflows/site-deploy.yml b/.github/workflows/site-deploy.yml
index 79cfe9c07df0..814563746af2 100644
--- a/.github/workflows/site-deploy.yml
+++ b/.github/workflows/site-deploy.yml
@@ -16,7 +16,7 @@ jobs:
if: (startsWith(github.ref, 'refs/tags/') && (contains(github.ref_name, '-') == false)) || github.event_name == 'workflow_dispatch'
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: cache package-lock.json
uses: actions/cache@v3
@@ -49,7 +49,7 @@ jobs:
needs: setup
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml
index 7f86bf3eeca1..73dc5bd68398 100644
--- a/.github/workflows/size-limit.yml
+++ b/.github/workflows/size-limit.yml
@@ -23,7 +23,7 @@ jobs:
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 3eb5482b89f2..7dce9880a74c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
@@ -53,7 +53,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
@@ -79,7 +79,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
@@ -104,7 +104,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
@@ -147,7 +147,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
@@ -188,6 +188,12 @@ jobs:
if: ${{ matrix.module == 'dist' }}
run: node ./tests/dekko/dist.test.js
+ - name: check use client
+ if: ${{ matrix.module == 'dist' }}
+ run: node ./tests/dekko/use-client.test.js
+ env:
+ LIB_DIR: dist
+
# dom test
- name: dom test
if: ${{ matrix.module == 'dom' }}
@@ -225,7 +231,7 @@ jobs:
runs-on: ubuntu-latest
needs: [normal-test]
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
@@ -251,7 +257,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
@@ -286,6 +292,10 @@ jobs:
- name: check
run: node ./tests/dekko/lib.test.js
+
+ - name: check use client
+ run: node ./tests/dekko/use-client.test.js
+
needs: setup
compiled-module-test:
@@ -302,7 +312,7 @@ jobs:
- name: checkout
# lib only run in master branch not in pull request
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
diff --git a/.github/workflows/verify-package-version.yml b/.github/workflows/verify-package-version.yml
index 6ff6065a3581..506294874259 100644
--- a/.github/workflows/verify-package-version.yml
+++ b/.github/workflows/verify-package-version.yml
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
if: contains(github.event.pull_request.title, 'changelog') || contains(github.event.pull_request.title, 'release')
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: verify-version
uses: actions-cool/verify-package-version@v1
with:
diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md
index 3faac82200c9..8a3d6241a4db 100644
--- a/CHANGELOG.zh-CN.md
+++ b/CHANGELOG.zh-CN.md
@@ -22,8 +22,8 @@ tag: vVERSION
- 🛠 针对 CSSInJS 加载 styles 大小进行了优化。
- 🛠 Notification 和 Message 组件只有在展示时才会插入对应样式。[#44488](https://github.com/ant-design/ant-design/pull/44488)
- - 🛠 剥离 Tag 状态与预设颜色样式,现在只有当使用的使用才会生成它们。[#44512](https://github.com/ant-design/ant-design/pull/44512)
- - 🛠 剥离 Button 紧凑模式样式,现在只有当使用了 Space.Compact 才会生成对应样式。[#44475](https://github.com/ant-design/ant-design/pull/44475)
+ - 🛠 剥离 Tag 状态与预设颜色样式,现在只有当使用的时候才会生成它们。[#44512](https://github.com/ant-design/ant-design/pull/44512)
+ - 🛠 剥离 Button 紧凑模式样式,现在只有当使用了 Space.Compact 的时候才会生成它们。[#44475](https://github.com/ant-design/ant-design/pull/44475)
- 📦 移除 `@ant-design/icons` 依赖 `lodash/camelCase` 以优化 bundle size。[ant-design-icons#595](https://github.com/ant-design/ant-design-icons/pull/595)
- Form
- 🐞 修复 Form.Item 设置 `wrapperCol.span` 为 `0` 时,子元素不隐藏的问题。[#44485](https://github.com/ant-design/ant-design/pull/44485) [#44472](https://github.com/ant-design/ant-design/pull/44472) [@crazyair](https://github.com/crazyair)
diff --git a/components/affix/index.tsx b/components/affix/index.tsx
index af3c9e3f54c0..9c37baff91c0 100644
--- a/components/affix/index.tsx
+++ b/components/affix/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import React, { createRef, forwardRef, useContext } from 'react';
import classNames from 'classnames';
diff --git a/components/alert/index.ts b/components/alert/index.ts
index 77038aea223c..5bb8dd2dbe8a 100644
--- a/components/alert/index.ts
+++ b/components/alert/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type { AlertProps } from './Alert';
import InternalAlert from './Alert';
import ErrorBoundary from './ErrorBoundary';
diff --git a/components/anchor/index.ts b/components/anchor/index.ts
index 6812dc61cbcc..8fa68a45ec9b 100644
--- a/components/anchor/index.ts
+++ b/components/anchor/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import InternalAnchor from './Anchor';
import AnchorLink from './AnchorLink';
diff --git a/components/app/index.tsx b/components/app/index.tsx
index 347dfe308661..56df12b953a8 100644
--- a/components/app/index.tsx
+++ b/components/app/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import type { ReactNode } from 'react';
import React, { useContext } from 'react';
diff --git a/components/auto-complete/index.tsx b/components/auto-complete/index.tsx
index 872913ed6557..e3bc7bb0b75f 100755
--- a/components/auto-complete/index.tsx
+++ b/components/auto-complete/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import type { BaseSelectRef } from 'rc-select';
import toArray from 'rc-util/lib/Children/toArray';
diff --git a/components/avatar/index.ts b/components/avatar/index.ts
index 8f015a7cdc54..ea2fa4358021 100644
--- a/components/avatar/index.ts
+++ b/components/avatar/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type { ForwardRefExoticComponent, RefAttributes } from 'react';
import type { AvatarProps } from './avatar';
import InternalAvatar from './avatar';
diff --git a/components/back-top/index.tsx b/components/back-top/index.tsx
index 7ade5fa225b6..8cfa5e2f174d 100644
--- a/components/back-top/index.tsx
+++ b/components/back-top/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import VerticalAlignTopOutlined from '@ant-design/icons/VerticalAlignTopOutlined';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
diff --git a/components/badge/index.tsx b/components/badge/index.tsx
index 0af4dc831b4b..1784d2976693 100644
--- a/components/badge/index.tsx
+++ b/components/badge/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classnames from 'classnames';
import CSSMotion from 'rc-motion';
import * as React from 'react';
diff --git a/components/breadcrumb/index.ts b/components/breadcrumb/index.ts
index 90f3843f1e19..202800b19cc3 100644
--- a/components/breadcrumb/index.ts
+++ b/components/breadcrumb/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import Breadcrumb from './Breadcrumb';
export type { BreadcrumbProps } from './Breadcrumb';
diff --git a/components/button/index.ts b/components/button/index.ts
index bc706908779b..1283a0d6f662 100644
--- a/components/button/index.ts
+++ b/components/button/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import Button from './button';
export type { SizeType as ButtonSize } from '../config-provider/SizeContext';
diff --git a/components/calendar/index.ts b/components/calendar/index.ts
index 4f282637f672..9933d970d337 100644
--- a/components/calendar/index.ts
+++ b/components/calendar/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type { Dayjs } from 'dayjs';
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
import type { CalendarProps } from './generateCalendar';
diff --git a/components/card/index.ts b/components/card/index.ts
index c599a656db67..f3a1ccbe8e04 100644
--- a/components/card/index.ts
+++ b/components/card/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import InternalCard from './Card';
import Grid from './Grid';
import Meta from './Meta';
diff --git a/components/carousel/index.tsx b/components/carousel/index.tsx
index 7b5ddb0c470a..d7ba787c8ffc 100644
--- a/components/carousel/index.tsx
+++ b/components/carousel/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import * as React from 'react';
import type { Settings } from '@ant-design/react-slick';
import SlickCarousel from '@ant-design/react-slick';
diff --git a/components/cascader/index.tsx b/components/cascader/index.tsx
index d5a966a0b3aa..ebc399c44ec1 100644
--- a/components/cascader/index.tsx
+++ b/components/cascader/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import * as React from 'react';
import LeftOutlined from '@ant-design/icons/LeftOutlined';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
diff --git a/components/checkbox/index.en-US.md b/components/checkbox/index.en-US.md
index d8e196c9226a..a68efc757a92 100644
--- a/components/checkbox/index.en-US.md
+++ b/components/checkbox/index.en-US.md
@@ -75,3 +75,15 @@ interface Option {
## Design Token
+
+## FAQ
+
+### Why not work in Form.Item?
+
+Form.Item default bind value to `value` property, but Checkbox value property is `checked`. You can use `valuePropName` to change bind property.
+
+```tsx | pure
+
+
+
+```
diff --git a/components/checkbox/index.ts b/components/checkbox/index.ts
index 283aacfcead5..c2876bcdf4e9 100644
--- a/components/checkbox/index.ts
+++ b/components/checkbox/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type * as React from 'react';
import type { CheckboxProps } from './Checkbox';
import InternalCheckbox from './Checkbox';
diff --git a/components/checkbox/index.zh-CN.md b/components/checkbox/index.zh-CN.md
index d55074ba349b..0ee3ddd08d05 100644
--- a/components/checkbox/index.zh-CN.md
+++ b/components/checkbox/index.zh-CN.md
@@ -76,3 +76,15 @@ interface Option {
## 主题变量(Design Token)
+
+## FAQ
+
+### 为什么在 Form.Item 下不能绑定数据?
+
+Form.Item 默认绑定值属性到 `value` 上,而 Checkbox 的值属性为 `checked`。你可以通过 `valuePropName` 来修改绑定的值属性。
+
+```tsx | pure
+
+
+
+```
diff --git a/components/col/index.ts b/components/col/index.ts
index 96021b9eaa62..413ae4d2be25 100644
--- a/components/col/index.ts
+++ b/components/col/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import { Col, type ColProps, type ColSize } from '../grid';
export type { ColProps, ColSize };
diff --git a/components/collapse/index.ts b/components/collapse/index.ts
index d5b6c97af491..9232a92c71c0 100644
--- a/components/collapse/index.ts
+++ b/components/collapse/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import Collapse from './Collapse';
export type { CollapseProps } from './Collapse';
diff --git a/components/color-picker/index.ts b/components/color-picker/index.ts
index 411285ce9489..8d6efd810e9d 100644
--- a/components/color-picker/index.ts
+++ b/components/color-picker/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import ColorPicker from './ColorPicker';
export type { ColorPickerProps } from './ColorPicker';
diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx
index 88647bbb4815..4a3a36d52d7b 100644
--- a/components/config-provider/index.tsx
+++ b/components/config-provider/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import { createTheme } from '@ant-design/cssinjs';
import IconContext from '@ant-design/icons/lib/components/Context';
import type { ValidateMessages } from 'rc-field-form/lib/interface';
diff --git a/components/date-picker/index.ts b/components/date-picker/index.ts
index 31055d12d4fd..08ff863589d5 100755
--- a/components/date-picker/index.ts
+++ b/components/date-picker/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type { Dayjs } from 'dayjs';
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
import genPurePanel from '../_util/PurePanel';
diff --git a/components/descriptions/index.tsx b/components/descriptions/index.tsx
index 6108108c79e3..28b5a8df9823 100644
--- a/components/descriptions/index.tsx
+++ b/components/descriptions/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
/* eslint-disable react/no-array-index-key */
import * as React from 'react';
import classNames from 'classnames';
diff --git a/components/divider/index.tsx b/components/divider/index.tsx
index 5e32f73dc86d..5140bd3aa636 100644
--- a/components/divider/index.tsx
+++ b/components/divider/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import * as React from 'react';
import warning from '../_util/warning';
diff --git a/components/drawer/index.tsx b/components/drawer/index.tsx
index b71ba51e56e9..6141e06e3a44 100644
--- a/components/drawer/index.tsx
+++ b/components/drawer/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import type { DrawerProps as RcDrawerProps } from 'rc-drawer';
import RcDrawer from 'rc-drawer';
diff --git a/components/dropdown/index.ts b/components/dropdown/index.ts
index 994ae7f38992..a7f2ea466933 100644
--- a/components/dropdown/index.ts
+++ b/components/dropdown/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import InternalDropdown from './dropdown';
import DropdownButton from './dropdown-button';
diff --git a/components/empty/index.tsx b/components/empty/index.tsx
index fe242f139f05..5ed64457b30e 100644
--- a/components/empty/index.tsx
+++ b/components/empty/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
diff --git a/components/float-button/index.ts b/components/float-button/index.ts
index 2e443547314f..663dda19f0c8 100644
--- a/components/float-button/index.ts
+++ b/components/float-button/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import BackTop from './BackTop';
import FloatButton from './FloatButton';
import FloatButtonGroup from './FloatButtonGroup';
diff --git a/components/form/FormItem/ItemHolder.tsx b/components/form/FormItem/ItemHolder.tsx
index 182c94f7edbf..2f2fa4cf9dbf 100644
--- a/components/form/FormItem/ItemHolder.tsx
+++ b/components/form/FormItem/ItemHolder.tsx
@@ -1,27 +1,19 @@
-import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
-import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
-import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
-import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
+import * as React from 'react';
import classNames from 'classnames';
import type { Meta } from 'rc-field-form/lib/interface';
-import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import isVisible from 'rc-util/lib/Dom/isVisible';
+import useLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import omit from 'rc-util/lib/omit';
-import * as React from 'react';
-import type { FormItemProps, ValidateStatus } from '.';
+
+import type { FormItemProps } from '.';
import { Row } from '../../grid';
+import type { ReportMetaChange } from '../context';
+import { FormContext, NoStyleItemContext } from '../context';
import FormItemInput from '../FormItemInput';
import FormItemLabel from '../FormItemLabel';
-import type { FormItemStatusContextProps, ReportMetaChange } from '../context';
-import { FormContext, FormItemInputContext, NoStyleItemContext } from '../context';
import useDebounce from '../hooks/useDebounce';
-
-const iconMap = {
- success: CheckCircleFilled,
- warning: ExclamationCircleFilled,
- error: CloseCircleFilled,
- validating: LoadingOutlined,
-};
+import { getStatus } from '../util';
+import StatusProvider from './StatusProvider';
export interface ItemHolderProps extends FormItemProps {
prefixCls: string;
@@ -59,7 +51,7 @@ export default function ItemHolder(props: ItemHolderProps) {
} = props;
const itemPrefixCls = `${prefixCls}-item`;
- const { requiredMark, feedbackIcons } = React.useContext(FormContext);
+ const { requiredMark } = React.useContext(FormContext);
// ======================== Margin ========================
const itemRef = React.useRef(null);
@@ -88,57 +80,14 @@ export default function ItemHolder(props: ItemHolderProps) {
// ======================== Status ========================
const getValidateState = (isDebounce = false) => {
- let status: ValidateStatus = '';
const _errors = isDebounce ? debounceErrors : meta.errors;
const _warnings = isDebounce ? debounceWarnings : meta.warnings;
- if (validateStatus !== undefined) {
- status = validateStatus;
- } else if (meta.validating) {
- status = 'validating';
- } else if (_errors.length) {
- status = 'error';
- } else if (_warnings.length) {
- status = 'warning';
- } else if (meta.touched || (hasFeedback && meta.validated)) {
- // success feedback should display when pass hasFeedback prop and current value is valid value
- status = 'success';
- }
- return status;
+
+ return getStatus(_errors, _warnings, meta, '', !!hasFeedback, validateStatus);
};
const mergedValidateStatus = getValidateState();
- const formItemStatusContext = React.useMemo(() => {
- let feedbackIcon: React.ReactNode;
- if (hasFeedback) {
- const customIcons = (hasFeedback !== true && hasFeedback.icons) || feedbackIcons;
- const customIconNode =
- mergedValidateStatus &&
- customIcons?.({ status: mergedValidateStatus, errors, warnings })?.[mergedValidateStatus];
- const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus];
- feedbackIcon =
- customIconNode !== false && IconNode ? (
-
- {customIconNode || }
-
- ) : null;
- }
-
- return {
- status: mergedValidateStatus,
- errors,
- warnings,
- hasFeedback: !!hasFeedback,
- feedbackIcon,
- isFormItemInput: true,
- };
- }, [mergedValidateStatus, hasFeedback]);
-
// ======================== Render ========================
const itemClassName = classNames(itemPrefixCls, className, rootClassName, {
[`${itemPrefixCls}-with-help`]: hasHelp || debounceErrors.length || debounceWarnings.length,
@@ -209,9 +158,17 @@ export default function ItemHolder(props: ItemHolderProps) {
onErrorVisibleChanged={onErrorVisibleChanged}
>
-
+
{children}
-
+
diff --git a/components/form/FormItem/StatusProvider.tsx b/components/form/FormItem/StatusProvider.tsx
new file mode 100644
index 000000000000..20907de57491
--- /dev/null
+++ b/components/form/FormItem/StatusProvider.tsx
@@ -0,0 +1,103 @@
+import * as React from 'react';
+import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
+import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
+import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
+import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
+import classNames from 'classnames';
+import type { Meta } from 'rc-field-form/lib/interface';
+
+import type { FeedbackIcons, ValidateStatus } from '.';
+import { FormContext, FormItemInputContext, type FormItemStatusContextProps } from '../context';
+import { getStatus } from '../util';
+
+const iconMap = {
+ success: CheckCircleFilled,
+ warning: ExclamationCircleFilled,
+ error: CloseCircleFilled,
+ validating: LoadingOutlined,
+};
+
+export interface StatusProviderProps {
+ children?: React.ReactNode;
+ validateStatus?: ValidateStatus;
+ prefixCls: string;
+ meta: Meta;
+ errors: React.ReactNode[];
+ warnings: React.ReactNode[];
+ hasFeedback?: boolean | { icons?: FeedbackIcons };
+ noStyle?: boolean;
+}
+
+export default function StatusProvider({
+ children,
+ errors,
+ warnings,
+ hasFeedback,
+ validateStatus,
+ prefixCls,
+ meta,
+ noStyle,
+}: StatusProviderProps) {
+ const itemPrefixCls = `${prefixCls}-item`;
+ const { feedbackIcons } = React.useContext(FormContext);
+
+ const mergedValidateStatus = getStatus(
+ errors,
+ warnings,
+ meta,
+ null,
+ !!hasFeedback,
+ validateStatus,
+ );
+
+ const { isFormItemInput: parentIsFormItemInput, status: parentStatus } =
+ React.useContext(FormItemInputContext);
+
+ // ====================== Context =======================
+ const formItemStatusContext = React.useMemo(() => {
+ let feedbackIcon: React.ReactNode;
+ if (hasFeedback) {
+ const customIcons = (hasFeedback !== true && hasFeedback.icons) || feedbackIcons;
+ const customIconNode =
+ mergedValidateStatus &&
+ customIcons?.({ status: mergedValidateStatus, errors, warnings })?.[mergedValidateStatus];
+ const IconNode = mergedValidateStatus && iconMap[mergedValidateStatus];
+ feedbackIcon =
+ customIconNode !== false && IconNode ? (
+
+ {customIconNode || }
+
+ ) : null;
+ }
+
+ let isFormItemInput: boolean | undefined = true;
+ let status: ValidateStatus = mergedValidateStatus || '';
+
+ // No style will follow parent context
+ if (noStyle) {
+ isFormItemInput = parentIsFormItemInput;
+ status = (mergedValidateStatus ?? parentStatus) || '';
+ }
+
+ return {
+ status,
+ errors,
+ warnings,
+ hasFeedback: !!hasFeedback,
+ feedbackIcon,
+ isFormItemInput,
+ };
+ }, [mergedValidateStatus, hasFeedback, noStyle, parentIsFormItemInput, parentStatus]);
+
+ // ======================= Render =======================
+ return (
+
+ {children}
+
+ );
+}
diff --git a/components/form/FormItem/index.tsx b/components/form/FormItem/index.tsx
index 0f12e8bf8662..2fdbbe9cfcc2 100644
--- a/components/form/FormItem/index.tsx
+++ b/components/form/FormItem/index.tsx
@@ -1,24 +1,26 @@
+import * as React from 'react';
import classNames from 'classnames';
import { Field, FieldContext, ListContext } from 'rc-field-form';
import type { FieldProps } from 'rc-field-form/lib/Field';
import type { Meta, NamePath } from 'rc-field-form/lib/interface';
import useState from 'rc-util/lib/hooks/useState';
import { supportRef } from 'rc-util/lib/ref';
-import * as React from 'react';
+
import { cloneElement, isValidElement } from '../../_util/reactNode';
import warning from '../../_util/warning';
import { ConfigContext } from '../../config-provider';
+import { FormContext, NoStyleItemContext } from '../context';
+import type { FormInstance } from '../Form';
import type { FormItemInputProps } from '../FormItemInput';
import type { FormItemLabelProps, LabelTooltipType } from '../FormItemLabel';
-import { FormContext, NoStyleItemContext } from '../context';
+import useChildren from '../hooks/useChildren';
import useFormItemStatus from '../hooks/useFormItemStatus';
import useFrameState from '../hooks/useFrameState';
import useItemRef from '../hooks/useItemRef';
+import useStyle from '../style';
import { getFieldId, toArray } from '../util';
import ItemHolder from './ItemHolder';
-import useChildren from '../hooks/useChildren';
-import useStyle from '../style';
-import type { FormInstance } from '../Form';
+import StatusProvider from './StatusProvider';
const NAME_SPLIT = '__SPLIT__';
@@ -219,7 +221,19 @@ function InternalFormItem(props: FormItemProps): React.Rea
isRequired?: boolean,
): React.ReactNode {
if (noStyle && !hidden) {
- return baseChildren;
+ return (
+
+ {baseChildren}
+
+ );
}
return (
diff --git a/components/form/__tests__/index.test.tsx b/components/form/__tests__/index.test.tsx
index 444dc38a70fd..8acab718477c 100644
--- a/components/form/__tests__/index.test.tsx
+++ b/components/form/__tests__/index.test.tsx
@@ -1,15 +1,16 @@
-import classNames from 'classnames';
import type { ChangeEventHandler } from 'react';
import React, { version as ReactVersion, useEffect, useRef, useState } from 'react';
+import type { ColProps } from 'antd/es/grid';
+import classNames from 'classnames';
import scrollIntoView from 'scroll-into-view-if-needed';
import { AlertFilled } from '@ant-design/icons';
-import type { ColProps } from 'antd/es/grid';
+
import type { FormInstance } from '..';
import Form from '..';
+import { resetWarned } from '../../_util/warning';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, pureRender, render, screen, waitFakeTimer } from '../../../tests/utils';
-import { resetWarned } from '../../_util/warning';
import Button from '../../button';
import Cascader from '../../cascader';
import Checkbox from '../../checkbox';
@@ -1407,38 +1408,91 @@ describe('Form', () => {
expect(subFormInstance).toBe(formInstance);
});
- it('noStyle should not affect status', () => {
- const Demo: React.FC = () => (
-
-
-
-
+ describe('noStyle with status', () => {
+ it('noStyle should not affect status', async () => {
+ const Demo: React.FC = () => (
+
+
+
+
+ {/* should follow parent status */}
+
+
+
+
+
+
+ {/* should follow child status */}
+
+
+
+
+
+
+ {/* should follow child status */}
-
+
+
+
-
-
-
-
+
+ {/* should follow child status */}
+
+
+
+
-
-
-
-
+
+ );
+ const { container } = render();
+
+ await waitFakeTimer();
+
+ expect(container.querySelector('.custom-select')).toHaveClass('ant-select-status-error');
+ expect(container.querySelector('.custom-select')).not.toHaveClass('ant-select-in-form-item');
+
+ expect(container.querySelector('.custom-select-b')).toHaveClass('ant-select-status-error');
+ expect(container.querySelector('.custom-select-b')).toHaveClass('ant-select-in-form-item');
+
+ expect(container.querySelector('.custom-select-c')).toHaveClass('ant-select-status-warning');
+ expect(container.querySelector('.custom-select-c')).toHaveClass('ant-select-in-form-item');
+
+ expect(container.querySelector('.custom-select-d')).toHaveClass('ant-select-status-warning');
+ expect(container.querySelector('.custom-select-d')).toHaveClass('ant-select-in-form-item');
+
+ expect(container.querySelector('.custom-select-e')).not.toHaveClass(
+ 'ant-select-status-error',
+ );
+ expect(container.querySelector('.custom-select-e')).toHaveClass('ant-select-in-form-item');
+ });
+
+ it('parent pass status', async () => {
+ const { container } = render(
+
+
+
+
+
+
+
-
-
- );
- const { container } = render();
- expect(container.querySelector('.custom-select')?.className).not.toContain('status-error');
- expect(container.querySelector('.custom-select')?.className).not.toContain('in-form-item');
- expect(container.querySelector('.custom-select-b')?.className).toContain('status-error');
- expect(container.querySelector('.custom-select-b')?.className).toContain('in-form-item');
- expect(container.querySelector('.custom-select-c')?.className).toContain('status-error');
- expect(container.querySelector('.custom-select-c')?.className).toContain('in-form-item');
- expect(container.querySelector('.custom-select-d')?.className).toContain('status-warning');
- expect(container.querySelector('.custom-select-d')?.className).toContain('in-form-item');
+ ,
+ );
+
+ // Input and set back to empty
+ await changeValue(0, 'Once');
+ await changeValue(0, '');
+
+ expect(container.querySelector('.ant-form-item-explain-error')?.textContent).toEqual(
+ "'first' is required",
+ );
+
+ expect(container.querySelectorAll('input')[0]).toHaveClass('ant-input-status-error');
+ expect(container.querySelectorAll('input')[1]).not.toHaveClass('ant-input-status-error');
+ });
});
it('should not affect Popup children style', () => {
diff --git a/components/form/context.tsx b/components/form/context.tsx
index 6f8ab3d0142a..e06dee957dd1 100644
--- a/components/form/context.tsx
+++ b/components/form/context.tsx
@@ -1,10 +1,11 @@
+import type { PropsWithChildren, ReactNode } from 'react';
+import * as React from 'react';
+import { useContext, useMemo } from 'react';
import { FormProvider as RcFormProvider } from 'rc-field-form';
import type { FormProviderProps as RcFormProviderProps } from 'rc-field-form/lib/FormContext';
import type { Meta } from 'rc-field-form/lib/interface';
import omit from 'rc-util/lib/omit';
-import type { PropsWithChildren, ReactNode } from 'react';
-import * as React from 'react';
-import { useContext, useMemo } from 'react';
+
import type { ColProps } from '../grid/col';
import type { FormInstance, RequiredMark } from './Form';
import type { ValidateStatus, FeedbackIcons } from './FormItem';
@@ -66,6 +67,10 @@ export interface FormItemStatusContextProps {
export const FormItemInputContext = React.createContext({});
+if (process.env.NODE_ENV !== 'production') {
+ FormItemInputContext.displayName = 'FormItemInputContext';
+}
+
export type NoFormStyleProps = PropsWithChildren<{
status?: boolean;
override?: boolean;
diff --git a/components/form/index.en-US.md b/components/form/index.en-US.md
index 42cef98e7276..4956feec8373 100644
--- a/components/form/index.en-US.md
+++ b/components/form/index.en-US.md
@@ -135,7 +135,7 @@ Form field component for data bidirectional binding, validation, layout, and so
| messageVariables | The default validate field info | Record<string, string> | - | 4.7.0 |
| name | Field name, support array | [NamePath](#namepath) | - | |
| normalize | Normalize value from component value before passing to Form instance. Do not support async | (value, prevValue, prevValues) => any | - | |
-| noStyle | No style for `true`, used as a pure field control | boolean | false | |
+| noStyle | No style for `true`, used as a pure field control. Will inherit parent Form.Item `validateStatus` if self `validateStatus` not configured | boolean | false | |
| preserve | Keep field value even when field removed | boolean | true | 4.4.0 |
| required | Display required style. It will be generated by the validation rule | boolean | false | |
| rules | Rules for field validation. Click [here](#components-form-demo-basic) to see an example | [Rule](#rule)\[] | - | |
@@ -145,7 +145,7 @@ Form field component for data bidirectional binding, validation, layout, and so
| validateFirst | Whether stop validate on first rule of error for this field. Will parallel validate when `parallel` configured | boolean \| `parallel` | false | `parallel`: 4.5.0 |
| validateStatus | The validation status. If not provided, it will be generated by validation rule. options: `success` `warning` `error` `validating` | string | - | |
| validateTrigger | When to validate the value of children node | string \| string\[] | `onChange` | |
-| valuePropName | Props of children node, for example, the prop of Switch is 'checked'. This prop is an encapsulation of `getValueProps`, which will be invalid after customizing `getValueProps` | string | `value` | |
+| valuePropName | Props of children node, for example, the prop of Switch or Checkbox is `checked`. This prop is an encapsulation of `getValueProps`, which will be invalid after customizing `getValueProps` | string | `value` | |
| wrapperCol | The layout for input controls, same as `labelCol`. You can set `wrapperCol` on Form which will not affect nest Item. If both exists, use Item first | [object](/components/grid/#col) | - | |
After wrapped by `Form.Item` with `name` property, `value`(or other property defined by `valuePropName`) `onChange`(or other property defined by `trigger`) props will be added to form controls, the flow of form data will be handled by Form which will cause:
@@ -540,6 +540,16 @@ type Rule = RuleConfig | ((form: FormInstance) => RuleConfig);
## FAQ
+### Why can't Switch、Checkbox bind data?
+
+Form.Item default bind value to `value` prop, but Switch or Checkbox value prop is `checked`. You can use `valuePropName` to change bind value prop.
+
+```tsx | pure
+
+
+
+```
+
### Custom validator not working
It may be caused by your `validator` if it has some errors that prevents `callback` to be called. You can use `async` instead or use `try...catch` to catch the error:
diff --git a/components/form/index.ts b/components/form/index.ts
index 167f3fd07ca0..1438f9f4f99c 100644
--- a/components/form/index.ts
+++ b/components/form/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type { Rule, RuleObject, RuleRender } from 'rc-field-form/lib/interface';
import warning from '../_util/warning';
import ErrorList, { type ErrorListProps } from './ErrorList';
diff --git a/components/form/index.zh-CN.md b/components/form/index.zh-CN.md
index 6d0583856c72..108f4679ee67 100644
--- a/components/form/index.zh-CN.md
+++ b/components/form/index.zh-CN.md
@@ -136,7 +136,7 @@ const validateMessages = {
| messageVariables | 默认验证字段的信息 | Record<string, string> | - | 4.7.0 |
| name | 字段名,支持数组 | [NamePath](#namepath) | - | |
| normalize | 组件获取值后进行转换,再放入 Form 中。不支持异步 | (value, prevValue, prevValues) => any | - | |
-| noStyle | 为 `true` 时不带样式,作为纯字段控件使用 | boolean | false | |
+| noStyle | 为 `true` 时不带样式,作为纯字段控件使用。当自身没有 `validateStatus` 而父元素存在有 `validateStatus` 的 Form.Item 会继承父元素的 `validateStatus` | boolean | false | |
| preserve | 当字段被删除时保留字段值 | boolean | true | 4.4.0 |
| required | 必填样式设置。如不设置,则会根据校验规则自动生成 | boolean | false | |
| rules | 校验规则,设置字段的校验逻辑。点击[此处](#components-form-demo-basic)查看示例 | [Rule](#rule)\[] | - | |
@@ -146,7 +146,7 @@ const validateMessages = {
| validateFirst | 当某一规则校验不通过时,是否停止剩下的规则的校验。设置 `parallel` 时会并行校验 | boolean \| `parallel` | false | `parallel`: 4.5.0 |
| validateStatus | 校验状态,如不设置,则会根据校验规则自动生成,可选:'success' 'warning' 'error' 'validating' | string | - | |
| validateTrigger | 设置字段校验的时机 | string \| string\[] | `onChange` | |
-| valuePropName | 子节点的值的属性,如 Switch 的是 'checked'。该属性为 `getValueProps` 的封装,自定义 `getValueProps` 后会失效 | string | `value` | |
+| valuePropName | 子节点的值的属性,如 Switch、Checkbox 的是 `checked`。该属性为 `getValueProps` 的封装,自定义 `getValueProps` 后会失效 | string | `value` | |
| wrapperCol | 需要为输入控件设置布局样式时,使用该属性,用法同 `labelCol`。你可以通过 Form 的 `wrapperCol` 进行统一设置,不会作用于嵌套 Item。当和 Form 同时设置时,以 Item 为准 | [object](/components/grid-cn#col) | - | |
被设置了 `name` 属性的 `Form.Item` 包装的控件,表单控件会自动添加 `value`(或 `valuePropName` 指定的其他属性) `onChange`(或 `trigger` 指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
@@ -535,6 +535,16 @@ type Rule = RuleConfig | ((form: FormInstance) => RuleConfig);
## FAQ
+### Switch、Checkbox 为什么不能绑定数据?
+
+Form.Item 默认绑定值属性到 `value` 上,而 Switch、Checkbox 等组件的值属性为 `checked`。你可以通过 `valuePropName` 来修改绑定的值属性。
+
+```tsx | pure
+
+
+
+```
+
### 自定义 validator 没有效果
这是由于你的 `validator` 有错误导致 `callback` 没有执行到。你可以选择通过 `async` 返回一个 promise 或者使用 `try...catch` 进行错误捕获:
diff --git a/components/form/util.ts b/components/form/util.ts
index 857d5c66dee9..1b18bd552cd9 100644
--- a/components/form/util.ts
+++ b/components/form/util.ts
@@ -1,3 +1,6 @@
+import type { Meta } from 'rc-field-form/lib/interface';
+
+import type { ValidateStatus } from './FormItem';
import type { InternalNamePath } from './interface';
// form item name black list. in form ,you can use form.id get the form item element.
@@ -28,3 +31,31 @@ export function getFieldId(namePath: InternalNamePath, formName?: string): strin
return isIllegalName ? `${defaultItemNamePrefixCls}_${mergedId}` : mergedId;
}
+
+/**
+ * Get merged status by meta or passed `validateStatus`.
+ */
+export function getStatus(
+ errors: React.ReactNode[],
+ warnings: React.ReactNode[],
+ meta: Meta,
+ defaultValidateStatus: ValidateStatus | DefaultValue,
+ hasFeedback?: boolean,
+ validateStatus?: ValidateStatus,
+): ValidateStatus | DefaultValue {
+ let status = defaultValidateStatus;
+
+ if (validateStatus !== undefined) {
+ status = validateStatus;
+ } else if (meta.validating) {
+ status = 'validating';
+ } else if (errors.length) {
+ status = 'error';
+ } else if (warnings.length) {
+ status = 'warning';
+ } else if (meta.touched || (hasFeedback && meta.validated)) {
+ // success feedback should display when pass hasFeedback prop and current value is valid value
+ status = 'success';
+ }
+ return status;
+}
diff --git a/components/grid/index.ts b/components/grid/index.ts
index d8b5a655bdd4..c8310c63c7df 100644
--- a/components/grid/index.ts
+++ b/components/grid/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import Col from './col';
import useInternalBreakpoint from './hooks/useBreakpoint';
import Row from './row';
diff --git a/components/image/index.tsx b/components/image/index.tsx
index e844cc069ccd..3d60dcfb85a0 100644
--- a/components/image/index.tsx
+++ b/components/image/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import * as React from 'react';
import EyeOutlined from '@ant-design/icons/EyeOutlined';
import classNames from 'classnames';
diff --git a/components/index.ts b/components/index.ts
index 0feaaf2b59ff..c88e98b25b93 100644
--- a/components/index.ts
+++ b/components/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
export type { Breakpoint } from './_util/responsiveObserver';
export { default as Affix } from './affix';
export type { AffixProps } from './affix';
diff --git a/components/input-number/index.tsx b/components/input-number/index.tsx
index 3d6aa42af761..31c25e5bcce6 100644
--- a/components/input-number/index.tsx
+++ b/components/input-number/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import DownOutlined from '@ant-design/icons/DownOutlined';
import UpOutlined from '@ant-design/icons/UpOutlined';
import classNames from 'classnames';
diff --git a/components/input/index.ts b/components/input/index.ts
index e82a0cf4b2ca..e0da843930a7 100644
--- a/components/input/index.ts
+++ b/components/input/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type * as React from 'react';
import Group from './Group';
import type { InputProps, InputRef } from './Input';
diff --git a/components/layout/index.ts b/components/layout/index.ts
index a07f39a0b93d..1cd8a7a70e3c 100644
--- a/components/layout/index.ts
+++ b/components/layout/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import InternalLayout, { Content, Footer, Header } from './layout';
import Sider from './Sider';
diff --git a/components/list/index.tsx b/components/list/index.tsx
index 2548438db731..0467245a916a 100644
--- a/components/list/index.tsx
+++ b/components/list/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
// eslint-disable-next-line import/no-named-as-default
import * as React from 'react';
diff --git a/components/locale/index.tsx b/components/locale/index.tsx
index 4d4c356ff86a..b37de4f6bb78 100644
--- a/components/locale/index.tsx
+++ b/components/locale/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import type { ValidateMessages } from 'rc-field-form/lib/interface';
import * as React from 'react';
import warning from '../_util/warning';
diff --git a/components/mentions/index.tsx b/components/mentions/index.tsx
index 2d221fbabe81..a05c0c818c9b 100644
--- a/components/mentions/index.tsx
+++ b/components/mentions/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import RcMentions from 'rc-mentions';
import type {
diff --git a/components/menu/index.tsx b/components/menu/index.tsx
index 623c889ded4c..d745f8048649 100644
--- a/components/menu/index.tsx
+++ b/components/menu/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import type { MenuRef as RcMenuRef } from 'rc-menu';
import { ItemGroup } from 'rc-menu';
import * as React from 'react';
diff --git a/components/message/index.tsx b/components/message/index.tsx
index d3216af7fccc..a93efca4162e 100755
--- a/components/message/index.tsx
+++ b/components/message/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import * as React from 'react';
import { render } from 'rc-util/lib/React/render';
diff --git a/components/modal/index.tsx b/components/modal/index.tsx
index 65a0a60b4342..e28acd113878 100644
--- a/components/modal/index.tsx
+++ b/components/modal/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import type { ModalStaticFunctions } from './confirm';
import confirm, {
modalGlobalConfig,
diff --git a/components/notification/index.tsx b/components/notification/index.tsx
index e6775820fb5b..6826a95a1354 100755
--- a/components/notification/index.tsx
+++ b/components/notification/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import * as React from 'react';
import { render } from 'rc-util/lib/React/render';
diff --git a/components/pagination/index.ts b/components/pagination/index.ts
index 63185d14d7d7..3ea001b0c49a 100644
--- a/components/pagination/index.ts
+++ b/components/pagination/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import Pagination from './Pagination';
export type { PaginationConfig, PaginationProps } from './Pagination';
diff --git a/components/popconfirm/index.tsx b/components/popconfirm/index.tsx
index c8ec0aec5a2c..5e3e8b5661ed 100644
--- a/components/popconfirm/index.tsx
+++ b/components/popconfirm/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
import classNames from 'classnames';
import KeyCode from 'rc-util/lib/KeyCode';
diff --git a/components/popover/index.tsx b/components/popover/index.tsx
index af3f2e5f89f8..42a7ddf9f68a 100644
--- a/components/popover/index.tsx
+++ b/components/popover/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import * as React from 'react';
import type { RenderFunction } from '../_util/getRenderPropValue';
diff --git a/components/progress/index.tsx b/components/progress/index.tsx
index 4311c803bec8..94570e886a37 100644
--- a/components/progress/index.tsx
+++ b/components/progress/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import Progress from './progress';
export type { ProgressAriaProps, ProgressProps } from './progress';
diff --git a/components/qr-code/index.tsx b/components/qr-code/index.tsx
index 79f85f29787d..2801bbe1050e 100644
--- a/components/qr-code/index.tsx
+++ b/components/qr-code/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import ReloadOutlined from '@ant-design/icons/ReloadOutlined';
import classNames from 'classnames';
import { QRCodeCanvas, QRCodeSVG } from 'qrcode.react';
diff --git a/components/radio/index.ts b/components/radio/index.ts
index cb25df7b379f..bae4f15edeac 100644
--- a/components/radio/index.ts
+++ b/components/radio/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type * as React from 'react';
import Group from './group';
import type { RadioProps } from './interface';
diff --git a/components/rate/index.tsx b/components/rate/index.tsx
index 82b74dd202fa..f813658466c3 100644
--- a/components/rate/index.tsx
+++ b/components/rate/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import StarFilled from '@ant-design/icons/StarFilled';
import classNames from 'classnames';
import RcRate from 'rc-rate';
diff --git a/components/result/index.tsx b/components/result/index.tsx
index 07fe5028ad6a..f657dd7f2a28 100644
--- a/components/result/index.tsx
+++ b/components/result/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled';
diff --git a/components/row/index.tsx b/components/row/index.tsx
index b55581603347..81cf63c1f297 100644
--- a/components/row/index.tsx
+++ b/components/row/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import { Row, type RowProps } from '../grid';
export type { RowProps };
diff --git a/components/segmented/index.tsx b/components/segmented/index.tsx
index 3f5391328c51..e607be06f1f5 100644
--- a/components/segmented/index.tsx
+++ b/components/segmented/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import type {
SegmentedLabeledOption as RcSegmentedLabeledOption,
diff --git a/components/select/index.tsx b/components/select/index.tsx
index 22d1342e0f23..601b0832af26 100755
--- a/components/select/index.tsx
+++ b/components/select/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
// TODO: 4.0 - codemod should help to change `filterOption` to support node props.
import classNames from 'classnames';
import type { BaseSelectRef, SelectProps as RcSelectProps } from 'rc-select';
diff --git a/components/skeleton/index.tsx b/components/skeleton/index.tsx
index a37a3c933029..14c93f4d97ad 100644
--- a/components/skeleton/index.tsx
+++ b/components/skeleton/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import Skeleton from './Skeleton';
export type { SkeletonProps } from './Skeleton';
diff --git a/components/slider/index.tsx b/components/slider/index.tsx
index 51f13af2a701..719f3cf583f5 100644
--- a/components/slider/index.tsx
+++ b/components/slider/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import type { SliderProps as RcSliderProps } from 'rc-slider';
import RcSlider from 'rc-slider';
diff --git a/components/space/index.tsx b/components/space/index.tsx
index 27e99033fb0e..8e0eed67b278 100644
--- a/components/space/index.tsx
+++ b/components/space/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import * as React from 'react';
import classNames from 'classnames';
import toArray from 'rc-util/lib/Children/toArray';
diff --git a/components/spin/index.tsx b/components/spin/index.tsx
index 24de3fb8cae4..209d07631f1e 100644
--- a/components/spin/index.tsx
+++ b/components/spin/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import * as React from 'react';
diff --git a/components/statistic/index.tsx b/components/statistic/index.tsx
index c55cd80f62de..dfe3cd00ac13 100644
--- a/components/statistic/index.tsx
+++ b/components/statistic/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import type { CountdownProps } from './Countdown';
import Countdown from './Countdown';
import type { StatisticProps } from './Statistic';
diff --git a/components/steps/index.tsx b/components/steps/index.tsx
index 8be96627e251..62f165604e57 100644
--- a/components/steps/index.tsx
+++ b/components/steps/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import CheckOutlined from '@ant-design/icons/CheckOutlined';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import classNames from 'classnames';
diff --git a/components/style/index.ts b/components/style/index.ts
index 7e9eb6c8e379..43a975d37dda 100644
--- a/components/style/index.ts
+++ b/components/style/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
/* eslint-disable import/prefer-default-export */
import type { CSSObject } from '@ant-design/cssinjs';
import type { AliasToken, DerivativeToken } from '../theme/internal';
diff --git a/components/switch/index.en-US.md b/components/switch/index.en-US.md
index 3f7be9494c4b..e895f9000f12 100644
--- a/components/switch/index.en-US.md
+++ b/components/switch/index.en-US.md
@@ -53,3 +53,15 @@ Common props ref:[Common props](/docs/react/common-props)
## Design Token
+
+## FAQ
+
+### Why not work in Form.Item?
+
+Form.Item default bind value to `value` property, but Switch value property is `checked`. You can use `valuePropName` to change bind property.
+
+```tsx | pure
+
+
+
+```
diff --git a/components/switch/index.tsx b/components/switch/index.tsx
index 7086be661f2a..119a2bfd21fd 100755
--- a/components/switch/index.tsx
+++ b/components/switch/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import classNames from 'classnames';
import RcSwitch from 'rc-switch';
diff --git a/components/switch/index.zh-CN.md b/components/switch/index.zh-CN.md
index ffe1712c68d8..5bc8a0f743d4 100644
--- a/components/switch/index.zh-CN.md
+++ b/components/switch/index.zh-CN.md
@@ -54,3 +54,15 @@ demo:
## 主题变量(Design Token)
+
+## FAQ
+
+### 为什么在 Form.Item 下不能绑定数据?
+
+Form.Item 默认绑定值属性到 `value` 上,而 Switch 的值属性为 `checked`。你可以通过 `valuePropName` 来修改绑定的值属性。
+
+```tsx | pure
+
+
+
+```
diff --git a/components/table/index.ts b/components/table/index.ts
index 8edeb0c04851..36f439e0b83e 100644
--- a/components/table/index.ts
+++ b/components/table/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import { type TablePaginationConfig, type TableProps } from './InternalTable';
import Table from './Table';
diff --git a/components/tabs/index.tsx b/components/tabs/index.tsx
index 54315945f2df..57d163f5ba4e 100755
--- a/components/tabs/index.tsx
+++ b/components/tabs/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import EllipsisOutlined from '@ant-design/icons/EllipsisOutlined';
import PlusOutlined from '@ant-design/icons/PlusOutlined';
diff --git a/components/tag/index.tsx b/components/tag/index.tsx
index d06a0bae1c81..941d9b73a87e 100644
--- a/components/tag/index.tsx
+++ b/components/tag/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import * as React from 'react';
import CloseOutlined from '@ant-design/icons/CloseOutlined';
import classNames from 'classnames';
diff --git a/components/theme/index.ts b/components/theme/index.ts
index a7d15abf12cc..481dd08d8ea5 100644
--- a/components/theme/index.ts
+++ b/components/theme/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
/* eslint-disable import/prefer-default-export */
import getDesignToken from './getDesignToken';
import type { GlobalToken, MappingAlgorithm } from './interface';
diff --git a/components/time-picker/index.tsx b/components/time-picker/index.tsx
index 6f158927bda4..c67926088203 100644
--- a/components/time-picker/index.tsx
+++ b/components/time-picker/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import type { Dayjs } from 'dayjs';
import * as React from 'react';
import genPurePanel from '../_util/PurePanel';
diff --git a/components/timeline/index.ts b/components/timeline/index.ts
index bef127a48cba..236af062115c 100644
--- a/components/timeline/index.ts
+++ b/components/timeline/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import Timeline from './Timeline';
export type { TimelineProps } from './Timeline';
diff --git a/components/tooltip/index.tsx b/components/tooltip/index.tsx
index 537a0de25933..3bff61055e00 100644
--- a/components/tooltip/index.tsx
+++ b/components/tooltip/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import type { BuildInPlacements } from '@rc-component/trigger';
import classNames from 'classnames';
import RcTooltip from 'rc-tooltip';
diff --git a/components/tour/index.tsx b/components/tour/index.tsx
index 7020e1b3e658..3f4c5c9f0762 100644
--- a/components/tour/index.tsx
+++ b/components/tour/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import RCTour from '@rc-component/tour';
import classNames from 'classnames';
import React, { useContext } from 'react';
diff --git a/components/transfer/index.tsx b/components/transfer/index.tsx
index bc8401dd9c95..0dee687fc90b 100644
--- a/components/transfer/index.tsx
+++ b/components/transfer/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import type { ChangeEvent, CSSProperties } from 'react';
import React, { useCallback, useContext } from 'react';
diff --git a/components/tree-select/index.tsx b/components/tree-select/index.tsx
index 7391c9993e37..cf36fea1f81f 100644
--- a/components/tree-select/index.tsx
+++ b/components/tree-select/index.tsx
@@ -1,5 +1,3 @@
-'use client';
-
import classNames from 'classnames';
import type { BaseSelectRef } from 'rc-select';
import type { Placement } from 'rc-select/lib/BaseSelect';
diff --git a/components/tree/index.ts b/components/tree/index.ts
index cab82d96819f..dc7b1bbf5de2 100644
--- a/components/tree/index.ts
+++ b/components/tree/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import type RcTree from 'rc-tree';
import type { BasicDataNode } from 'rc-tree';
import { TreeNode } from 'rc-tree';
diff --git a/components/typography/index.ts b/components/typography/index.ts
index fdae240e06ba..9f9f3084cb7e 100644
--- a/components/typography/index.ts
+++ b/components/typography/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import Link from './Link';
import Paragraph from './Paragraph';
import Text from './Text';
diff --git a/components/upload/index.ts b/components/upload/index.ts
index 6546d8da307c..98a5a8b20095 100644
--- a/components/upload/index.ts
+++ b/components/upload/index.ts
@@ -1,5 +1,3 @@
-'use client';
-
import Dragger from './Dragger';
import type { UploadProps } from './Upload';
import InternalUpload, { LIST_IGNORE } from './Upload';
diff --git a/components/upload/style/list.ts b/components/upload/style/list.ts
index 98b4728f45a0..a986bd2a28ee 100644
--- a/components/upload/style/list.ts
+++ b/components/upload/style/list.ts
@@ -52,7 +52,7 @@ const genListStyle: GenerateStyle = (token) => {
},
[`
- ${actionCls}:focus,
+ ${actionCls}:focus-visible,
&.picture ${actionCls}
`]: {
opacity: 1,
diff --git a/docs/blog/config-provider-style.en-US.md b/docs/blog/config-provider-style.en-US.md
new file mode 100644
index 000000000000..3adddd85f062
--- /dev/null
+++ b/docs/blog/config-provider-style.en-US.md
@@ -0,0 +1,109 @@
+---
+title: Extends Theme
+date: 2023-09-03
+author: zombieJ
+---
+
+Ant Design v5 provides the Design Token model, which supports custom algorithm to implement theme extension capabilities. For example, the compact theme itself does not carry color style algorithms, so it can be implemented by passing in multiple algorithms to achieve the compact theme under the light theme and the compact theme under the dark theme.
+
+Today, we now put down the algorithm part. Talk about how to extend the theme through ConfigProvider.
+
+## An Example
+
+This is an example of using ConfigProvider to extend the theme. You can view the complete code directly [here](https://github.com/zombieJ/antd-geek-theme-sample) ([online demo](https://zombiej.github.io/antd-geek-theme-sample/demos/theme)):
+
+![Geek Theme](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*i3kvR6-tozgAAAAAAAAAAAAADrJ8AQ/original)
+
+We will talk about how to use ConfigProvider to extend the theme in Ant Design. Of course, this article is not a CSS tutorial, so we will not introduce the style implementation above. If you are interested, you can directly look at the code instead.
+
+## Limitation of Token
+
+Design Token has powerful extension capabilities, but it also has limitations. For example, when Token does not support some configurations, developers become powerless. Even worse, some theme implementations cannot rely solely on a certain Token, which will become very difficult. For example, the gradient border colors in the above example cannot be implemented simply by `border-color`, it requires some CSS tricks. As mentioned in ["Happy Work Theme"](/docs/blog/happy-work), landing some specific implementations to Design Token will cause the code quality to deteriorate rapidly. Therefore, we need some other ways to extend the theme, which can uniformly modify the style of a component. And ConfigProvider is such an entry.
+
+## ConfigProvider
+
+In `5.7.0`, ConfigProvider supports the `className` and `style` configurations of all components. So we can easily extend beyond Token:
+
+```tsx
+
+```
+
+And then we can go to add our style:
+
+```less
+.my-button {
+ background: red;
+}
+```
+
+This is actually strange. Since we can modify the style through `className`, why do we need ConfigProvider? We can just override the `.ant-btn` style.
+
+If your project is maintained by only one person, this is a good idea. But if your project is a large project, then you will find that this approach will cause style conflicts. Especially in the case of multi-person collaboration, modifying styles at will will result in unexpected results, and other people have to use more complex selectors to override your styles. ConfigProvider can solve this problem well. It can isolate styles inside ConfigProvider and will not affect other components.
+
+## Theme Extension
+
+Above example looks easy to implement, but in real scenarios you will find that there are some shortcomings for hierarchical structures. For example, the `ant-` prefix can be modified by ConfigProvider's `prefixCls`, so the prefix of the semantic structure may change from `ant-btn-icon` to `abc-btn-icon`. So it is not enough to override only by `my-button`:
+
+```less
+.my-button {
+ // OPS. It's `abc-btn-icon` now.
+ .ant-btn-icon {
+ background: red;
+ }
+}
+```
+
+So our extended theme also needs the ability to consume `prefixCls`. In CSS-in-JS, mixing `prefixCls` is easy. We can get `prefixCls` through the `getPrefixCls` method of ConfigProvider, and then mix it:
+
+```tsx
+// This is an example of using `antd-style`, you can use any CSS-in-JS library.
+import React from 'react';
+import { ConfigProvider } from 'antd';
+import { createStyles } from 'antd-style';
+
+const useButtonStyle = () => {
+ const { getPrefixCls } = React.useContext(ConfigProvider.ConfigContext);
+ const btnPrefixCls = getPrefixCls('btn');
+
+ // Customize styles
+ return createStyles(({ css }) => ({
+ btn: css`
+ background: red;
+ .${btnPrefixCls}-icon {
+ color: green;
+ }
+ `,
+ }))();
+};
+
+function GeekProvider(props: { children?: React.ReactNode }) {
+ const { styles } = useButtonStyle();
+
+ return {props.children};
+}
+```
+
+
+
+It's also easy to extend for scenarios that need to inherit `className`:
+
+```tsx
+function GeekProvider(props: { children?: React.ReactNode }) {
+ const { button } = React.useContext(ConfigProvider.ConfigContext);
+ const { styles } = useButtonStyle();
+
+ return (
+
+ {props.children}
+
+ );
+}
+```
+
+## Summary
+
+Through ConfigProvider, we can further extend the theme. It can isolate styles well and avoid style conflicts. Let's try it out!
diff --git a/docs/blog/config-provider-style.zh-CN.md b/docs/blog/config-provider-style.zh-CN.md
new file mode 100644
index 000000000000..6935ec6cd385
--- /dev/null
+++ b/docs/blog/config-provider-style.zh-CN.md
@@ -0,0 +1,109 @@
+---
+title: 主题拓展
+date: 2023-09-03
+author: zombieJ
+---
+
+Ant Design v5 提供了 Design Token 模型,支持自定义算法实现主题拓展能力。例如 紧凑主题 本身并不携带颜色样式算法,所以可以通过传入多个算法的方式实现 亮色主题下的紧凑主题 以及 暗色主题下的紧凑主题。
+
+而今天,我们现在放下算法部分。讲讲如何通过 ConfigProvider 来拓展主题。
+
+## 一个例子
+
+这是我通过 ConfigProvider 来拓展主题的示例,你可以直接在[这里](https://github.com/zombieJ/antd-geek-theme-sample)查看完整的代码([在线演示](https://zombiej.github.io/antd-geek-theme-sample/demos/theme)):
+
+![Geek Theme](https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*i3kvR6-tozgAAAAAAAAAAAAADrJ8AQ/original)
+
+以下会聊聊在 Ant Design 中如何使用 ConfigProvider 拓展主题。当然这篇文章并不是 CSS 的教程,所以不会去介绍上面的样式实现。如果有兴趣可以直接看看上面的代码地址。
+
+## Token 之痛
+
+Design Token 提供了非常强大的拓展能力,但是同样它也有限制。例如当 Token 并没有支持某些配置时,开发者就变得无能为力了。更有甚者,某些主题实现不能单纯依赖某种 Token 就会变得十分困难。例如在上面例子中的各种渐变边框色不能简单的通过 `border-color` 来实现,它需要一些 CSS 小技巧。而如[《快乐工作主题》](/docs/blog/happy-work)我们提到,将一些具体实现落地到 Design Token 会使得代码质量迅速劣化。因而我们需要一些其他的方式来拓展主题,可以统一的修改某个组件的样式。而 ConfigProvider 就是这样的一个入口。
+
+## ConfigProvider
+
+在 `5.7.0` 中,ConfigProvider 支持了所有组件的 `className` 和 `style` 配置。因此我们可以很容易进行 Token 之外的拓展:
+
+```tsx
+
+```
+
+接着我们就可以去添加我们的样式了:
+
+```less
+.my-button {
+ background: red;
+}
+```
+
+你会发现,这其实奇怪。既然我们可以通过 `className` 来修改样式,那么为什么还需要 ConfigProvider 呢?我们覆盖 `.ant-btn` 样式不就行了。
+
+如果你的项目只由你一个人来维护,这是个不错的主意。但是如果你的项目是一个大型项目,那么你就会发现这样的做法会导致样式冲突。尤其在多人协作的情况下,随意修改样式会出现非预期的结果,而其他人为了覆盖你的样式不得不使用更加复杂的选择器。而 ConfigProvider 则可以很好的解决这个问题,它可以将样式隔离在 ConfigProvider 内部,不会影响到其他组件。
+
+## 主题拓展
+
+上面的示例看起来实现很容易,但是真实场景下你会发现对于层级结构而言不免也有一些不足。比如说 `ant-` 前缀可以通过 ConfigProvider 的 `prefixCls` 修改,所以语义化结构的前缀可能从 `ant-btn-icon` 变成 `abc-btn-icon`。那么仅通过 `my-button` 是不足以实现覆盖的:
+
+```less
+.my-button {
+ // OPS. It's `abc-btn-icon` now.
+ .ant-btn-icon {
+ background: red;
+ }
+}
+```
+
+所以我们的拓展主题也同样需要能够消费 `prefixCls` 的能力。而在 CSS-in-JS 中,混合 `prefixCls` 是很容易的事情。我们可以通过 ConfigProvider 的 `getPrefixCls` 方法来获取 `prefixCls`,然后进行混合:
+
+```tsx
+// This is an example of using `antd-style`, you can use any CSS-in-JS library.
+import React from 'react';
+import { ConfigProvider } from 'antd';
+import { createStyles } from 'antd-style';
+
+const useButtonStyle = () => {
+ const { getPrefixCls } = React.useContext(ConfigProvider.ConfigContext);
+ const btnPrefixCls = getPrefixCls('btn');
+
+ // Customize styles
+ return createStyles(({ css }) => ({
+ btn: css`
+ background: red;
+ .${btnPrefixCls}-icon {
+ color: green;
+ }
+ `,
+ }))();
+};
+
+function GeekProvider(props: { children?: React.ReactNode }) {
+ const { styles } = useButtonStyle();
+
+ return {props.children};
+}
+```
+
+
+
+对需要继承 `className` 的场景,拓展也很容易:
+
+```tsx
+function GeekProvider(props: { children?: React.ReactNode }) {
+ const { button } = React.useContext(ConfigProvider.ConfigContext);
+ const { styles } = useButtonStyle();
+
+ return (
+
+ {props.children}
+
+ );
+}
+```
+
+## 总结
+
+通过 ConfigProvider 可以进一步拓展主题,它可以很好的隔离样式,避免样式冲突。赶快动手试试吧!
diff --git a/docs/blog/github-actions-workflow.en-US.md b/docs/blog/github-actions-workflow.en-US.md
index bb7df0320956..f59dff801d7e 100644
--- a/docs/blog/github-actions-workflow.en-US.md
+++ b/docs/blog/github-actions-workflow.en-US.md
@@ -173,7 +173,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v3
diff --git a/docs/blog/github-actions-workflow.zh-CN.md b/docs/blog/github-actions-workflow.zh-CN.md
index 2fd30a1be1a7..b1dbf7f74e7f 100644
--- a/docs/blog/github-actions-workflow.zh-CN.md
+++ b/docs/blog/github-actions-workflow.zh-CN.md
@@ -176,7 +176,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code (检出代码)
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Node.js (设置 node 版本)
uses: actions/setup-node@v3
diff --git a/package.json b/package.json
index b8b27175f442..cdf75b687a25 100644
--- a/package.json
+++ b/package.json
@@ -161,7 +161,7 @@
"devDependencies": {
"@ant-design/compatible": "^5.1.2",
"@ant-design/happy-work-theme": "^1.0.0",
- "@ant-design/tools": "^17.0.0",
+ "@ant-design/tools": "^17.3.1",
"@antv/g6": "^4.8.13",
"@argos-ci/core": "^0.9.0",
"@babel/eslint-plugin": "^7.19.1",
@@ -194,11 +194,9 @@
"@types/jest-image-snapshot": "^6.1.0",
"@types/jquery": "^3.5.14",
"@types/lodash": "^4.14.139",
- "@types/lz-string": "^1.3.34",
"@types/node": "^20.0.0",
"@types/prismjs": "^1.26.0",
"@types/progress": "^2.0.5",
- "@types/puppeteer": "^7.0.4",
"@types/qs": "^6.9.7",
"@types/react": "^18.0.0",
"@types/react-copy-to-clipboard": "^5.0.0",
@@ -271,6 +269,7 @@
"pretty-format": "^29.0.0",
"prismjs": "^1.29.0",
"progress": "^2.0.3",
+ "puppeteer": "^21.1.1",
"qs": "^6.10.1",
"rc-footer": "^0.6.8",
"rc-tween-one": "^3.0.3",
diff --git a/tests/dekko/use-client.test.js b/tests/dekko/use-client.test.js
new file mode 100644
index 000000000000..99f6aae9fd34
--- /dev/null
+++ b/tests/dekko/use-client.test.js
@@ -0,0 +1,32 @@
+const $ = require('dekko');
+const chalk = require('chalk');
+const fs = require('fs');
+
+const includeUseClient = (filename) =>
+ fs.readFileSync(filename).toString().includes('"use client"');
+
+if (process.env.LIB_DIR === 'dist') {
+ $('dist/*')
+ .isFile()
+ .assert("doesn't contain use client", (filename) => !includeUseClient(filename));
+} else {
+ $('{es,lib}/index.js')
+ .isFile()
+ .assert('contain use client', (filename) => includeUseClient(filename));
+
+ $('{es,lib}/*/index.js')
+ .isFile()
+ .assert('contain use client', (filename) => includeUseClient(filename));
+
+ // check tsx files
+ $('{es,lib}/typography/*.js')
+ .isFile()
+ .assert('contain use client', (filename) => includeUseClient(filename));
+
+ $('{es,lib}/typography/Base/*.js')
+ .isFile()
+ .assert('contain use client', (filename) => includeUseClient(filename));
+}
+
+// eslint-disable-next-line no-console
+console.log(chalk.green('✨ use client passed!'));
diff --git a/tests/shared/imageTest.tsx b/tests/shared/imageTest.tsx
index 372e1a18fed6..ab723470fd65 100644
--- a/tests/shared/imageTest.tsx
+++ b/tests/shared/imageTest.tsx
@@ -75,16 +75,20 @@ export default function imageTest(component: React.ReactElement) {
styleStr,
);
- await page.waitForSelector('#end-of-screen');
+ await page.waitForSelector('#end-of-screen', {
+ timeout: 0,
+ });
const image = await page.screenshot({
fullPage: true,
+ captureBeyondViewport: true,
+ optimizeForSpeed: true,
});
expect(image).toMatchImageSnapshot();
MockDate.reset();
- page.removeListener('request', onRequestHandle);
+ page.off('request', onRequestHandle);
});
}