|
1 |
| -import { useMemo, useRef, useEffect } from "react"; |
| 1 | +import { |
| 2 | + useMemo, |
| 3 | + useRef, |
| 4 | + useEffect, |
| 5 | + forwardRef, |
| 6 | + useImperativeHandle, |
| 7 | +} from "react"; |
2 | 8 | import { Stack, Typography, Box, ThemeProvider } from "@mui/material";
|
3 | 9 | import DangerousRoundedIcon from "@mui/icons-material/DangerousRounded";
|
4 | 10 | import { theme } from "@zesty-io/material";
|
@@ -64,97 +70,115 @@ const getErrorMessage = (errors: Error) => {
|
64 | 70 | return errorMessages;
|
65 | 71 | };
|
66 | 72 |
|
67 |
| -export const FieldError = ({ errors, fields }: FieldErrorProps) => { |
68 |
| - const errorContainerEl = useRef(null); |
69 |
| - |
70 |
| - // Scroll to the errors on mount |
71 |
| - useEffect(() => { |
72 |
| - errorContainerEl?.current?.scrollIntoView({ |
73 |
| - behavior: "smooth", |
74 |
| - block: "center", |
75 |
| - inline: "center", |
76 |
| - }); |
77 |
| - }, []); |
78 |
| - |
79 |
| - const fieldErrors = useMemo(() => { |
80 |
| - const errorMap = Object.entries(errors)?.map(([name, errorDetails]) => { |
81 |
| - const errorMessages = getErrorMessage(errorDetails); |
82 |
| - |
83 |
| - const fieldData = fields?.find((field) => field.name === name); |
84 |
| - |
85 |
| - return { |
86 |
| - label: |
87 |
| - fieldData?.label || |
88 |
| - SEO_FIELD_LABELS[name as keyof typeof SEO_FIELD_LABELS], |
89 |
| - errorMessages, |
90 |
| - sort: fieldData?.sort, |
91 |
| - ZUID: fieldData?.ZUID || name, |
92 |
| - }; |
93 |
| - }); |
94 |
| - |
95 |
| - return errorMap.sort((a, b) => a.sort - b.sort); |
96 |
| - }, [errors, fields]); |
97 |
| - |
98 |
| - const fieldsWithErrors = fieldErrors?.filter( |
99 |
| - (error) => error.errorMessages.length > 0 |
100 |
| - ); |
101 |
| - |
102 |
| - const handleErrorClick = (fieldZUID: string) => { |
103 |
| - const fieldElement = document.getElementById(fieldZUID); |
104 |
| - fieldElement?.scrollIntoView({ behavior: "smooth" }); |
105 |
| - }; |
106 |
| - |
107 |
| - return ( |
108 |
| - <ThemeProvider theme={theme}> |
109 |
| - <Stack |
110 |
| - data-cy="FieldErrorsList" |
111 |
| - ref={errorContainerEl} |
112 |
| - p={2} |
113 |
| - gap={1} |
114 |
| - borderRadius={1} |
115 |
| - sx={{ backgroundColor: "red.50", color: "error.dark" }} |
116 |
| - > |
117 |
| - <DangerousRoundedIcon color="inherit" fontSize="small" /> |
118 |
| - <Typography variant="h6"> |
119 |
| - Item cannot be saved due to invalid field values. |
120 |
| - </Typography> |
121 |
| - <Typography variant="body2"> |
122 |
| - Please correct the following {fieldsWithErrors?.length} field |
123 |
| - {fieldsWithErrors?.length > 1 && "s"} before saving: |
124 |
| - </Typography> |
125 |
| - <Box component="ol" ml={2}> |
126 |
| - {fieldErrors?.map((error, index) => { |
127 |
| - if (error.errorMessages.length > 0) { |
128 |
| - return ( |
129 |
| - <Typography key={index} variant="body2" component="li"> |
130 |
| - <Box |
131 |
| - sx={{ |
132 |
| - borderBottom: 1, |
133 |
| - borderColor: "error.dark", |
134 |
| - cursor: "pointer", |
135 |
| - height: 16, |
136 |
| - display: "inline-block", |
137 |
| - }} |
138 |
| - component="span" |
139 |
| - onClick={() => handleErrorClick(error.ZUID)} |
140 |
| - > |
141 |
| - {error.label} |
142 |
| - </Box> |
143 |
| - {error.errorMessages.length === 1 ? ( |
144 |
| - <i> - {error.errorMessages[0]}</i> |
145 |
| - ) : ( |
146 |
| - <Box component="ul" sx={{ pl: 3, listStyleType: "disc" }}> |
147 |
| - {error.errorMessages.map((msg, idx) => ( |
148 |
| - <li key={idx}>{msg}</li> |
149 |
| - ))} |
| 73 | +export const FieldError = forwardRef( |
| 74 | + ({ errors, fields }: FieldErrorProps, ref) => { |
| 75 | + const errorContainerEl = useRef(null); |
| 76 | + |
| 77 | + useImperativeHandle( |
| 78 | + ref, |
| 79 | + () => { |
| 80 | + return { |
| 81 | + scrollToErrors() { |
| 82 | + errorContainerEl?.current?.scrollIntoView({ |
| 83 | + behavior: "smooth", |
| 84 | + block: "center", |
| 85 | + inline: "center", |
| 86 | + }); |
| 87 | + }, |
| 88 | + }; |
| 89 | + }, |
| 90 | + [errorContainerEl] |
| 91 | + ); |
| 92 | + |
| 93 | + // Scroll to the errors on mount |
| 94 | + useEffect(() => { |
| 95 | + errorContainerEl?.current?.scrollIntoView({ |
| 96 | + behavior: "smooth", |
| 97 | + block: "center", |
| 98 | + inline: "center", |
| 99 | + }); |
| 100 | + }, []); |
| 101 | + |
| 102 | + const fieldErrors = useMemo(() => { |
| 103 | + const errorMap = Object.entries(errors)?.map(([name, errorDetails]) => { |
| 104 | + const errorMessages = getErrorMessage(errorDetails); |
| 105 | + |
| 106 | + const fieldData = fields?.find((field) => field.name === name); |
| 107 | + |
| 108 | + return { |
| 109 | + label: |
| 110 | + fieldData?.label || |
| 111 | + SEO_FIELD_LABELS[name as keyof typeof SEO_FIELD_LABELS], |
| 112 | + errorMessages, |
| 113 | + sort: fieldData?.sort, |
| 114 | + ZUID: fieldData?.ZUID || name, |
| 115 | + }; |
| 116 | + }); |
| 117 | + |
| 118 | + return errorMap.sort((a, b) => a.sort - b.sort); |
| 119 | + }, [errors, fields]); |
| 120 | + |
| 121 | + const fieldsWithErrors = fieldErrors?.filter( |
| 122 | + (error) => error.errorMessages.length > 0 |
| 123 | + ); |
| 124 | + |
| 125 | + const handleErrorClick = (fieldZUID: string) => { |
| 126 | + const fieldElement = document.getElementById(fieldZUID); |
| 127 | + fieldElement?.scrollIntoView({ behavior: "smooth" }); |
| 128 | + }; |
| 129 | + |
| 130 | + return ( |
| 131 | + <ThemeProvider theme={theme}> |
| 132 | + <Stack |
| 133 | + data-cy="FieldErrorsList" |
| 134 | + ref={errorContainerEl} |
| 135 | + p={2} |
| 136 | + gap={1} |
| 137 | + borderRadius={1} |
| 138 | + sx={{ backgroundColor: "red.50", color: "error.dark" }} |
| 139 | + > |
| 140 | + <DangerousRoundedIcon color="inherit" fontSize="small" /> |
| 141 | + <Typography variant="h6"> |
| 142 | + Item cannot be saved due to invalid field values. |
| 143 | + </Typography> |
| 144 | + <Typography variant="body2"> |
| 145 | + Please correct the following {fieldsWithErrors?.length} field |
| 146 | + {fieldsWithErrors?.length > 1 && "s"} before saving: |
| 147 | + </Typography> |
| 148 | + <Box component="ol" ml={2}> |
| 149 | + {fieldErrors?.map((error, index) => { |
| 150 | + if (error.errorMessages.length > 0) { |
| 151 | + return ( |
| 152 | + <Typography key={index} variant="body2" component="li"> |
| 153 | + <Box |
| 154 | + sx={{ |
| 155 | + borderBottom: 1, |
| 156 | + borderColor: "error.dark", |
| 157 | + cursor: "pointer", |
| 158 | + height: 16, |
| 159 | + display: "inline-block", |
| 160 | + }} |
| 161 | + component="span" |
| 162 | + onClick={() => handleErrorClick(error.ZUID)} |
| 163 | + > |
| 164 | + {error.label} |
150 | 165 | </Box>
|
151 |
| - )} |
152 |
| - </Typography> |
153 |
| - ); |
154 |
| - } |
155 |
| - })} |
156 |
| - </Box> |
157 |
| - </Stack> |
158 |
| - </ThemeProvider> |
159 |
| - ); |
160 |
| -}; |
| 166 | + {error.errorMessages.length === 1 ? ( |
| 167 | + <i> - {error.errorMessages[0]}</i> |
| 168 | + ) : ( |
| 169 | + <Box component="ul" sx={{ pl: 3, listStyleType: "disc" }}> |
| 170 | + {error.errorMessages.map((msg, idx) => ( |
| 171 | + <li key={idx}>{msg}</li> |
| 172 | + ))} |
| 173 | + </Box> |
| 174 | + )} |
| 175 | + </Typography> |
| 176 | + ); |
| 177 | + } |
| 178 | + })} |
| 179 | + </Box> |
| 180 | + </Stack> |
| 181 | + </ThemeProvider> |
| 182 | + ); |
| 183 | + } |
| 184 | +); |
0 commit comments