diff --git a/apps/website/screens/guidelines/localization/LocalizationPage.tsx b/apps/website/screens/guidelines/localization/LocalizationPage.tsx
index 7b2ecd52c..b51bc31f2 100644
--- a/apps/website/screens/guidelines/localization/LocalizationPage.tsx
+++ b/apps/website/screens/guidelines/localization/LocalizationPage.tsx
@@ -82,21 +82,25 @@ const sections = [
- lengthErrorMessage
+ maxLengthErrorMessage
|
- Min length minLength, max length maxLength.
+ The maximum length is maxLength.
|
- It is a function that receives two parameters (minlength and maxlength) and returns the text with
- those parameters.
+ It is a function that receives a parameter (maxlength) and returns the text with this parameter.
|
- logoAlternativeText
+ minLengthErrorMessage
+ |
+
+ The minimum length is minLength.
+ |
+
+ It is a function that receives a parameter (minlength) and returns the text with this parameter.
|
- Logo |
diff --git a/packages/lib/src/common/utils.ts b/packages/lib/src/common/utils.ts
index c861119b5..13ececd8f 100644
--- a/packages/lib/src/common/utils.ts
+++ b/packages/lib/src/common/utils.ts
@@ -17,3 +17,25 @@ export const getMargin = (marginProp: Space | Margin | undefined, side: Side) =>
: marginProp && typeof marginProp === "string"
? spaces[marginProp]
: "0px";
+
+export const getLengthErrorMessage = ({
+ value,
+ minLength,
+ maxLength,
+ minLengthErrorMessage,
+ maxLengthErrorMessage,
+}: {
+ value: string;
+ minLength?: number;
+ maxLength?: number;
+ minLengthErrorMessage: (minLength: number) => string;
+ maxLengthErrorMessage: (maxLength: number) => string;
+}) => {
+ if (minLength != null && value.length < minLength && minLengthErrorMessage) {
+ return minLengthErrorMessage(minLength);
+ }
+ if (maxLength != null && value.length > maxLength && maxLengthErrorMessage) {
+ return maxLengthErrorMessage(maxLength);
+ }
+ return undefined;
+};
diff --git a/packages/lib/src/common/variables.ts b/packages/lib/src/common/variables.ts
index 3195db75c..23e8a1697 100644
--- a/packages/lib/src/common/variables.ts
+++ b/packages/lib/src/common/variables.ts
@@ -75,8 +75,8 @@ export const defaultTranslatedComponentLabels = {
requiredSelectionErrorMessage: "This field is required. Please, choose an option.",
requiredValueErrorMessage: "This field is required. Please, enter a value.",
formatRequestedErrorMessage: "Please match the format requested.",
- lengthErrorMessage: (minLength?: number, maxLength?: number) => `Min length ${minLength}, max length ${maxLength}.`,
- logoAlternativeText: "Logo",
+ minLengthErrorMessage: (minLength: number) => `The minimum length is ${minLength}.`,
+ maxLengthErrorMessage: (maxLength: number) => `The maximum length is ${maxLength}.`,
},
header: {
closeIcon: "Close menu",
diff --git a/packages/lib/src/text-input/TextInput.test.tsx b/packages/lib/src/text-input/TextInput.test.tsx
index c8f234ff4..1aba7d025 100644
--- a/packages/lib/src/text-input/TextInput.test.tsx
+++ b/packages/lib/src/text-input/TextInput.test.tsx
@@ -2,6 +2,7 @@ import { act, fireEvent, render, waitForElementToBeRemoved } from "@testing-libr
import userEvent from "@testing-library/user-event";
import DxcTextInput from "./TextInput";
import MockDOMRect from "../../test/mocks/domRectMock";
+import { HalstackProvider } from "../HalstackContext";
// Mocking DOMRect for Radix Primitive Popover
global.DOMRect = MockDOMRect;
@@ -157,23 +158,30 @@ describe("TextInput component tests", () => {
);
const input = getByRole("textbox");
fireEvent.change(input, { target: { value: "test" } });
- expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({
value: "test",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
});
fireEvent.blur(input);
- expect(onBlur).toHaveBeenCalled();
expect(onBlur).toHaveBeenCalledWith({
value: "test",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
});
- userEvent.clear(input);
+
+ fireEvent.change(input, { target: { value: "test-maximum-length" } });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "test-maximum-length",
+ error: "The maximum length is 10.",
+ });
+ fireEvent.blur(input);
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "test-maximum-length",
+ error: "The maximum length is 10.",
+ });
+
fireEvent.change(input, { target: { value: "length" } });
- expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({ value: "length" });
fireEvent.blur(input);
- expect(onBlur).toHaveBeenCalled();
expect(onBlur).toHaveBeenCalledWith({ value: "length" });
});
@@ -195,34 +203,38 @@ describe("TextInput component tests", () => {
);
const input = getByRole("textbox");
fireEvent.change(input, { target: { value: "test" } });
- expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({
value: "test",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
});
fireEvent.blur(input);
- expect(onBlur).toHaveBeenCalled();
expect(onBlur).toHaveBeenCalledWith({
value: "test",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
+ });
+ fireEvent.change(input, { target: { value: "test-maximum-length" } });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "test-maximum-length",
+ error: "The maximum length is 10.",
+ });
+ fireEvent.blur(input);
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "test-maximum-length",
+ error: "The maximum length is 10.",
});
fireEvent.change(input, { target: { value: "tests" } });
- expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({
value: "tests",
error: "Please match the format requested.",
});
fireEvent.blur(input);
- expect(onBlur).toHaveBeenCalled();
expect(onBlur).toHaveBeenCalledWith({
value: "tests",
error: "Please match the format requested.",
});
fireEvent.change(input, { target: { value: "tests4&" } });
- expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({ value: "tests4&" });
fireEvent.blur(input);
- expect(onBlur).toHaveBeenCalled();
expect(onBlur).toHaveBeenCalledWith({ value: "tests4&" });
});
@@ -732,12 +744,29 @@ describe("TextInput component synchronous autosuggest tests", () => {
});
expect(onChange).toHaveBeenCalledWith({
value: "Cha",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
});
fireEvent.blur(input);
expect(onBlur).toHaveBeenCalledWith({
value: "Chad",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
+ });
+
+ userEvent.clear(input);
+ fireEvent.focus(input);
+ fireEvent.change(input, { target: { value: "Democratic Rep" } });
+ expect(getByText("Democratic Rep")).toBeTruthy();
+ act(() => {
+ userEvent.click(getByRole("option"));
+ });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "Democratic Rep",
+ error: "The maximum length is 10.",
+ });
+ fireEvent.blur(input);
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "Democratic Republic of the Congo",
+ error: "The maximum length is 10.",
});
});
@@ -1156,4 +1185,52 @@ describe("TextInput component asynchronous autosuggest tests", () => {
await waitForElementToBeRemoved(() => getByText("Searching..."));
expect(getByText("Error fetching data")).toBeTruthy();
});
+
+ test("Maximum and minimum error messages change within HalstackProvider", () => {
+ const onChange = jest.fn();
+ const onBlur = jest.fn();
+ const { getByRole } = render(
+ `Please do not enter more than ${maxLength} characters.`,
+ minLengthErrorMessage: (minLegth: number) => `Please do not enter less than ${minLegth} characters.`,
+ },
+ }}
+ >
+
+
+ );
+ const input = getByRole("textbox");
+ fireEvent.change(input, { target: { value: "test" } });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "test",
+ error: "Please do not enter less than 5 characters.",
+ });
+ fireEvent.blur(input);
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "test",
+ error: "Please do not enter less than 5 characters.",
+ });
+
+ fireEvent.change(input, { target: { value: "test-maximum-length" } });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "test-maximum-length",
+ error: "Please do not enter more than 10 characters.",
+ });
+ fireEvent.blur(input);
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "test-maximum-length",
+ error: "Please do not enter more than 10 characters.",
+ });
+ });
});
diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx
index 2b2911574..71b56b0ba 100644
--- a/packages/lib/src/text-input/TextInput.tsx
+++ b/packages/lib/src/text-input/TextInput.tsx
@@ -25,7 +25,6 @@ import TextInputPropsType, { AutosuggestWrapperProps, RefType } from "./types";
import {
calculateWidth,
hasSuggestions,
- isLengthIncorrect,
isNumberIncorrect,
isRequired,
makeCancelable,
@@ -35,6 +34,7 @@ import HelperText from "../styles/forms/HelperText";
import Label from "../styles/forms/Label";
import ErrorMessage from "../styles/forms/ErrorMessage";
import inputStylesByState from "../styles/forms/inputStylesByState";
+import { getLengthErrorMessage } from "../common/utils";
const TextInputContainer = styled.div<{
margin: TextInputPropsType["margin"];
@@ -232,15 +232,23 @@ const DxcTextInput = forwardRef(
setInnerValue(formattedValue);
}
+ const lengthError = getLengthErrorMessage({
+ value: formattedValue,
+ minLength,
+ maxLength,
+ minLengthErrorMessage: translatedLabels.formFields.minLengthErrorMessage,
+ maxLengthErrorMessage: translatedLabels.formFields.maxLengthErrorMessage,
+ });
+
if (isRequired(formattedValue, optional)) {
onChange?.({
value: formattedValue,
error: translatedLabels.formFields.requiredValueErrorMessage,
});
- } else if (isLengthIncorrect(formattedValue, minLength, maxLength)) {
+ } else if (lengthError) {
onChange?.({
value: formattedValue,
- error: translatedLabels.formFields.lengthErrorMessage?.(minLength, maxLength),
+ error: lengthError,
});
} else if (patternMismatch(pattern, formattedValue)) {
onChange?.({ value: formattedValue, error: translatedLabels.formFields.formatRequestedErrorMessage });
@@ -331,12 +339,20 @@ const DxcTextInput = forwardRef(
const handleInputOnBlur = (event: FocusEvent) => {
closeSuggestions();
+ const lengthError = getLengthErrorMessage({
+ value: event.target.value,
+ minLength,
+ maxLength,
+ minLengthErrorMessage: translatedLabels.formFields.minLengthErrorMessage,
+ maxLengthErrorMessage: translatedLabels.formFields.maxLengthErrorMessage,
+ });
+
if (isRequired(event.target.value, optional)) {
onBlur?.({ value: event.target.value, error: translatedLabels.formFields.requiredValueErrorMessage });
- } else if (isLengthIncorrect(event.target.value, minLength, maxLength)) {
+ } else if (lengthError) {
onBlur?.({
value: event.target.value,
- error: translatedLabels.formFields.lengthErrorMessage?.(minLength, maxLength),
+ error: lengthError,
});
} else if (patternMismatch(pattern, event.target.value)) {
onBlur?.({ value: event.target.value, error: translatedLabels.formFields.formatRequestedErrorMessage });
diff --git a/packages/lib/src/text-input/utils.ts b/packages/lib/src/text-input/utils.ts
index 202e0f56c..3f0c73b69 100644
--- a/packages/lib/src/text-input/utils.ts
+++ b/packages/lib/src/text-input/utils.ts
@@ -48,13 +48,6 @@ export const hasSuggestions = (suggestions: TextInputPropsType["suggestions"]) =
export const isRequired = (value: string, optional: boolean) => value === "" && !optional;
-export const isLengthIncorrect = (
- value: string,
- minLength: TextInputPropsType["minLength"],
- maxLength: TextInputPropsType["maxLength"]
-) =>
- value != null && ((minLength != null && value.length < minLength) || (maxLength != null && value.length > maxLength));
-
export const isNumberIncorrect = (
value: number,
minNumber: TextInputPropsType["minLength"],
diff --git a/packages/lib/src/textarea/Textarea.test.tsx b/packages/lib/src/textarea/Textarea.test.tsx
index 89a315c53..71c320b3c 100644
--- a/packages/lib/src/textarea/Textarea.test.tsx
+++ b/packages/lib/src/textarea/Textarea.test.tsx
@@ -196,13 +196,26 @@ describe("Textarea component tests", () => {
expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({
value: "test",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
});
fireEvent.blur(textarea);
expect(onBlur).toHaveBeenCalled();
expect(onBlur).toHaveBeenCalledWith({
value: "test",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
+ });
+ userEvent.clear(textarea);
+ fireEvent.change(textarea, { target: { value: "this is a longer value" } });
+ expect(onChange).toHaveBeenCalled();
+ expect(onChange).toHaveBeenCalledWith({
+ value: "this is a longer value",
+ error: "The maximum length is 10.",
+ });
+ fireEvent.blur(textarea);
+ expect(onBlur).toHaveBeenCalled();
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "this is a longer value",
+ error: "The maximum length is 10.",
});
userEvent.clear(textarea);
fireEvent.change(textarea, { target: { value: "length" } });
@@ -233,13 +246,13 @@ describe("Textarea component tests", () => {
expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({
value: "test",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
});
fireEvent.blur(textarea);
expect(onBlur).toHaveBeenCalled();
expect(onBlur).toHaveBeenCalledWith({
value: "test",
- error: "Min length 5, max length 10.",
+ error: "The minimum length is 5.",
});
fireEvent.change(textarea, { target: { value: "tests" } });
expect(onChange).toHaveBeenCalled();
diff --git a/packages/lib/src/textarea/Textarea.tsx b/packages/lib/src/textarea/Textarea.tsx
index b180dcb38..e6fbefff9 100644
--- a/packages/lib/src/textarea/Textarea.tsx
+++ b/packages/lib/src/textarea/Textarea.tsx
@@ -1,6 +1,6 @@
import { ChangeEvent, FocusEvent, forwardRef, useContext, useEffect, useId, useRef, useState } from "react";
import styled from "@emotion/styled";
-import { getMargin } from "../common/utils";
+import { getLengthErrorMessage, getMargin } from "../common/utils";
import { spaces } from "../common/variables";
import { HalstackLanguageContext } from "../HalstackContext";
import TextareaPropsType, { RefType } from "./types";
@@ -99,21 +99,26 @@ const DxcTextarea = forwardRef(
const textareaRef = useRef(null);
const prevValueRef = useRef(null);
- const isLengthOutOfRange = (value: string) =>
- value !== "" && minLength && maxLength && (value.length < minLength || value.length > maxLength);
-
const changeValue = (newValue: string) => {
if (value == null) setInnerValue(newValue);
+ const lengthError = getLengthErrorMessage({
+ value: newValue,
+ minLength,
+ maxLength,
+ minLengthErrorMessage: translatedLabels.formFields.minLengthErrorMessage,
+ maxLengthErrorMessage: translatedLabels.formFields.maxLengthErrorMessage,
+ });
+
if (newValue === "" && !optional) {
onChange?.({
value: newValue,
error: translatedLabels.formFields.requiredValueErrorMessage,
});
- } else if (isLengthOutOfRange(newValue)) {
+ } else if (lengthError) {
onChange?.({
value: newValue,
- error: translatedLabels.formFields.lengthErrorMessage?.(minLength, maxLength),
+ error: lengthError,
});
} else if (newValue && pattern && !patternMatch(pattern, newValue)) {
onChange?.({
@@ -124,15 +129,22 @@ const DxcTextarea = forwardRef(
};
const handleOnBlur = (event: FocusEvent) => {
+ const lengthError = getLengthErrorMessage({
+ value: event.target.value,
+ minLength,
+ maxLength,
+ minLengthErrorMessage: translatedLabels.formFields.minLengthErrorMessage,
+ maxLengthErrorMessage: translatedLabels.formFields.maxLengthErrorMessage,
+ });
if (event.target.value === "" && !optional) {
onBlur?.({
value: event.target.value,
error: translatedLabels.formFields.requiredValueErrorMessage,
});
- } else if (isLengthOutOfRange(event.target.value)) {
+ } else if (lengthError) {
onBlur?.({
value: event.target.value,
- error: translatedLabels.formFields.lengthErrorMessage?.(minLength, maxLength),
+ error: lengthError,
});
} else if (event.target.value && pattern && !patternMatch(pattern, event.target.value)) {
onBlur?.({