Skip to content

Commit

Permalink
feat: custom feedback icons (ant-design#43894)
Browse files Browse the repository at this point in the history
* custom feedback icons initial

(cherry picked from commit 22e43ad)

* tests added and snaps updated

* Revert "tests added and snaps updated"

This reverts commit 13b57be.

* unittest and documentation changes

* feedback items could be turn off

* documentation fix

* move feedback icons object into the hasFeedback prop

* feedbackIcons added to the form element

* test: commit trigger

* fix: failed form test

* snaps updated

* Update components/form/index.en-US.md

Co-authored-by: afc163 <[email protected]>
Signed-off-by: Gunay <[email protected]>

* Update components/form/index.en-US.md

Co-authored-by: afc163 <[email protected]>
Signed-off-by: Gunay <[email protected]>

* Update components/form/index.zh-CN.md

Co-authored-by: afc163 <[email protected]>
Signed-off-by: Gunay <[email protected]>

* Update components/form/demo/custom-feedback-icons.md

Signed-off-by: afc163 <[email protected]>

* Update components/form/demo/custom-feedback-icons.md

Signed-off-by: afc163 <[email protected]>

---------

Signed-off-by: Gunay <[email protected]>
Signed-off-by: afc163 <[email protected]>
Co-authored-by: afc163 <[email protected]>
  • Loading branch information
gldio and afc163 authored Sep 4, 2023
1 parent 4c91896 commit 46341b1
Show file tree
Hide file tree
Showing 11 changed files with 494 additions and 18 deletions.
16 changes: 15 additions & 1 deletion components/form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import useFormWarning from './hooks/useFormWarning';
import type { FormLabelAlign } from './interface';
import useStyle from './style';
import ValidateMessagesContext from './validateMessagesContext';
import type { FeedbackIcons } from './FormItem';

export type RequiredMark =
| boolean
Expand All @@ -35,6 +36,7 @@ export interface FormProps<Values = any> extends Omit<RcFormProps<Values>, 'form
labelCol?: ColProps;
wrapperCol?: ColProps;
form?: FormInstance<Values>;
feedbackIcons?: FeedbackIcons;
size?: SizeType;
disabled?: boolean;
scrollToFirstError?: Options | boolean;
Expand Down Expand Up @@ -67,6 +69,7 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
onFinishFailed,
name,
style,
feedbackIcons,
...restFormProps
} = props;

Expand Down Expand Up @@ -132,8 +135,19 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
requiredMark: mergedRequiredMark,
itemRef: __INTERNAL__.itemRef,
form: wrapForm,
feedbackIcons,
}),
[name, labelAlign, labelCol, wrapperCol, layout, mergedColon, mergedRequiredMark, wrapForm],
[
name,
labelAlign,
labelCol,
wrapperCol,
layout,
mergedColon,
mergedRequiredMark,
wrapForm,
feedbackIcons,
],
);

React.useImperativeHandle(ref, () => wrapForm);
Expand Down
29 changes: 17 additions & 12 deletions components/form/FormItem/ItemHolder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function ItemHolder(props: ItemHolderProps) {
} = props;

const itemPrefixCls = `${prefixCls}-item`;
const { requiredMark } = React.useContext(FormContext);
const { requiredMark, feedbackIcons } = React.useContext(FormContext);

// ======================== Margin ========================
const itemRef = React.useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -111,24 +111,29 @@ export default function ItemHolder(props: ItemHolderProps) {
const formItemStatusContext = React.useMemo<FormItemStatusContextProps>(() => {
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 = IconNode ? (
<span
className={classNames(
`${itemPrefixCls}-feedback-icon`,
`${itemPrefixCls}-feedback-icon-${mergedValidateStatus}`,
)}
>
<IconNode />
</span>
) : null;
feedbackIcon =
customIconNode !== false && IconNode ? (
<span
className={classNames(
`${itemPrefixCls}-feedback-icon`,
`${itemPrefixCls}-feedback-icon-${mergedValidateStatus}`,
)}
>
{customIconNode || <IconNode />}
</span>
) : null;
}

return {
status: mergedValidateStatus,
errors,
warnings,
hasFeedback,
hasFeedback: !!hasFeedback,
feedbackIcon,
isFormItemInput: true,
};
Expand Down
8 changes: 7 additions & 1 deletion components/form/FormItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ type RenderChildren<Values = any> = (form: FormInstance<Values>) => React.ReactN
type RcFieldProps<Values = any> = Omit<FieldProps<Values>, 'children'>;
type ChildrenType<Values = any> = RenderChildren<Values> | React.ReactNode;

export type FeedbackIcons = (itemStatus: {
status: ValidateStatus;
errors?: React.ReactNode[];
warnings?: React.ReactNode[];
}) => { [key in ValidateStatus]?: React.ReactNode };

interface MemoInputProps {
value: any;
update: any;
Expand Down Expand Up @@ -61,7 +67,7 @@ export interface FormItemProps<Values = any>
rootClassName?: string;
children?: ChildrenType<Values>;
id?: string;
hasFeedback?: boolean;
hasFeedback?: boolean | { icons: FeedbackIcons };
validateStatus?: ValidateStatus;
required?: boolean;
hidden?: boolean;
Expand Down
171 changes: 171 additions & 0 deletions components/form/__tests__/__snapshots__/demo-extend.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2215,6 +2215,177 @@ exports[`renders components/form/demo/control-ref.tsx extend context correctly 1

exports[`renders components/form/demo/control-ref.tsx extend context correctly 2`] = `[]`;

exports[`renders components/form/demo/custom-feedback-icons.tsx extend context correctly 1`] = `
<form
class="ant-form ant-form-horizontal"
id="custom-feedback-icons"
style="max-width: 600px;"
>
<div
class="ant-form-item acss-140b0ev ant-form-item-with-help"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label"
>
<label
class="ant-form-item-required"
for="custom-feedback-icons_custom-feedback-test-item"
title="Test"
>
Test
</label>
</div>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-has-feedback"
>
<input
aria-required="true"
class="ant-input"
id="custom-feedback-icons_custom-feedback-test-item"
type="text"
value=""
/>
<span
class="ant-input-suffix"
/>
</span>
</div>
</div>
<div
style="display: flex; flex-wrap: nowrap;"
>
<div
class="ant-form-item-explain ant-form-show-help-appear ant-form-show-help-appear-start ant-form-show-help ant-form-item-explain-connected"
id="custom-feedback-icons_custom-feedback-test-item_help"
role="alert"
>
<div
class="ant-form-show-help-item-appear ant-form-show-help-item-appear-start ant-form-show-help-item"
style="height: 0px; opacity: 0;"
/>
</div>
<div
style="width: 0px; height: 24px;"
/>
</div>
</div>
</div>
<div
class="ant-form-item-margin-offset"
style="margin-bottom: -24px;"
/>
</div>
<div
class="ant-form-item acss-140b0ev ant-form-item-with-help"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label"
>
<label
class="ant-form-item-required"
for="custom-feedback-icons_custom-feedback-test-item2"
title="Test"
>
Test
</label>
</div>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<span
class="ant-input-affix-wrapper ant-input-affix-wrapper-has-feedback"
>
<input
aria-required="true"
class="ant-input"
id="custom-feedback-icons_custom-feedback-test-item2"
type="text"
value=""
/>
<span
class="ant-input-suffix"
/>
</span>
</div>
</div>
<div
style="display: flex; flex-wrap: nowrap;"
>
<div
class="ant-form-item-explain ant-form-show-help-appear ant-form-show-help-appear-start ant-form-show-help ant-form-item-explain-connected"
id="custom-feedback-icons_custom-feedback-test-item2_help"
role="alert"
>
<div
class="ant-form-show-help-item-appear ant-form-show-help-item-appear-start ant-form-show-help-item"
style="height: 0px; opacity: 0;"
/>
</div>
<div
style="width: 0px; height: 24px;"
/>
</div>
</div>
</div>
<div
class="ant-form-item-margin-offset"
style="margin-bottom: -24px;"
/>
</div>
<div
class="ant-form-item"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<button
class="ant-btn ant-btn-default"
type="submit"
>
<span>
Submit
</span>
</button>
</div>
</div>
</div>
</div>
</div>
</form>
`;

exports[`renders components/form/demo/custom-feedback-icons.tsx extend context correctly 2`] = `[]`;

exports[`renders components/form/demo/customized-form-controls.tsx extend context correctly 1`] = `
<form
class="ant-form ant-form-inline"
Expand Down
Loading

0 comments on commit 46341b1

Please sign in to comment.