diff --git a/cypress/components/decimal/decimal.cy.tsx b/cypress/components/decimal/decimal.cy.tsx
deleted file mode 100644
index 5351ae61d7..0000000000
--- a/cypress/components/decimal/decimal.cy.tsx
+++ /dev/null
@@ -1,570 +0,0 @@
-/* eslint-disable array-callback-return, no-unused-expressions, jest/valid-expect-in-promise, jest/valid-expect */
-import React from "react";
-import Decimal, {
- CustomEvent,
- DecimalProps,
-} from "../../../src/components/decimal";
-import * as stories from "../../../src/components/decimal/decimal.stories";
-import CypressMountWithProviders from "../../support/component-helper/cypress-mount";
-
-import {
- fieldHelpPreview,
- getDataElementByValue,
- tooltipPreview,
- commonDataElementInputPreview,
- cyRoot,
- getElement,
-} from "../../locators/index";
-
-import { textboxPrefix } from "../../locators/textbox";
-import { CHARACTERS } from "../../support/component-helper/constants";
-
-const eventOutput = (formattedVal: string, rawVal: string) => {
- return {
- target: {
- id: undefined,
- name: undefined,
- value: {
- formattedValue: formattedVal,
- rawValue: rawVal,
- },
- },
- };
-};
-
-context("Tests for Decimal component", () => {
- describe("check props for Decimal component", () => {
- const input = [
- [0, "1", "1"],
- [0, "134^", "134^"],
- [0, "1234567789", "1,234,567,789"],
- [0, ",,,,,", ",,,,,"],
- [0, ".....", "....."],
- [0, "abc,.123,.!@.00", "abc,.123,.!@.00"],
- [0, "1,234$", "1,234$"],
-
- [1, "1", "1.0"],
- [1, "1.2", "1.2"],
- [1, "1.23", "1.23"],
-
- [2, "2", "2.00"],
- [2, "2.1", "2.10"],
- [2, "2.12", "2.12"],
- [2, "2.123", "2.123"],
-
- [3, "2.1", "2.100"],
- [3, "2.12", "2.120"],
- [3, "2.123", "2.123"],
- [3, "2.1234", "2.1234"],
-
- [4, "1.1234", "1.1234"],
- [4, "2", "2.0000"],
- [4, "234556654", "234,556,654.0000"],
- [4, "%^%^%<<,,,", "%^%^%<<,,,"],
-
- [5, "1", "1.00000"],
- [5, "1.12345", "1.12345"],
- [5, "1a.23", "1a.23"],
-
- [6, "2.123456", "2.123456"],
- [6, "2.1", "2.100000"],
- [6, "2a.12", "2a.12"],
- [6, "1,232.123", "1,232.123000"],
-
- [7, "1", "1.0000000"],
- [7, "2344.1234567", "2,344.1234567"],
- [7, "88652344.1234567", "88,652,344.1234567"],
-
- [8, "1", "1.00000000"],
- [8, "1.2", "1.20000000"],
- [8, "1.23", "1.23000000"],
-
- [9, "2", "2.000000000"],
- [9, "2.1", "2.100000000"],
- [9, "1222.12", "1,222.120000000"],
- [9, "2.123000000", "2.123000000"],
-
- [10, "2.1", "2.1000000000"],
- [10, "2.12", "2.1200000000"],
- [10, "1222.123", "1,222.1230000000"],
- [10, "2.12345", "2.1234500000"],
-
- [11, "1", "1.00000000000"],
- [11, "2345", "2,345.00000000000"],
- [11, "17899536472345", "17,899,536,472,345.00000000000"],
-
- [12, "1", "1.000000000000"],
- [12, "1.12345", "1.123450000000"],
- [12, "1a.23", "1a.23"],
-
- [13, "2", "2.0000000000000"],
- [13, "2.1", "2.1000000000000"],
- [13, "2a.12", "2a.12"],
- [13, "1232.123", "1,232.1230000000000"],
-
- [14, "2.1", "2.10000000000000"],
- [14, "2.12", "2.12000000000000"],
- [14, "2.123", "2.12300000000000"],
- [14, "1222.1234", "1,222.12340000000000"],
-
- [15, "1", "1.000000000000000"],
- [15, "2332.78", "2,332.780000000000000"],
- [15, "1a3.55", "1a3.55"],
- [15, "1.12345", "1.123450000000000"],
- [15, "1a.23", "1a.23"],
- ] as [DecimalProps["precision"], string, string][];
-
- it.each(input)(
- "should use %s as precision and %s as input value to produce %s output value",
- (precision, inputValue, outputValue) => {
- CypressMountWithProviders();
-
- commonDataElementInputPreview()
- .type(inputValue, { delay: 0 })
- .blur({ force: true });
- commonDataElementInputPreview().should("have.value", outputValue);
- }
- );
-
- const inputLocale = [
- ["en", "1,1,1,1,1.1", "11,111.100"],
- ["en", "1,1,1222,12,1.1", "111,222,121.100"],
- ["en", "1,,1,,1", "1,,1,,1"],
-
- ["es-ES", "1.1.1.1.1.1,1", "111.111,100"],
- ["es-ES", "2.123", "2123,000"],
- ["es-ES", "21.21.111.1,013", "21.211.111,013"],
- ["es-ES", "2.,12.,1", "2.,12.,1"],
-
- ["fr", "11111,25", "11 111,250"],
- ["fr", "1 1 1 1 1,25", "1 1 1 1 1,25"],
-
- ["pt-PT", "1111,2", "1111,200"],
- ["pt-PT", "111 11,25", "11 111,250"],
-
- ["no-NO", "1 1 11,21", "1 111,210"],
- ["no-No", "111 1 1,25", "11 111,250"],
- ["no-NO", "1 1 1 1 1,25", "1 1 1 1 1,25"],
- ] as [DecimalProps["locale"], string, string][];
-
- it.each(inputLocale)(
- "should use %s locale and %s input value to produce %s output value",
- (locale, inputValue, outputValue) => {
- CypressMountWithProviders();
-
- commonDataElementInputPreview()
- .type(inputValue, { delay: 0 })
- .blur({ force: true });
- commonDataElementInputPreview()
- .invoke("val")
- .then(($el) => {
- for (let number = 0; number < String($el).length; number++) {
- expect(
- String($el)
- .replace(/(\s)|( )|(\u00a0)/g, " ")
- .charCodeAt(number)
- ).to.equals(outputValue.charCodeAt(number));
- }
- });
- }
- );
-
- it("should render Decimal with readOnly prop", () => {
- CypressMountWithProviders();
-
- const inputValue = "test";
-
- commonDataElementInputPreview()
- .type(inputValue, { force: true })
- .blur({ force: true });
- commonDataElementInputPreview()
- .parent()
- .should("not.have.value", inputValue)
- .and("have.attr", "readOnly");
- });
-
- it.each(["10%", "30%", "50%", "80%", "100%"])(
- "should check maxWidth as %s for Decimal component",
- (maxWidth) => {
- CypressMountWithProviders();
-
- getDataElementByValue("input")
- .parent()
- .parent()
- .should("have.css", "max-width", maxWidth);
- }
- );
-
- it("when maxWidth has no value it should render as 100%", () => {
- CypressMountWithProviders();
-
- getDataElementByValue("input")
- .parent()
- .parent()
- .should("have.css", "max-width", "100%");
- });
- });
-
- describe("check Decimal input", () => {
- const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS];
-
- it.each(testData)(
- "check label renders properly with %s as specific value",
- (specificValue) => {
- CypressMountWithProviders();
-
- fieldHelpPreview().should("have.text", specificValue);
- }
- );
-
- it.each(testData)(
- "check fieldHelp renders properly with %s specific value",
- (specificValue) => {
- CypressMountWithProviders();
-
- getDataElementByValue("label").should("have.text", specificValue);
- }
- );
-
- it.each(testData)(
- "check tooltip renders properly with %s specific values",
- (specificValue) => {
- CypressMountWithProviders(
-
- );
-
- getDataElementByValue("question").trigger("mouseover");
- tooltipPreview().should("have.text", specificValue);
- cyRoot().realHover({ position: "topLeft" });
- }
- );
-
- it.each(testData)(
- "should render Decimal with prefix prop set to %s",
- (prefix) => {
- CypressMountWithProviders();
-
- textboxPrefix()
- .should("have.text", prefix)
- .and("have.css", "font-size", "14px")
- .and("have.css", "font-weight", "900")
- .and("have.css", "margin-left", "12px");
- }
- );
-
- it("when prefix prop is set, 'flex-direction' should be 'row'", () => {
- CypressMountWithProviders();
-
- getDataElementByValue("input")
- .parent()
- .should("have.css", "flex-direction", "row");
- });
-
- it.each(testData)(
- "check Decimal component accepts %s as specific value",
- (specificValue) => {
- CypressMountWithProviders();
-
- commonDataElementInputPreview()
- .type(specificValue)
- .blur({ force: true });
- commonDataElementInputPreview()
- .invoke("val")
- .then(($el) => {
- for (let number = 0; number < String($el).length; number++) {
- expect(
- String($el)
- .replace(/(\s)|( )|(\u00a0)/g, " ")
- .charCodeAt(number)
- ).to.equals(specificValue.charCodeAt(number));
- }
- });
- }
- );
-
- it("check Decimal component accepts white spaces", () => {
- CypressMountWithProviders();
-
- commonDataElementInputPreview().type(" ").blur({ force: true });
- commonDataElementInputPreview().should("have.attr", "value", " ");
- });
- });
-
- describe("allowEmptyValue", () => {
- it.each([
- [0, "en", "0", "0"],
- [1, "en", "0.0", "0.0"],
- [2, "en", "0.00", "0.00"],
- [0, "es-ES", "0", "0"],
- [1, "es-ES", "0.0", "0,0"],
- [2, "es-ES", "0.00", "0,00"],
- [0, "fr", "0", "0"],
- [1, "fr", "0.0", "0,0"],
- [2, "fr", "0.00", "0,00"],
- [0, "pt-PT", "0", "0"],
- [1, "pt-PT", "0.0", "0,0"],
- [2, "pt-PT", "0.00", "0,00"],
- ] as [DecimalProps["precision"], string, string, string][])(
- "should format an empty value correctly when precision is %s, locale is %s and allowEmptyValue is false",
- (precisionValue, localeValue, rawValue, expectedValue) => {
- const callback: DecimalProps["onBlur"] = cy.stub().as("onBlur");
-
- CypressMountWithProviders(
-
- );
-
- commonDataElementInputPreview().type("100");
- commonDataElementInputPreview()
- .clear()
- .blur({ force: true })
- .then(() => {
- cy.get("@onBlur").should(
- "have.been.calledWith",
- eventOutput(expectedValue, rawValue)
- );
- });
- expect(
- commonDataElementInputPreview().should("have.value", expectedValue)
- );
- }
- );
-
- it.each([
- [0, "en"],
- [1, "en"],
- [2, "en"],
- [0, "es-ES"],
- [1, "es-ES"],
- [2, "es-ES"],
- [0, "fr"],
- [1, "fr"],
- [2, "fr"],
- [0, "pt-PT"],
- [1, "pt-PT"],
- [2, "pt-PT"],
- ] as [DecimalProps["precision"], string][])(
- "should format an empty value correctly when precision is %s, locale is %s and allowEmptyValue is true",
- (precisionValue, localeValue) => {
- const callback: DecimalProps["onBlur"] = cy.stub().as("onBlur");
-
- CypressMountWithProviders(
-
- );
-
- commonDataElementInputPreview().type("100");
- commonDataElementInputPreview()
- .clear()
- .blur({ force: true })
- .then(() => {
- cy.get("@onBlur").should(
- "have.been.calledWith",
- eventOutput("", "")
- );
- });
- expect(commonDataElementInputPreview().should("have.value", ""));
- }
- );
- });
-
- describe("check events for Decimal component", () => {
- const inputValue = "123";
- const iterable = [
- ["1", "1.00"],
- ["12", "12.00"],
- ["123", "123.00"],
- ] as [string, string][];
-
- it.each(iterable)(
- "should call onChange callback when a type event is triggered with %s value",
- (rawValueTest, formattedValueTest) => {
- const callback: DecimalProps["onChange"] = cy.stub().as("onChange");
-
- CypressMountWithProviders();
-
- commonDataElementInputPreview()
- .type(rawValueTest)
- .blur({ force: true })
-
- .then(() => {
- cy.get("@onChange").should(
- "have.been.calledWith",
- eventOutput(formattedValueTest, rawValueTest)
- );
- });
- }
- );
-
- it("can have a custom onChange handler", () => {
- const CustomDecimalComponent = (props: DecimalProps) => {
- const [state, setState] = React.useState("0.01");
- const handleChange = ({ target }: CustomEvent) => {
- let newValue = target.value.rawValue;
- if (newValue.startsWith("22.22")) newValue = "22.22";
- setState(newValue);
- };
-
- return ;
- };
-
- CypressMountWithProviders();
-
- commonDataElementInputPreview().type("22.222");
- expect(
- commonDataElementInputPreview().should("have.attr", "value", "22.22")
- );
- });
-
- it("should call onBlur callback when a blur event is triggered", () => {
- const callback: DecimalProps["onBlur"] = cy.stub().as("onBlur");
-
- CypressMountWithProviders();
-
- commonDataElementInputPreview()
- .type(inputValue)
- .blur({ force: true })
- .then(() => {
- cy.get("@onBlur").should(
- "have.been.calledWith",
- eventOutput("123.00", inputValue)
- );
- expect(callback).to.have.been.calledOnce;
- });
- });
- });
-
- describe("Accessibility tests for Decimal component", () => {
- it.each(["small", "medium", "large"] as DecimalProps["size"][])(
- "should pass accessibility tests for Decimal with %s input size",
- (size) => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- }
- );
-
- it.each(["left", "right"] as DecimalProps["labelAlign"][])(
- "should pass accessibility tests for Decimal with label aligned %s",
- (labelAlign) => {
- CypressMountWithProviders(
-
- );
-
- cy.checkAccessibility();
- }
- );
-
- it("should pass accessibility tests for Decimal with custom precision", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal with custom label width and input width", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal with custom max width", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal with field help", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal with label help", () => {
- CypressMountWithProviders();
-
- getDataElementByValue("question")
- .trigger("mouseover")
- .then(() => {
- cy.checkAccessibility();
- });
- });
-
- it("should pass accessibility tests for Decimal required", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal left aligned", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal with validation", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal validations redesigned", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal default", () => {
- CypressMountWithProviders(
-
- );
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal with readOnly prop", () => {
- CypressMountWithProviders(
-
- );
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal with tooltip", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
-
- it("should pass accessibility tests for Decimal with tooltip label", () => {
- CypressMountWithProviders();
-
- cy.checkAccessibility();
- });
- });
-
- it("should have the expected border radius styling", () => {
- CypressMountWithProviders();
- getElement("input").should("have.css", "border-radius", "4px");
- });
-});
diff --git a/playwright/components/index.ts b/playwright/components/index.ts
index 0cc250fe63..bd57a4605e 100644
--- a/playwright/components/index.ts
+++ b/playwright/components/index.ts
@@ -9,6 +9,7 @@ import {
FIELD_HELP_PREVIEW,
LABEL,
STICKY_FOOTER,
+ COMMMON_DATA_ELEMENT_INPUT,
} from "./locators";
export const icon = (page: Page) => {
@@ -19,6 +20,10 @@ export const getDataElementByValue = (page: Page, element: string) => {
return page.locator(`[data-element="${element}"]`);
};
+export const commonDataElementInputPreview = (page: Page) => {
+ return page.locator(COMMMON_DATA_ELEMENT_INPUT);
+};
+
export const closeIconButton = (page: Page) => {
return page.locator(CLOSE_ICON_BUTTON);
};
@@ -51,6 +56,10 @@ export const getComponent = (page: Page, component: string) => {
return page.locator(`[data-component="${component}"]`);
};
+export const getElement = (page: Page, element: string) => {
+ return page.locator(`[data-element="${element}"]`).first();
+};
+
export const label = (page: Page) => {
return page.locator(LABEL);
};
diff --git a/playwright/components/textbox/index.ts b/playwright/components/textbox/index.ts
new file mode 100644
index 0000000000..1e22797c51
--- /dev/null
+++ b/playwright/components/textbox/index.ts
@@ -0,0 +1,20 @@
+import type { Page } from "@playwright/test";
+
+import { TEXTBOX, TEXTBOX_DATA_COMPONENT, TEXTBOX_PREFIX } from "./locators";
+
+// component preview locators
+export const textbox = (page: Page) => {
+ return page.locator(TEXTBOX);
+};
+
+export const textboxDataComponent = (page: Page) => {
+ return page.locator(TEXTBOX_DATA_COMPONENT);
+};
+
+export const textboxPrefix = (page: Page) => {
+ return page.locator(TEXTBOX_PREFIX);
+};
+
+export const textboxInput = (page: Page) => {
+ return page.locator(TEXTBOX).locator("div").first();
+};
diff --git a/playwright/components/textbox/locators.ts b/playwright/components/textbox/locators.ts
new file mode 100644
index 0000000000..cc8bf17768
--- /dev/null
+++ b/playwright/components/textbox/locators.ts
@@ -0,0 +1,4 @@
+// component preview locators
+export const TEXTBOX = 'div[role="presentation"]';
+export const TEXTBOX_DATA_COMPONENT = '[data-component="textbox"]';
+export const TEXTBOX_PREFIX = '[data-element="textbox-prefix"]';
diff --git a/src/components/decimal/components.test-pw.tsx b/src/components/decimal/components.test-pw.tsx
new file mode 100644
index 0000000000..8af9b55142
--- /dev/null
+++ b/src/components/decimal/components.test-pw.tsx
@@ -0,0 +1,217 @@
+import React, { useState } from "react";
+
+import Decimal, { DecimalProps, CustomEvent } from ".";
+import CarbonProvider from "../carbon-provider/carbon-provider.component";
+
+export const DefaultStory = (
+ args: Partial & { message?: string | boolean }
+) => {
+ const [state, setState] = useState("0.01");
+ const setValue = ({ target }: CustomEvent) => {
+ setState(target.value.rawValue);
+ };
+
+ return (
+
+ );
+};
+
+export const Sizes = () => {
+ const [state, setState] = useState({
+ small: "0.01",
+ medium: "0.01",
+ large: "0.01",
+ });
+
+ const handleChange = (size: DecimalProps["size"]) => (e: CustomEvent) => {
+ setState({ ...state, [size || "small"]: e.target.value.rawValue });
+ };
+
+ return (["small", "medium", "large"] as const).map((size) => (
+
+ ));
+};
+
+export const Disabled = () => ;
+
+export const Prefix = () => ;
+
+export const LabelAlign = () => {
+ const [state, setState] = useState({
+ right: "0.01",
+ left: "0.01",
+ });
+ const handleChange = (alignment: DecimalProps["labelAlign"]) => (
+ e: CustomEvent
+ ) => {
+ setState({ ...state, [alignment || "left"]: e.target.value.rawValue });
+ };
+ return (["right", "left"] as const).map((alignment) => (
+
+ ));
+};
+
+export const ReadOnly = () => ;
+
+export const Empty = () => ;
+
+export const WithCustomPrecision = () => {
+ const [state, setState] = useState("0.0001");
+ const setValue = ({ target }: CustomEvent) => {
+ setState(target.value.rawValue);
+ };
+ return (
+
+ );
+};
+
+export const LabelInline = () => ;
+
+export const WithCustomLabelWidthAndInputWidth = (
+ props: Partial
+) => ;
+
+export const WithCustomMaxWidth = () => ;
+
+export const WithFieldHelp = (props: Partial) => (
+
+);
+
+export const WithLabelHelp = (props: Partial) => (
+
+);
+
+export const Required = () => ;
+
+export const LeftAligned = () => ;
+
+type Validation = "error" | "warning" | "info";
+
+export const Validations = (
+ args: Partial & { message?: string | boolean }
+) => {
+ const [state, setState] = useState({
+ error: "0.01",
+ warning: "0.01",
+ info: "0.01",
+ });
+
+ const handleChange = (validation: Validation) => (e: CustomEvent) => {
+ setState({ ...state, [validation]: e.target.value.rawValue });
+ };
+
+ return (
+ <>
+ {(["error", "warning", "info"] as const).map((validationType) => (
+
+
+
+
+ ))}
+ >
+ );
+};
+
+export const ValidationsStringComponent = () => (
+
+);
+
+export const ValidationsStringLabel = () => (
+
+);
+
+export const ValidationsBoolean = () => ;
+
+export const ValidationsRedesign = () => {
+ const [state, setState] = useState({
+ error: "0.01",
+ warning: "0.01",
+ });
+ const handleChange = (validation: Validation) => (e: CustomEvent) => {
+ setState({ ...state, [validation]: e.target.value.rawValue });
+ };
+ return (
+
+ {(["error", "warning"] as const).map((validationType) =>
+ (["small", "medium", "large"] as const).map((size) => (
+
+
+
+
+ ))
+ )}
+
+ );
+};
+
+export const ValidationsTooltip = () => {
+ const [state, setState] = useState({
+ error: "0.01",
+ warning: "0.01",
+ info: "0.01",
+ });
+ const handleChange = (validation: Validation) => (e: CustomEvent) => {
+ setState({ ...state, [validation]: e.target.value.rawValue });
+ };
+ return (
+ <>
+ {(["error", "warning", "info"] as const).map((validationType) => (
+
+
+
+ ))}
+ >
+ );
+};
+
+export const ValidationsTooltipLabel = () => ;
diff --git a/src/components/decimal/decimal.pw.tsx b/src/components/decimal/decimal.pw.tsx
new file mode 100644
index 0000000000..c365877861
--- /dev/null
+++ b/src/components/decimal/decimal.pw.tsx
@@ -0,0 +1,629 @@
+import React from "react";
+import { expect, test } from "@playwright/experimental-ct-react17";
+import { Locator } from "@playwright/test";
+
+import {
+ commonDataElementInputPreview,
+ fieldHelpPreview,
+ getDataElementByValue,
+ getElement,
+ tooltipPreview,
+} from "../../../playwright/components/index";
+import { textboxPrefix, textbox } from "../../../playwright/components/textbox";
+
+import { CHARACTERS } from "../../../playwright/support/constants";
+import {
+ checkAccessibility,
+ getStyle,
+} from "../../../playwright/support/helper";
+
+import Decimal, { DecimalProps } from "../../../src/components/decimal";
+import {
+ DefaultStory,
+ WithCustomPrecision,
+ WithCustomLabelWidthAndInputWidth,
+ WithCustomMaxWidth,
+ WithFieldHelp,
+ WithLabelHelp,
+ Required,
+ LeftAligned,
+ Validations,
+ ValidationsRedesign,
+ ValidationsTooltip,
+ ValidationsTooltipLabel,
+} from "./components.test-pw";
+
+test.describe("check props for Decimal component", () => {
+ ([
+ [0, "1", "1"],
+ [0, "134^", "134^"],
+ [0, "1234567789", "1,234,567,789"],
+ [0, ",,,,,", ",,,,,"],
+ [0, ".....", "....."],
+ [0, "abc,.123,.!@.00", "abc,.123,.!@.00"],
+ [0, "1,234$", "1,234$"],
+ [1, "1", "1.0"],
+ [1, "1.2", "1.2"],
+ [1, "1.23", "1.23"],
+ [2, "2", "2.00"],
+ [2, "2.1", "2.10"],
+ [2, "2.12", "2.12"],
+ [2, "2.123", "2.123"],
+ [3, "2.1", "2.100"],
+ [3, "2.12", "2.120"],
+ [3, "2.123", "2.123"],
+ [3, "2.1234", "2.1234"],
+ [4, "1.1234", "1.1234"],
+ [4, "2", "2.0000"],
+ [4, "234556654", "234,556,654.0000"],
+ [4, "%^%^%<<,,,", "%^%^%<<,,,"],
+ [5, "1", "1.00000"],
+ [5, "1.12345", "1.12345"],
+ [5, "1a.23", "1a.23"],
+ [6, "2.123456", "2.123456"],
+ [6, "2.1", "2.100000"],
+ [6, "2a.12", "2a.12"],
+ [6, "1,232.123", "1,232.123000"],
+ [7, "1", "1.0000000"],
+ [7, "2344.1234567", "2,344.1234567"],
+ [7, "88652344.1234567", "88,652,344.1234567"],
+ [8, "1", "1.00000000"],
+ [8, "1.2", "1.20000000"],
+ [8, "1.23", "1.23000000"],
+ [9, "2", "2.000000000"],
+ [9, "2.1", "2.100000000"],
+ [9, "1222.12", "1,222.120000000"],
+ [9, "2.123000000", "2.123000000"],
+ [10, "2.1", "2.1000000000"],
+ [10, "2.12", "2.1200000000"],
+ [10, "1222.123", "1,222.1230000000"],
+ [10, "2.12345", "2.1234500000"],
+ [11, "1", "1.00000000000"],
+ [11, "2345", "2,345.00000000000"],
+ [11, "17899536472345", "17,899,536,472,345.00000000000"],
+ [12, "1", "1.000000000000"],
+ [12, "1.12345", "1.123450000000"],
+ [12, "1a.23", "1a.23"],
+ [13, "2", "2.0000000000000"],
+ [13, "2.1", "2.1000000000000"],
+ [13, "2a.12", "2a.12"],
+ [13, "1232.123", "1,232.1230000000000"],
+ [14, "2.1", "2.10000000000000"],
+ [14, "2.12", "2.12000000000000"],
+ [14, "2.123", "2.12300000000000"],
+ [14, "1222.1234", "1,222.12340000000000"],
+ [15, "1", "1.000000000000000"],
+ [15, "2332.78", "2,332.780000000000000"],
+ [15, "1a3.55", "1a3.55"],
+ [15, "1.12345", "1.123450000000000"],
+ [15, "1a.23", "1a.23"],
+ ] as const).forEach(([precision, inputValue, outputValue]) => {
+ test(`should use ${precision} as precision and ${inputValue} as input value to produce ${outputValue} output value`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.fill(inputValue);
+ await commonDataElementInputPreviewElement.blur();
+ await expect(commonDataElementInputPreviewElement).toHaveAttribute(
+ "value",
+ outputValue
+ );
+ });
+ });
+
+ ([
+ ["en", "1,1,1,1,1.1", "11,111.100"],
+ ["en", "1,1,1222,12,1.1", "111,222,121.100"],
+ ["en", "1,,1,,1", "1,,1,,1"],
+ ["es-ES", "1.1.1.1.1.1,1", "111.111,100"],
+ ["es-ES", "2.123", "2123,000"],
+ ["es-ES", "21.21.111.1,013", "21.211.111,013"],
+ ["es-ES", "2.,12.,1", "2.,12.,1"],
+ ["fr", "11111,25", "11 111,250"],
+ ["fr", "1 1 1 1 1,25", "1 1 1 1 1,25"],
+ ["pt-PT", "1111,2", "1111,200"],
+ ["pt-PT", "111 11,25", "11 111,250"],
+ ["no-NO", "1 1 11,21", "1 111,210"],
+ ["no-No", "111 1 1,25", "11 111,250"],
+ ["no-NO", "1 1 1 1 1,25", "1 1 1 1 1,25"],
+ ] as const).forEach(([locale, inputValue, outputValue]) => {
+ test(`should use ${locale} locale and ${inputValue} input value to produce ${outputValue} output value`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.fill(inputValue);
+ await commonDataElementInputPreviewElement.blur();
+ const value = await commonDataElementInputPreviewElement.evaluate(
+ (element) =>
+ (element as HTMLInputElement).value.replace(
+ /(\s)|( )|(\u00a0)/g,
+ " "
+ )
+ );
+ expect(value).toBe(outputValue);
+ });
+ });
+
+ test("should render Decimal with readOnly prop", async ({ mount, page }) => {
+ await mount();
+
+ const inputValue = "test";
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.type(inputValue);
+ await commonDataElementInputPreviewElement.blur();
+ await expect(commonDataElementInputPreviewElement).not.toHaveAttribute(
+ "value",
+ inputValue
+ );
+ await expect(commonDataElementInputPreviewElement).not.toBeEditable();
+ });
+
+ (["10%", "30%", "50%", "80%", "100%"] as const).forEach((maxWidth) => {
+ test(`should check maxWidth as ${maxWidth} for Decimal component`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const textboxParent = await textbox(page).evaluateHandle(
+ (element: Element) => {
+ return element.parentElement;
+ }
+ );
+ const maxWidthValue = await getStyle(
+ (textboxParent as unknown) as Locator,
+ "max-width"
+ );
+ expect(maxWidthValue).toContain(maxWidth);
+ });
+ });
+
+ test("when maxWidth has no value it should render as 100%", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const textboxParent = await textbox(page).evaluateHandle(
+ (element: Element) => {
+ return element.parentElement;
+ }
+ );
+ const maxWidthValue = await getStyle(
+ (textboxParent as unknown) as Locator,
+ "max-width"
+ );
+ expect(maxWidthValue).toContain("100%");
+ });
+});
+
+test.describe("check Decimal input", () => {
+ const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS];
+ testData.forEach((specificValue) => {
+ test(`check label renders properly with ${specificValue} as specific value`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const fieldHelpPreviewElement = fieldHelpPreview(page);
+ await expect(fieldHelpPreviewElement).toHaveText(specificValue);
+ });
+ });
+
+ testData.forEach((specificValue) => {
+ test(`check fieldHelp renders properly with ${specificValue} specific value`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const getDataElementByValueElementLabel = getDataElementByValue(
+ page,
+ "label"
+ );
+ await expect(getDataElementByValueElementLabel).toHaveText(specificValue);
+ });
+ });
+
+ testData.forEach((specificValue) => {
+ test(`check tooltip renders properly with ${specificValue} specific values`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const getDataElementByValueElementQuestion = getDataElementByValue(
+ page,
+ "question"
+ );
+ await getDataElementByValueElementQuestion.hover();
+ const tooltipPreviewElement = tooltipPreview(page);
+ await expect(tooltipPreviewElement).toHaveText(specificValue);
+ });
+ });
+
+ testData.forEach((prefix) => {
+ test(`should render Decimal with prefix prop set to ${prefix}`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const textboxPrefixElement = textboxPrefix(page);
+ await expect(textboxPrefixElement).toHaveText(prefix);
+ await expect(textboxPrefixElement).toHaveCSS("font-size", "14px");
+ await expect(textboxPrefixElement).toHaveCSS("font-weight", "900");
+ await expect(textboxPrefixElement).toHaveCSS("margin-left", "12px");
+ });
+ });
+
+ test("when prefix prop is set, 'flex-direction' should be 'row'", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const textboxInput = textbox(page);
+ await expect(textboxInput).toHaveCSS("flex-direction", "row");
+ });
+
+ testData.forEach((specificValue) => {
+ test(`check Decimal component accepts ${specificValue} as specific value`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.fill(specificValue);
+ await commonDataElementInputPreviewElement.blur();
+ const value = await commonDataElementInputPreviewElement.evaluate(
+ (element) =>
+ (element as HTMLInputElement).value.replace(
+ /(\s)|( )|(\u00a0)/g,
+ " "
+ )
+ );
+ expect(value).toBe(specificValue);
+ });
+ });
+
+ test("check Decimal component accepts white spaces", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.fill(" ");
+ await commonDataElementInputPreviewElement.blur();
+ await expect(commonDataElementInputPreviewElement).toHaveAttribute(
+ "value",
+ " "
+ );
+ });
+});
+
+test.describe("allowEmptyValue", () => {
+ ([
+ [0, "en", "0"],
+ [1, "en", "0.0"],
+ [2, "en", "0.00"],
+ [0, "es-ES", "0"],
+ [1, "es-ES", "0,0"],
+ [2, "es-ES", "0,00"],
+ [0, "fr", "0"],
+ [1, "fr", "0,0"],
+ [2, "fr", "0,00"],
+ [0, "pt-PT", "0"],
+ [1, "pt-PT", "0,0"],
+ [2, "pt-PT", "0,00"],
+ ] as [DecimalProps["precision"], string, string][]).forEach(
+ ([precisionValue, localeValue, expectedValue]) => {
+ test(`should format an empty value correctly when precision is ${precisionValue}, locale is ${localeValue} and allowEmptyValue is false`, async ({
+ mount,
+ page,
+ }) => {
+ await mount(
+
+ );
+
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.fill("100");
+ await commonDataElementInputPreviewElement.clear();
+ await commonDataElementInputPreviewElement.blur();
+ await expect(commonDataElementInputPreviewElement).toHaveAttribute(
+ "value",
+ expectedValue
+ );
+ });
+ }
+ );
+
+ ([
+ [0, "en"],
+ [1, "en"],
+ [2, "en"],
+ [0, "es-ES"],
+ [1, "es-ES"],
+ [2, "es-ES"],
+ [0, "fr"],
+ [1, "fr"],
+ [2, "fr"],
+ [0, "pt-PT"],
+ [1, "pt-PT"],
+ [2, "pt-PT"],
+ ] as [DecimalProps["precision"], string][]).forEach(
+ ([precisionValue, localeValue]) => {
+ test(`should format an empty value correctly when precision is ${precisionValue}, locale is ${localeValue} and allowEmptyValue is true`, async ({
+ mount,
+ page,
+ }) => {
+ await mount(
+
+ );
+
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.fill("100");
+ await commonDataElementInputPreviewElement.clear();
+ await commonDataElementInputPreviewElement.blur();
+
+ await expect(commonDataElementInputPreviewElement).toHaveAttribute(
+ "value",
+ ""
+ );
+ });
+ }
+ );
+});
+
+test.describe("check events for Decimal component", () => {
+ ([
+ ["1", "1.00"],
+ ["12", "12.00"],
+ ["123", "123.00"],
+ ] as [string, string][]).forEach(([rawValueTest, formattedValueTest]) => {
+ test(`should call onChange callback when a type event is triggered with ${rawValueTest} value`, async ({
+ mount,
+ page,
+ }) => {
+ let callbackCount = 0;
+ const callback: DecimalProps["onChange"] = () => {
+ callbackCount += 1;
+ };
+ await mount();
+
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.fill(rawValueTest);
+ expect(callbackCount).toBe(1);
+ await commonDataElementInputPreviewElement.blur();
+ await expect(commonDataElementInputPreviewElement).toHaveAttribute(
+ "value",
+ formattedValueTest
+ );
+ });
+ });
+
+ test("should call onBlur callback when a blur event is triggered", async ({
+ mount,
+ page,
+ }) => {
+ let callbackCount = 0;
+ const callback: DecimalProps["onBlur"] = () => {
+ callbackCount += 1;
+ };
+ await mount();
+
+ const inputValue = "123";
+ const commonDataElementInputPreviewElement = commonDataElementInputPreview(
+ page
+ );
+ await commonDataElementInputPreviewElement.fill(inputValue);
+ await commonDataElementInputPreviewElement.blur();
+ expect(callbackCount).toBe(1);
+ });
+});
+
+test.describe("Accessibility tests for Decimal component", () => {
+ (["small", "medium", "large"] as DecimalProps["size"][]).forEach((size) => {
+ test(`should pass accessibility tests for Decimal with ${size} input size`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+ });
+
+ (["left", "right"] as DecimalProps["labelAlign"][]).forEach((labelAlign) => {
+ test(`should pass accessibility tests for Decimal with label aligned ${labelAlign}`, async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+ });
+
+ test("should pass accessibility tests for Decimal with custom precision", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal with custom label width and input width", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal with custom max width", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal with field help", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal with label help", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ const getDataElementByValueElementQuestion = getDataElementByValue(
+ page,
+ "question"
+ );
+ await getDataElementByValueElementQuestion.hover();
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal required", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal left aligned", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal with validation", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal validations redesigned", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal default", async ({
+ mount,
+ page,
+ }) => {
+ await mount(
+
+ );
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal with readOnly prop", async ({
+ mount,
+ page,
+ }) => {
+ await mount(
+
+ );
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal with tooltip", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+
+ test("should pass accessibility tests for Decimal with tooltip label", async ({
+ mount,
+ page,
+ }) => {
+ await mount();
+
+ await checkAccessibility(page);
+ });
+});
+
+test("should have the expected border radius styling", async ({
+ mount,
+ page,
+}) => {
+ await mount();
+
+ const getElementElementInput = getElement(page, "input");
+ await expect(getElementElementInput).toHaveCSS("border-radius", "4px");
+});