Repository: arco-design/arco-design (ts)

Issue:
form表单项目如果设置了rules={[{ required: false }]}也会导致出现.arco-form-item-status-warning（黄底）样式
- [ ] I'm sure this does not appear in [the issue list of the repository](https://github.com/arco-design/arco-design/issues)

## Basic Info 
- **Package Name And Version:** @arco-design/web-react: 2.26.0
- **System:** MAC
- **Browser:** CHROME
- **Reproduction Link:** https://codesandbox.io/s/interesting-snowflake-4hfi2?file=/index.js:2803-2832

## What is expected?
灰底

## Steps to reproduce
1. form表单项目设置field和rules={[{ required: false }]}
2. form.submit



<!-- generated by arco-issue. DO NOT REMOVE -->

Pull request:
fix: Form validate status bug
<!--
  Thanks so much for your PR and contribution.

  Before submitting, please make sure to follow the Pull Request Guidelines: https://github.com/arco-design/arco-design/blob/main/CONTRIBUTING.md
-->

<!-- Put an `x` in "[ ]" to check a box) -->

## Types of changes

<!-- What types of changes does this PR introduce -->
- [ ] New feature
- [x] Bug fix
- [ ] Documentation change
- [ ] Coding style change
- [ ] Component style change
- [ ] Refactoring
- [ ] Performance improvement
- [ ] Test cases
- [ ] Continuous integration
- [ ] Typescript definition change
- [ ] Breaking change
- [ ] Others 

## Background and context

<!-- Explain what problem does the PR solve -->
<!-- Link to related open issues if applicable -->

## Solution

<!-- Describe how the problem is fixed in detail -->

## How is the change tested?

<!-- Unit tests should be added/updated for bug fixes and new features, if applicable -->
<!-- Please describe how you tested the change. E.g. Creating/updating unit tests or attaching a screenshot of how it works with your change -->

## Changelog

| Component | Changelog(CN) | Changelog(EN) | Related issues |
| --------- | ------------- | ------------- | -------------- |
|   Form        |     修复 `Form` 组件教验通过时表现出 `warning` 样式的 bug。          |   Fix the bug that the `warning` style of the `Form` component is shown when the teaching experience is passed.          |  close #279                |

## Checklist:

- [ ] Test suite passes (`npm run test`)
- [ ] Provide changelog for relevant changes (e.g. bug fixes and new features) if applicable.
- [ ] Changes are submitted to the appropriate branch (e.g. features should be submitted to `feature` branch and others should be submitted to `main` branch)

## Other information

<!-- Please describe what other information that should be taken care of. E.g. describe the impact if introduce a breaking change -->


Patch:
diff --git a/components/Form/control.tsx b/components/Form/control.tsx
index c1dfd684..c622c7bc 100644
--- a/components/Form/control.tsx
+++ b/components/Form/control.tsx
@@ -41,7 +41,7 @@ export default class Control<
 
   private errors: FieldError<FieldValue> = null;
 
-  private warnings: React.ReactNode = null;
+  private warnings: React.ReactNode[] = null;
 
   private isDestroyed = false;
 
@@ -149,7 +149,7 @@ export default class Control<
             this.touched = info.data.touched;
           }
           if (info.data && 'warnings' in info.data) {
-            this.warnings = info.data.warnings;
+            this.warnings = [].concat(info.data.warnings);
           }
           if (info.data && 'errors' in info.data) {
             this.errors = info.data.errors;
diff --git a/components/Form/form-item.tsx b/components/Form/form-item.tsx
index ce634070..d3424373 100644
--- a/components/Form/form-item.tsx
+++ b/components/Form/form-item.tsx
@@ -47,13 +47,11 @@ const FormItemTip: React.FC<FormItemTipProps> = ({
   });
   const warningTip = [];
   warnings.map((item, i) => {
-    if (item) {
-      warningTip.push(
-        <div key={i} className={`${prefixCls}-message-help_warning`}>
-          {item}
-        </div>
-      );
-    }
+    warningTip.push(
+      <div key={i} className={`${prefixCls}-message-help-warning`}>
+        {item}
+      </div>
+    );
   });
   const isHelpTip = !isUndefined(help) || !!warningTip.length;
   const visible = isHelpTip || !!errorTip.length;
@@ -94,7 +92,7 @@ const Item = <
     [key: string]: FieldError<FieldValue>;
   }>(null);
   const [warnings, setWarnings] = useState<{
-    [key: string]: ReactNode;
+    [key: string]: ReactNode[];
   }>(null);
   const formContext = useContext(FormContext);
   const prefixCls = formContext.prefixCls || getPrefixCls('form');
@@ -107,7 +105,7 @@ const Item = <
     field: string,
     params: {
       errors?: FieldError<FieldValue>;
-      warnings?: ReactNode;
+      warnings?: ReactNode[];
     } = {}
   ) => {
     if (isDestroyed.current) {
@@ -126,7 +124,7 @@ const Item = <
     });
     setWarnings((current) => {
       const newVal = { ...(current || {}) };
-      if (warnings) {
+      if (warnings && warnings.length) {
         newVal[field] = warnings;
       } else {
         delete newVal[field];
@@ -160,21 +158,26 @@ const Item = <
     [`${prefixCls}-label-item-left`]: labelAlign === 'left',
   });
 
+  const errorInfo = errors ? Object.values(errors) : [];
+  const warningInfo = warnings
+    ? Object.values(warnings).reduce((total, next) => total.concat(next), [])
+    : [];
+
   const itemStatus = useMemo(() => {
     if (validateStatus) {
       return validateStatus;
     }
-    if (errors && Object.values(errors).length) {
+    if (errorInfo.length) {
       return VALIDATE_STATUS.error;
     }
-    if (warnings && Object.values(warnings).length) {
+    if (warningInfo.length) {
       return VALIDATE_STATUS.warning;
     }
     return undefined;
   }, [errors, warnings, validateStatus]);
 
   const hasHelp = useMemo(() => {
-    return !isUndefined(props.help) || (warnings && Object.values(warnings).length > 0);
+    return !isUndefined(props.help) || warningInfo.length > 0;
   }, [props.help, warnings]);
 
   const classNames = cs(
@@ -329,8 +332,8 @@ const Item = <
               <FormItemTip
                 prefixCls={prefixCls}
                 help={props.help}
-                errors={(errors && Object.values(errors)) || []}
-                warnings={(warnings && Object.values(warnings)) || []}
+                errors={errorInfo}
+                warnings={warningInfo}
               />
               {extra && <div className={`${prefixCls}-extra`}>{extra}</div>}
             </Col>
diff --git a/components/Form/interface.tsx b/components/Form/interface.tsx
index 33fe6788..ed47ab33 100644
--- a/components/Form/interface.tsx
+++ b/components/Form/interface.tsx
@@ -430,7 +430,7 @@ export type FormItemContextProps<
 > = FormContextProps<FormData, FieldValue, FieldKey> & {
   updateFormItem?: (
     field: string,
-    params: { errors?: FieldError<FieldValue>; warnings?: ReactNode }
+    params: { errors?: FieldError<FieldValue>; warnings?: ReactNode[] }
   ) => void;
 };
 
diff --git a/components/Form/style/status.less b/components/Form/style/status.less
index 2099cc0b..17e85fd9 100644
--- a/components/Form/style/status.less
+++ b/components/Form/style/status.less
@@ -114,7 +114,7 @@
     .@{form-prefix-cls}-message-help {
       color: ~'@{form-color-tip-text_@{status}}';
 
-      .@{form-prefix-cls}-message-help_warning {
+      .@{form-prefix-cls}-message-help-warning {
         color: @form-color-tip-text_warning;
       }
     }
diff --git a/components/Select/style/index.less b/components/Select/style/index.less
index 7ea9e495..5eeda3a9 100644
--- a/components/Select/style/index.less
+++ b/components/Select/style/index.less
@@ -71,6 +71,7 @@
       &-size-@{size}&-multiple {
         .@{prefixCls}-view {
           height: auto;
+          font-size: @font-size;
           padding: 0 @multi-padding;
           line-height: 0;
         }
diff --git a/components/Trigger/index.tsx b/components/Trigger/index.tsx
index b3eee2f3..20b725cb 100644
--- a/components/Trigger/index.tsx
+++ b/components/Trigger/index.tsx
@@ -306,7 +306,9 @@ class Trigger extends PureComponent<TriggerProps, TriggerState> {
   };
 
   isClickToHide = () => {
-    return this.isClickTrigger() && this.getMergedProps().clickToClose;
+    return (
+      (this.isClickTrigger() || this.isContextMenuTrigger()) && this.getMergedProps().clickToClose
+    );
   };
 
   isBlurToHide = () => {
@@ -628,6 +630,18 @@ class Trigger extends PureComponent<TriggerProps, TriggerState> {
     }
   };
 
+  hideContextMenu = (e) => {
+    const { popupVisible } = this.state;
+    if (popupVisible) {
+      this.mousedownToHide = true;
+    }
+    this.triggerPropsEvent('onClick', e);
+
+    if (this.isClickToHide() && popupVisible) {
+      this.setPopupVisible(!popupVisible, 0);
+    }
+  };
+
   onClick = (e) => {
     const { popupVisible } = this.state;
     if (popupVisible) {
@@ -848,13 +862,14 @@ class Trigger extends PureComponent<TriggerProps, TriggerState> {
 
     if (this.isContextMenuTrigger() && !disabled) {
       mergeProps.onContextMenu = this.onContextMenu;
+      mergeProps.onClick = this.hideContextMenu;
     } else {
       mergeProps.onContextMenu = this.triggerOriginEvent('onContextMenu');
     }
     if (this.isClickTrigger() && !disabled) {
       mergeProps.onClick = this.onClick;
     } else {
-      mergeProps.onClick = this.triggerOriginEvent('onClick');
+      mergeProps.onClick = mergeProps.onClick || this.triggerOriginEvent('onClick');
     }
     if (this.isFocusTrigger() && !disabled) {
       mergeProps.onFocus = this.onFocus;


Test patch:
diff --git a/components/Form/__test__/validate.test.tsx b/components/Form/__test__/validate.test.tsx
index 91dff6e7..1c0dd7ac 100644
--- a/components/Form/__test__/validate.test.tsx
+++ b/components/Form/__test__/validate.test.tsx
@@ -77,4 +77,69 @@ describe('validate form', () => {
     expect(mockBlurFn).toHaveBeenCalledTimes(3);
     expect(mockClickFn).toHaveBeenCalledTimes(2);
   });
+
+  it('validateLevel in rules', async () => {
+    let form;
+    const wrapper = mount(
+      <Form ref={(node) => (form = node)} style={{ width: 600 }}>
+        <Form.Item
+          label="url"
+          field="url"
+          required
+          rules={[
+            {
+              type: 'email',
+              validateLevel: 'warning',
+            },
+            {
+              required: true,
+              type: 'string',
+              minLength: 6,
+            },
+          ]}
+        >
+          <Input placeholder="please enter your username" />
+        </Form.Item>
+      </Form>
+    );
+
+    const input = wrapper.find('input');
+    await act(() => {
+      input.simulate('change', {
+        target: {
+          value: 'Hello',
+        },
+      });
+    });
+
+    await sleep(100);
+    wrapper.update();
+
+    expect(wrapper.find('.arco-form-message').at(0).html()).toBe(
+      '<div class="arco-form-message arco-form-message-help"><div>Expect min length 6 but got 5</div><div class="arco-form-message-help-warning">Expect type email but got `Hello`</div></div>'
+    );
+    expect(wrapper.find('.arco-form-message-help-warning')).toHaveLength(1); // warning 信息
+
+    await act(() => {
+      input.simulate('change', {
+        target: {
+          value: 'Helloo',
+        },
+      });
+    });
+    await sleep(10);
+    wrapper.update();
+
+    expect(wrapper.find('.arco-form-item-status-error')).toHaveLength(0);
+    expect(wrapper.find('.arco-form-message-help-warning')).toHaveLength(1);
+
+    form.setFields({
+      url: {
+        warning: 'hahahaha',
+      },
+    });
+    wrapper.update();
+
+    expect(wrapper.find('.arco-form-message-help-warning').at(0).text()).toBe('hahahaha');
+  });
 });