diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml
index 7235e5a7a7..c242263eeb 100644
--- a/.github/workflows/check-links.yml
+++ b/.github/workflows/check-links.yml
@@ -13,4 +13,4 @@ jobs:
- name: Link Checker
id: lychee
- uses: lycheeverse/lychee-action@v2.0.2
\ No newline at end of file
+ uses: lycheeverse/lychee-action@v2.0.2
diff --git a/.github/workflows/create-issue.yml b/.github/workflows/create-issue.yml
index e83680b354..9a0556cb09 100644
--- a/.github/workflows/create-issue.yml
+++ b/.github/workflows/create-issue.yml
@@ -23,9 +23,6 @@ jobs:
run: cd scripts && npm install
- name: Run Autorespond Script
- run:
- node scripts/new_issue-message.js
+ run: node scripts/new_issue-message.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
-
diff --git a/.github/workflows/inactive-issues.yml b/.github/workflows/inactive-issues.yml
index 8848f2c70a..2a7d1c8bb5 100644
--- a/.github/workflows/inactive-issues.yml
+++ b/.github/workflows/inactive-issues.yml
@@ -16,5 +16,5 @@ jobs:
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 15 days with no activity. If there are no further updates or modifications within the next 15 days, it will be automatically closed."
close-issue-message: "This issue has been closed as it has been inactive for 15 days since being marked as stale."
- exempt-issue-labels: 'non-closable'
- repo-token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+ exempt-issue-labels: "non-closable"
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000000..d0a778429a
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+npx lint-staged
\ No newline at end of file
diff --git a/.lycheeignore b/.lycheeignore
index 90a84c29b1..6e06c8b2cb 100644
--- a/.lycheeignore
+++ b/.lycheeignore
@@ -1,2 +1 @@
-http://localhost
-%25PUBLIC_URL%25
\ No newline at end of file
+http://localhost
\ No newline at end of file
diff --git a/apps/website/.eslintrc.json b/apps/website/.eslintrc.json
deleted file mode 100644
index bffb357a71..0000000000
--- a/apps/website/.eslintrc.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "extends": "next/core-web-vitals"
-}
diff --git a/apps/website/eslint.config.js b/apps/website/eslint.config.js
new file mode 100644
index 0000000000..5a35fc9e65
--- /dev/null
+++ b/apps/website/eslint.config.js
@@ -0,0 +1,9 @@
+import nextConfig from "@dxc-technology/eslint-config/next.js";
+import { fileURLToPath } from "url";
+import { dirname } from "path";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+/** @type {import("eslint").Config[]} */
+export default [{ ignores: ["out/**", ".next/**", "eslint.config.js"] }, ...nextConfig({ tsconfigRootDir: __dirname })];
diff --git a/apps/website/global-styles.css b/apps/website/global-styles.css
index 85ab1d5744..d32ec47d6c 100644
--- a/apps/website/global-styles.css
+++ b/apps/website/global-styles.css
@@ -12,12 +12,13 @@
/* TODO: Remove global styles completely from the website */
body {
margin: 0;
- font-family: Open Sans, sans-serif;
+ font-family:
+ Open Sans,
+ sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
- monospace;
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
diff --git a/apps/website/next.config.js b/apps/website/next.config.ts
similarity index 70%
rename from apps/website/next.config.js
rename to apps/website/next.config.ts
index 7fb6d7f8f9..185f74340f 100644
--- a/apps/website/next.config.js
+++ b/apps/website/next.config.ts
@@ -1,12 +1,15 @@
-/** @type {import('next').NextConfig} */
-module.exports = {
+import type { NextConfig } from "next";
+import type { Configuration } from "webpack";
+
+const nextConfig: NextConfig = {
images: {
loader: "custom",
},
output: "export",
trailingSlash: true,
- webpack: (config) => {
- config.module.rules.push({
+ webpack: (config: Configuration): Configuration => {
+ config.module = config.module || { rules: [] };
+ config.module.rules?.push({
test: /\.md$/,
use: "raw-loader",
});
@@ -30,3 +33,5 @@ module.exports = {
"@cloudscape-design/theming-runtime",
],
};
+
+export default nextConfig;
diff --git a/apps/website/package.json b/apps/website/package.json
index cde948d549..e550380230 100644
--- a/apps/website/package.json
+++ b/apps/website/package.json
@@ -5,7 +5,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "eslint . --max-warnings 0"
},
"dependencies": {
"@cloudscape-design/components": "^3.0.706",
@@ -33,8 +33,8 @@
"@types/react": "^18",
"@types/react-color": "^3.0.6",
"@types/react-dom": "^18",
- "eslint": "^8",
- "eslint-config-next": "14.2.4",
+ "eslint": "^9.36.0",
+ "eslint-config-next": "15.5.4",
"typescript": "^5.6.3"
}
}
diff --git a/apps/website/pages/_document.tsx b/apps/website/pages/_document.tsx
index dc97779195..4ccbe68f39 100644
--- a/apps/website/pages/_document.tsx
+++ b/apps/website/pages/_document.tsx
@@ -1,25 +1,27 @@
-import Document, { Head, Html, Main, NextScript } from "next/document";
+import Document, { DocumentContext, DocumentInitialProps, Head, Html, Main, NextScript } from "next/document";
import createEmotionServer from "@emotion/server/create-instance";
-import React from "react";
import createCache from "@emotion/cache";
+import { Children } from "react";
export default class MyDocument extends Document {
- static async getInitialProps(ctx: any) {
+ static async getInitialProps(ctx: DocumentContext): Promise {
const originalRenderPage = ctx.renderPage;
const cache = createCache({ key: "css", prepend: true });
- const { extractCriticalToChunks } = createEmotionServer(cache);
+ const emotionServer = createEmotionServer(cache);
ctx.renderPage = () =>
originalRenderPage({
- enhanceApp: (App: any) =>
+ enhanceApp: (App) =>
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
function EnhanceApp(props: any) {
return ;
},
});
const initialProps = await Document.getInitialProps(ctx);
- const emotionStyles = extractCriticalToChunks(initialProps.html);
+ const emotionStyles = emotionServer.extractCriticalToChunks(initialProps.html);
+
const emotionStyleTags = emotionStyles.styles.map((style) => (
diff --git a/packages/lib/.storybook/preview.tsx b/packages/lib/.storybook/preview.tsx
index 361952531d..0b1158eaf7 100644
--- a/packages/lib/.storybook/preview.tsx
+++ b/packages/lib/.storybook/preview.tsx
@@ -1,8 +1,8 @@
-import type { Preview } from "@storybook/react";
-import { disabledRules } from "../test/accessibility/rules/common/disabledRules";
+import disabledRules from "../test/accessibility/rules/common/disabledRules";
import "../src/styles/variables.css";
+import { PreviewExtended } from "./types";
-const preview: Preview = {
+const preview: PreviewExtended = {
parameters: {
controls: {
matchers: {
diff --git a/packages/lib/.storybook/test-runner.ts b/packages/lib/.storybook/test-runner.ts
index f82ec29ed7..1bfb228e56 100644
--- a/packages/lib/.storybook/test-runner.ts
+++ b/packages/lib/.storybook/test-runner.ts
@@ -1,6 +1,6 @@
import { injectAxe, checkA11y, configureAxe } from "axe-playwright";
import { getStoryContext, type TestRunnerConfig } from "@storybook/test-runner";
-import { ViewportParameters, ViewportStyles } from "./types";
+import { PreviewExtended, ViewportStyles } from "./types";
const DEFAULT_VIEWPORT_SIZE = { width: 1280, height: 720 };
@@ -12,38 +12,37 @@ const a11yConfig: TestRunnerConfig = {
// Get the entire context of a story, including parameters, args, argTypes, etc.
const storyContext = await getStoryContext(page, context);
// Apply viewport handle support
- const viewPortParams: ViewportParameters = storyContext.parameters?.viewport;
+ const parameters = storyContext.parameters as Partial | undefined;
+ const viewPortParams = parameters?.viewport;
const defaultViewport = viewPortParams?.defaultViewport;
- const viewport = defaultViewport && viewPortParams?.viewports[defaultViewport]?.styles;
+ const viewport = defaultViewport ? viewPortParams.viewports?.[defaultViewport]?.styles : undefined;
+
const parsedViewportSizes: ViewportStyles = viewport
- ? Object.entries(viewport).reduce(
- (acc, [screen, size]) => ({
- ...acc,
- [screen]: parseInt(size),
- }),
- {} as ViewportStyles
- )
+ ? Object.entries(viewport).reduce((acc, [screen, size]) => {
+ const safeSize = typeof size === "string" ? parseInt(size, 10) : undefined;
+ if (safeSize) acc[screen as keyof ViewportStyles] = safeSize;
+ return acc;
+ }, {} as ViewportStyles)
: DEFAULT_VIEWPORT_SIZE;
- if (parsedViewportSizes && Object.keys(parsedViewportSizes)?.length !== 0) {
- page.setViewportSize(parsedViewportSizes);
+ if (parsedViewportSizes && Object.keys(parsedViewportSizes).length) {
+ await page.setViewportSize(parsedViewportSizes);
}
} catch (err) {
console.error("Problem when loading the Story Context -> ", err);
}
},
+
async postVisit(page, context) {
try {
// Get the entire context of a story, including parameters, args, argTypes, etc.
const storyContext = await getStoryContext(page, context);
- // Do not run a11y tests on disabled stories.
- if (storyContext.parameters?.a11y?.disable) {
- return;
- }
+ const parameters = storyContext.parameters as Partial | undefined;
+
+ if (parameters?.a11y?.disable) return;
- // Apply story-level a11y rules
await configureAxe(page, {
- rules: storyContext?.parameters?.a11y?.config?.rules,
+ rules: parameters?.a11y?.config?.rules,
});
} catch (err) {
console.error("Problem when loading the Story Context -> ", err);
@@ -58,4 +57,4 @@ const a11yConfig: TestRunnerConfig = {
},
};
-module.exports = a11yConfig;
+export default a11yConfig;
diff --git a/packages/lib/.storybook/types.ts b/packages/lib/.storybook/types.ts
index c0d8daca49..f4910bbcf3 100644
--- a/packages/lib/.storybook/types.ts
+++ b/packages/lib/.storybook/types.ts
@@ -1,17 +1,47 @@
+import { Preview } from "@storybook/react";
+
+export interface ViewportStyles {
+ height: number;
+ width: number;
+}
type Styles = ViewportStyles | ((s: ViewportStyles | undefined) => ViewportStyles) | null;
interface Viewport {
name: string;
styles: Styles;
type: "desktop" | "mobile" | "tablet" | "other";
}
-export interface ViewportStyles {
- height: number;
- width: number;
-}
interface ViewportMap {
[key: string]: Viewport;
}
-export interface ViewportParameters {
+interface ViewportParameters {
viewports: ViewportMap;
defaultViewport: string;
}
+
+interface A11yRule {
+ id: string;
+ enabled: boolean;
+}
+
+interface A11yParameters {
+ disable?: boolean;
+ config: {
+ rules: A11yRule[];
+ };
+ options?: Record;
+}
+
+interface StorybookParameters {
+ controls: {
+ matchers: {
+ color: RegExp;
+ date: RegExp;
+ };
+ };
+ a11y: A11yParameters;
+ viewport?: ViewportParameters;
+}
+
+export interface PreviewExtended extends Omit {
+ parameters: StorybookParameters;
+}
diff --git a/packages/lib/babel.config.js b/packages/lib/babel.config.js
deleted file mode 100644
index 600350bcb8..0000000000
--- a/packages/lib/babel.config.js
+++ /dev/null
@@ -1,26 +0,0 @@
-module.exports = {
- presets: [
- "@babel/preset-env",
- [
- "@babel/preset-react",
- {
- runtime: "automatic",
- },
- ],
- "@babel/preset-typescript",
- ],
- plugins: [
- "@babel/plugin-proposal-optional-chaining",
- "@babel/plugin-proposal-nullish-coalescing-operator",
- "@babel/plugin-transform-runtime",
- [
- "@emotion",
- {
- sourceMap: true,
- autoLabel: "dev-only",
- labelFormat: "[local]",
- },
- ],
- ],
- ignore: ["**/*.stories.*", "**/*.d.ts"],
-};
diff --git a/packages/lib/babel.config.json b/packages/lib/babel.config.json
new file mode 100644
index 0000000000..658689a58c
--- /dev/null
+++ b/packages/lib/babel.config.json
@@ -0,0 +1,26 @@
+{
+ "presets": [
+ "@babel/preset-env",
+ [
+ "@babel/preset-react",
+ {
+ "runtime": "automatic"
+ }
+ ],
+ "@babel/preset-typescript"
+ ],
+ "plugins": [
+ "@babel/plugin-proposal-optional-chaining",
+ "@babel/plugin-proposal-nullish-coalescing-operator",
+ "@babel/plugin-transform-runtime",
+ [
+ "@emotion",
+ {
+ "sourceMap": true,
+ "autoLabel": "dev-only",
+ "labelFormat": "[local]"
+ }
+ ]
+ ],
+ "ignore": ["**/*.stories.jsx", "**/*.stories.tsx", "**/*.d.ts"]
+}
diff --git a/packages/lib/esbuild-plugin-babel.d.ts b/packages/lib/esbuild-plugin-babel.d.ts
new file mode 100644
index 0000000000..8c1ef11730
--- /dev/null
+++ b/packages/lib/esbuild-plugin-babel.d.ts
@@ -0,0 +1,12 @@
+declare module "esbuild-plugin-babel" {
+ import type { Plugin } from "esbuild";
+
+ interface BabelPluginOptions {
+ configFile?: string;
+ filter?: RegExp;
+ }
+
+ function babel(_options?: BabelPluginOptions): Plugin;
+
+ export default babel;
+}
diff --git a/packages/lib/eslint.config.js b/packages/lib/eslint.config.js
new file mode 100644
index 0000000000..e7686661f3
--- /dev/null
+++ b/packages/lib/eslint.config.js
@@ -0,0 +1,12 @@
+import libraryConfig from "@dxc-technology/eslint-config/library.js";
+import { fileURLToPath } from "url";
+import { dirname } from "path";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+/** @type {import("eslint").Config[]} */
+export default [
+ { ignores: ["dist/**", "coverage/**", "eslint.config.js"] },
+ ...libraryConfig({ tsconfigRootDir: __dirname }),
+];
diff --git a/packages/lib/jest.config.accessibility.js b/packages/lib/jest.config.accessibility.js
deleted file mode 100644
index 4989792ff8..0000000000
--- a/packages/lib/jest.config.accessibility.js
+++ /dev/null
@@ -1,9 +0,0 @@
-module.exports = {
- moduleNameMapper: {
- "\\.(css|less|scss|sass)$": "identity-obj-proxy",
- "\\.(svg)$": "/test/mocks/svgMock.js",
- "\\.(png)$": "/test/mocks/pngMock.js",
- },
- testMatch: ["**/?(*.)+(accessibility.)(spec|test).[jt]s?(x)"],
- setupFilesAfterEnv: ["/setupJestAxe.js"],
-};
diff --git a/packages/lib/jest.config.accessibility.ts b/packages/lib/jest.config.accessibility.ts
new file mode 100644
index 0000000000..a43d48f096
--- /dev/null
+++ b/packages/lib/jest.config.accessibility.ts
@@ -0,0 +1,16 @@
+import type { Config } from "jest";
+
+const configAccessibility: Config = {
+ moduleNameMapper: {
+ "\\.(css|less|scss|sass)$": "identity-obj-proxy",
+ "\\.(svg)$": "/test/mocks/svgMock.ts",
+ "\\.(png)$": "/test/mocks/pngMock.ts",
+ },
+ testMatch: ["**/?(*.)+(accessibility.)(spec|test).[jt]s?(x)"],
+ setupFilesAfterEnv: ["/setupJestAxe.ts"],
+ transform: {
+ "^.+\\.[tj]sx?$": "babel-jest",
+ },
+};
+
+export default configAccessibility;
diff --git a/packages/lib/jest.config.js b/packages/lib/jest.config.ts
similarity index 68%
rename from packages/lib/jest.config.js
rename to packages/lib/jest.config.ts
index 9b9356956f..c84a9d09a5 100644
--- a/packages/lib/jest.config.js
+++ b/packages/lib/jest.config.ts
@@ -1,4 +1,6 @@
-module.exports = {
+import type { Config } from "jest";
+
+const config: Config = {
collectCoverage: true,
coveragePathIgnorePatterns: [
"utils.ts",
@@ -7,11 +9,13 @@ module.exports = {
],
moduleNameMapper: {
"\\.(css|less|scss|sass)$": "identity-obj-proxy",
- "\\.(svg)$": "/test/mocks/svgMock.js",
- "\\.(png)$": "/test/mocks/pngMock.js",
+ "\\.(svg)$": "/test/mocks/svgMock.ts",
+ "\\.(png)$": "/test/mocks/pngMock.ts",
},
testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)", "!**/?(*.)+(accessibility.)(spec|test).[jt]s?(x)"],
transform: {
"^.+\\.[tj]sx?$": "babel-jest",
},
};
+
+export default config;
diff --git a/packages/lib/package.json b/packages/lib/package.json
index 5db49e4340..027239dfc2 100644
--- a/packages/lib/package.json
+++ b/packages/lib/package.json
@@ -24,9 +24,9 @@
"storybook": "storybook dev -p 6006",
"storybook:accessibility": "test-storybook",
"storybook:accessibility:ci": "test-storybook --maxWorkers=2",
- "test": "jest --env=jsdom --config=./jest.config.js",
- "test:accessibility": "jest --env=jsdom --config=./jest.config.accessibility.js",
- "test:watch": "jest --env=jsdom --config=./jest.config.js --watch"
+ "test": "jest --env=jsdom --config=./jest.config.ts",
+ "test:accessibility": "jest --env=jsdom --config=./jest.config.accessibility.ts",
+ "test:watch": "jest --env=jsdom --config=./jest.config.ts --watch"
},
"peerDependencies": {
"@emotion/react": "^11.14.0",
@@ -52,7 +52,6 @@
"@babel/preset-env": "^7.16.8",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
- "@chromatic-com/storybook": "^1.5.0",
"@dxc-technology/eslint-config": "*",
"@dxc-technology/typescript-config": "*",
"@emotion/babel-plugin": "^11.13.5",
@@ -62,14 +61,17 @@
"@storybook/addon-essentials": "^8.1.10",
"@storybook/addon-interactions": "^8.1.10",
"@storybook/addon-links": "^8.1.10",
- "@storybook/addon-viewport": "^8.2.9",
- "@storybook/blocks": "^8.1.10",
- "@storybook/react": "^8.1.10",
- "@storybook/react-vite": "^8.1.10",
- "@storybook/test": "^8.1.10",
+ "@storybook/addon-viewport": "^8.3.2",
+ "@storybook/blocks": "^8.1.11",
+ "@storybook/react": "^8.1.11",
+ "@storybook/react-vite": "^8.1.11",
+ "@storybook/test": "^8.1.11",
"@storybook/test-runner": "^0.22.0",
+ "@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^13.0.0",
+ "@turbo/gen": "^1.12.4",
+ "@types/color": "^3.0.6",
"@types/eslint": "^8.56.5",
"@types/jest": "^29.5.12",
"@types/jest-axe": "^3.5.9",
@@ -80,15 +82,22 @@
"axe-playwright": "^2.1.0",
"chromatic": "^11.5.4",
"esbuild-plugin-babel": "^0.2.3",
- "eslint": "^8.57.0",
+ "eslint": "^9.36.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-import": "^2.29.0",
+ "eslint-plugin-jest": "^29.0.1",
+ "eslint-plugin-jsx-a11y": "^6.8.0",
+ "eslint-plugin-prettier": "^5.1.3",
+ "eslint-plugin-react": "^7.34.1",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-security": "^3.0.0",
"eslint-plugin-storybook": "^0.8.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.7.0",
"jest-axe": "^10.0.0",
"jest-environment-jsdom": "^29.7.0",
"playwright": "^1.44.1",
- "storybook": "^8.1.10",
- "storybook-addon-pseudo-states": "^3.1.1",
+ "storybook": "^8.1.11",
"tsup": "^8.1.0",
"typescript": "^5.6.3"
}
diff --git a/packages/lib/setupJestAxe.js b/packages/lib/setupJestAxe.ts
similarity index 100%
rename from packages/lib/setupJestAxe.js
rename to packages/lib/setupJestAxe.ts
diff --git a/packages/lib/src/accordion/Accordion.accessibility.test.tsx b/packages/lib/src/accordion/Accordion.accessibility.test.tsx
index f5850fca55..cfac5753dc 100644
--- a/packages/lib/src/accordion/Accordion.accessibility.test.tsx
+++ b/packages/lib/src/accordion/Accordion.accessibility.test.tsx
@@ -40,7 +40,7 @@ describe("Accordion component accessibility tests", () => {
expect(results).toHaveNoViolations();
});
- it("Should not have basic accessibility issues", async () => {
+ it("Should not have basic accessibility issues with badge and status light", async () => {
const { container } = render(
{
expect(results).toHaveNoViolations();
});
- it("Should not have basic accessibility issues for disabled mode", async () => {
+ it("Should not have basic accessibility issues for disabled mode with badge and status light", async () => {
const { container } = render(
margin && typeof margin === "object" && margin.bottom ? spaces[margin.bottom] : ""};
margin-left: ${({ margin }) => (margin && typeof margin === "object" && margin.left ? spaces[margin.left] : "")};
- cursor: "pointer";
+ cursor: pointer;
// first accordion
> div:first-of-type:not(:only-of-type) {
@@ -57,22 +57,39 @@ const AccordionContainer = styled.div<{
}
`;
+const AccordionItemWithProvider = ({
+ child,
+ index,
+ contextValue,
+}: {
+ child: React.ReactElement;
+ index: number;
+ contextValue: Omit;
+}) => {
+ const memoizedContext = useMemo(
+ () => ({ index, ...contextValue }),
+ [index, contextValue.activeIndex, contextValue.handlerActiveChange, contextValue.independent]
+ );
+
+ return {child};
+};
+
const DxcAccordion = (props: AccordionPropsType): JSX.Element => {
- const { children, margin, onActiveChange } = props;
+ const { children, defaultIndexActive, independent, indexActive, margin, onActiveChange } = props;
const [innerIndexActive, setInnerIndexActive] = useState(
- props.independent
- ? (props.defaultIndexActive ?? -1)
- : Array.isArray(props.defaultIndexActive)
- ? props.defaultIndexActive.filter((i) => i !== undefined)
+ independent
+ ? (defaultIndexActive ?? -1)
+ : Array.isArray(defaultIndexActive)
+ ? defaultIndexActive.filter((i) => i !== undefined)
: []
);
const handlerActiveChange = useCallback(
(index: number | number[]) => {
- if (props.indexActive == null) {
+ if (indexActive == null) {
setInnerIndexActive((prev) => {
- if (props.independent) return typeof index === "number" ? (index === prev ? -1 : index) : prev;
+ if (independent) return typeof index === "number" ? (index === prev ? -1 : index) : prev;
else {
const prevArray = Array.isArray(prev) ? prev : [];
return Array.isArray(index)
@@ -85,24 +102,27 @@ const DxcAccordion = (props: AccordionPropsType): JSX.Element => {
}
onActiveChange?.(index as number & number[]);
},
- [props.indexActive, props.independent, onActiveChange, innerIndexActive]
+ [indexActive, independent, onActiveChange, innerIndexActive]
);
const contextValue = useMemo(
() => ({
- activeIndex: props.indexActive ?? innerIndexActive,
+ activeIndex: indexActive ?? innerIndexActive,
handlerActiveChange,
- independent: props.independent,
+ independent,
}),
- [props.indexActive, innerIndexActive, handlerActiveChange, props.independent]
+ [indexActive, innerIndexActive, handlerActiveChange, independent]
);
return (
{Children.map(children, (accordion, index) => (
-
- {accordion}
-
+
))}
);
diff --git a/packages/lib/src/accordion/AccordionItem.tsx b/packages/lib/src/accordion/AccordionItem.tsx
index ae49259981..51323530c1 100644
--- a/packages/lib/src/accordion/AccordionItem.tsx
+++ b/packages/lib/src/accordion/AccordionItem.tsx
@@ -4,7 +4,6 @@ import { AccordionItemProps } from "./types";
import DxcIcon from "../icon/Icon";
import DxcFlex from "../flex/Flex";
import DxcContainer from "../container/Container";
-import React from "react";
import AccordionContext from "./AccordionContext";
const AccordionContainer = styled.div`
@@ -154,11 +153,13 @@ const AccordionItem = ({
}: AccordionItemProps): JSX.Element => {
const id = useId();
const { activeIndex, handlerActiveChange, index, independent } = useContext(AccordionContext) ?? {};
- const isItemExpanded = useMemo(() => {
- return independent
- ? activeIndex === index
- : Array.isArray(activeIndex) && index !== undefined && activeIndex.includes(index);
- }, [independent, activeIndex, index]);
+ const isItemExpanded = useMemo(
+ () =>
+ independent
+ ? activeIndex === index
+ : Array.isArray(activeIndex) && index !== undefined && activeIndex.includes(index),
+ [independent, activeIndex, index]
+ );
const handleAccordionState = () => {
if (index !== undefined) handlerActiveChange?.(index);
@@ -203,12 +204,12 @@ const AccordionItem = ({
)}
{badge && badge?.position === "after" && !assistiveText && (
- {disabled ? React.cloneElement(badge.element as ReactElement, { color: "grey" }) : badge.element}
+ {disabled ? cloneElement(badge.element as ReactElement, { color: "grey" }) : badge.element}
)}
{badge?.position !== "after" && statusLight && !assistiveText && (
- {disabled ? React.cloneElement(statusLight as ReactElement, { mode: "default" }) : statusLight}
+ {disabled ? cloneElement(statusLight as ReactElement, { mode: "default" }) : statusLight}
)}
diff --git a/packages/lib/src/action-icon/ActionIcon.stories.tsx b/packages/lib/src/action-icon/ActionIcon.stories.tsx
index be36839843..cdb8bdb4df 100644
--- a/packages/lib/src/action-icon/ActionIcon.stories.tsx
+++ b/packages/lib/src/action-icon/ActionIcon.stories.tsx
@@ -1,10 +1,10 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcActionIcon from "./ActionIcon";
-import { userEvent, within } from "@storybook/test";
import DxcTooltip from "../tooltip/Tooltip";
import DxcInset from "../inset/Inset";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Action Icon ",
diff --git a/packages/lib/src/action-icon/ActionIcon.tsx b/packages/lib/src/action-icon/ActionIcon.tsx
index db8bc8184b..b866ce52cb 100644
--- a/packages/lib/src/action-icon/ActionIcon.tsx
+++ b/packages/lib/src/action-icon/ActionIcon.tsx
@@ -35,8 +35,8 @@ const ActionIcon = styled.button`
}
`;
-export default forwardRef(
- ({ disabled = false, icon, onClick, tabIndex, title }, ref) => (
+const ForwardedActionIcon = forwardRef(
+ ({ disabled = false, title, icon, onClick, tabIndex }, ref) => (
(
)
);
+
+ForwardedActionIcon.displayName = "ActionIcon";
+
+export default ForwardedActionIcon;
diff --git a/packages/lib/src/action-icon/types.ts b/packages/lib/src/action-icon/types.ts
index 08ac678cbe..c527174941 100644
--- a/packages/lib/src/action-icon/types.ts
+++ b/packages/lib/src/action-icon/types.ts
@@ -1,5 +1,5 @@
-import { SVG } from "../common/utils";
import { MouseEvent } from "react";
+import { SVG } from "../common/utils";
type Props = {
/**
diff --git a/packages/lib/src/alert/Actions.tsx b/packages/lib/src/alert/Actions.tsx
index ab2bf621b3..65ef62cfb8 100644
--- a/packages/lib/src/alert/Actions.tsx
+++ b/packages/lib/src/alert/Actions.tsx
@@ -35,4 +35,6 @@ const Actions = memo(
)
);
+Actions.displayName = "Actions";
+
export default Actions;
diff --git a/packages/lib/src/alert/Alert.accessibility.test.tsx b/packages/lib/src/alert/Alert.accessibility.test.tsx
index 21c8713932..c20264cdf2 100644
--- a/packages/lib/src/alert/Alert.accessibility.test.tsx
+++ b/packages/lib/src/alert/Alert.accessibility.test.tsx
@@ -2,11 +2,11 @@ import { render } from "@testing-library/react";
import { axe } from "../../test/accessibility/axe-helper";
import DxcAlert from "./Alert";
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const messages = [
{ text: "Message 1", onClose: () => {} },
@@ -27,7 +27,9 @@ describe("Alert component accessibility tests", () => {
expect(results).toHaveNoViolations();
});
it("Should not have basic accessibility issues for modal mode", async () => {
- const { container } = render( {} }} />);
+ const { container } = render(
+ {} }} />
+ );
const results = await axe(container);
expect(results).toHaveNoViolations();
});
diff --git a/packages/lib/src/alert/Alert.stories.tsx b/packages/lib/src/alert/Alert.stories.tsx
index 0e582fc398..73d018151c 100644
--- a/packages/lib/src/alert/Alert.stories.tsx
+++ b/packages/lib/src/alert/Alert.stories.tsx
@@ -1,8 +1,8 @@
+import { Meta, StoryObj } from "@storybook/react";
import DxcAlert from "./Alert";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcLink from "../link/Link";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Alert",
diff --git a/packages/lib/src/alert/Alert.test.tsx b/packages/lib/src/alert/Alert.test.tsx
index 44b883c38c..a62c5430d4 100644
--- a/packages/lib/src/alert/Alert.test.tsx
+++ b/packages/lib/src/alert/Alert.test.tsx
@@ -2,11 +2,11 @@ import "@testing-library/jest-dom";
import { render, fireEvent } from "@testing-library/react";
import DxcAlert from "./Alert";
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const messages = [
{ text: "Message 1", onClose: () => {} },
@@ -48,11 +48,11 @@ describe("Alert component tests", () => {
test("Inline alert calls correctly the function onClose of several messages", () => {
const onClose1 = jest.fn();
const onClose2 = jest.fn();
- const messages = [
+ const onCloseMessages = [
{ text: "Message 1", onClose: onClose1 },
{ text: "Message 2", onClose: onClose2 },
];
- const { getByRole } = render();
+ const { getByRole } = render();
const closeButton = getByRole("button", { name: "Close message" });
const nextButton = getByRole("button", { name: "Next message" });
fireEvent.click(closeButton);
@@ -70,7 +70,7 @@ describe("Alert component tests", () => {
});
test("Alert with several messages closes properly each one", () => {
const { getByRole, getByText } = render();
- let closeButton = getByRole("button", { name: "Close message" });
+ const closeButton = getByRole("button", { name: "Close message" });
const nextButton = getByRole("button", { name: "Next message" });
expect(getByText("1 of 4")).toBeTruthy();
expect(getByText("Message 1")).toBeTruthy();
diff --git a/packages/lib/src/alert/Alert.tsx b/packages/lib/src/alert/Alert.tsx
index a9321f97a1..b2609c3728 100644
--- a/packages/lib/src/alert/Alert.tsx
+++ b/packages/lib/src/alert/Alert.tsx
@@ -92,7 +92,7 @@ const getIcon = (semantic: AlertPropsType["semantic"]) => {
}
};
-export default function DxcAlert({
+const DxcAlert = ({
closable = true,
message = [],
mode = "inline",
@@ -100,7 +100,7 @@ export default function DxcAlert({
secondaryAction,
semantic = "info",
title = "",
-}: AlertPropsType) {
+}: AlertPropsType) => {
const [messages, setMessages] = useState(Array.isArray(message) ? message : [message]);
const [currentIndex, setCurrentIndex] = useState(0);
@@ -122,7 +122,9 @@ export default function DxcAlert({
}, [messages, currentIndex, mode]);
useEffect(() => {
- if (currentIndex === messages.length) handlePrevOnClick();
+ if (currentIndex === messages.length) {
+ handlePrevOnClick();
+ }
}, [currentIndex, messages, handlePrevOnClick]);
return (
@@ -200,4 +202,6 @@ export default function DxcAlert({
);
-}
+};
+
+export default DxcAlert;
diff --git a/packages/lib/src/alert/ModalAlertWrapper.tsx b/packages/lib/src/alert/ModalAlertWrapper.tsx
index 3e2f5f4668..5456dd03a0 100644
--- a/packages/lib/src/alert/ModalAlertWrapper.tsx
+++ b/packages/lib/src/alert/ModalAlertWrapper.tsx
@@ -1,10 +1,10 @@
import { createPortal } from "react-dom";
+import { useEffect } from "react";
import { Global, css } from "@emotion/react";
import styled from "@emotion/styled";
import { responsiveSizes } from "../common/variables";
import FocusLock from "../utils/FocusLock";
import { ModalAlertWrapperProps } from "./types";
-import { useEffect } from "react";
const BodyStyle = () => (
void;
children: ReactNode;
};
+
+export default Props;
diff --git a/packages/lib/src/badge/Badge.stories.tsx b/packages/lib/src/badge/Badge.stories.tsx
index d06073abd3..a27a984443 100644
--- a/packages/lib/src/badge/Badge.stories.tsx
+++ b/packages/lib/src/badge/Badge.stories.tsx
@@ -1,11 +1,11 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import DxcBadge from "./Badge";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcFlex from "../flex/Flex";
import DxcInset from "../inset/Inset";
-import { userEvent, within } from "@storybook/test";
import DxcTooltip from "../tooltip/Tooltip";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Badge",
diff --git a/packages/lib/src/bleed/Bleed.stories.tsx b/packages/lib/src/bleed/Bleed.stories.tsx
index e9cfa60ab8..6c5cc28a37 100644
--- a/packages/lib/src/bleed/Bleed.stories.tsx
+++ b/packages/lib/src/bleed/Bleed.stories.tsx
@@ -1,9 +1,9 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { ReactNode } from "react";
import Title from "../../.storybook/components/Title";
import DxcBleed from "./Bleed";
import DxcFlex from "../flex/Flex";
-import { Meta, StoryObj } from "@storybook/react";
import DxcContainer from "../container/Container";
-import { ReactNode } from "react";
export default {
title: "Bleed",
diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx
index e10125df95..09af499cb4 100644
--- a/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx
+++ b/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx
@@ -1,15 +1,13 @@
import { render } from "@testing-library/react";
import { axe, formatRules } from "../../test/accessibility/axe-helper";
import DxcBreadcrumbs from "./Breadcrumbs";
-import { disabledRules as rules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules";
+import rules from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules";
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
-
- unobserve() {}
-
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const disabledRules = {
rules: formatRules(rules),
diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx
index c515ebbf83..cc5db466a0 100644
--- a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx
+++ b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx
@@ -1,11 +1,11 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcBreadcrumbs from "./Breadcrumbs";
import DxcContainer from "../container/Container";
-import { userEvent, within } from "@storybook/test";
-import { disabledRules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules";
+import disabledRules from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules";
import preview from "../../.storybook/preview";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Breadcrumbs",
@@ -15,7 +15,7 @@ export default {
config: {
rules: [
...disabledRules.map((ruleId) => ({ id: ruleId, enabled: false })),
- ...preview?.parameters?.a11y?.config?.rules,
+ ...(preview?.parameters?.a11y?.config?.rules || []),
],
},
},
@@ -159,6 +159,6 @@ export const Chromatic: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const dropdowns = canvas.getAllByRole("button");
- dropdowns[2] != null && (await userEvent.click(dropdowns[2]));
+ if (dropdowns[2] != null) await userEvent.click(dropdowns[2]);
},
};
diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.test.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.test.tsx
index 4ed7419345..7d0860d4a7 100644
--- a/packages/lib/src/breadcrumbs/Breadcrumbs.test.tsx
+++ b/packages/lib/src/breadcrumbs/Breadcrumbs.test.tsx
@@ -1,12 +1,12 @@
import { render } from "@testing-library/react";
-import DxcBreadcrumbs from "./Breadcrumbs";
import userEvent from "@testing-library/user-event";
+import DxcBreadcrumbs from "./Breadcrumbs";
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const items = [
{
@@ -34,16 +34,16 @@ describe("Breadcrumbs component tests", () => {
expect(breadcrumbs.getAttribute("aria-label")).toBe("example");
expect(getByText("Dark Mode").parentElement?.getAttribute("aria-current")).toBe("page");
});
- test("Collapsed variant renders all the items inside the dropdown menu except the root and the current page", async () => {
+ test("Collapsed variant renders all the items inside the dropdown menu except the root and the current page", () => {
const { queryByText, getByText, getByRole } = render();
const dropdown = getByRole("button");
expect(queryByText("User Menu")).toBeFalsy();
expect(queryByText("Preferences")).toBeFalsy();
- await userEvent.click(dropdown);
+ userEvent.click(dropdown);
expect(getByText("User Menu")).toBeTruthy();
expect(getByText("Preferences")).toBeTruthy();
});
- test("Collapsed variant, with show root set to false, renders all the items inside the dropdown menu except the current page", async () => {
+ test("Collapsed variant, with show root set to false, renders all the items inside the dropdown menu except the current page", () => {
const { queryByText, getByText, getByRole } = render(
);
@@ -51,12 +51,12 @@ describe("Breadcrumbs component tests", () => {
expect(queryByText("Home")).toBeFalsy();
expect(queryByText("User Menu")).toBeFalsy();
expect(queryByText("Preferences")).toBeFalsy();
- await userEvent.click(dropdown);
+ userEvent.click(dropdown);
expect(getByText("Home")).toBeTruthy();
expect(getByText("User Menu")).toBeTruthy();
expect(getByText("Preferences")).toBeTruthy();
});
- test("If itemsBeforeCollapse value is below two, ignores it and renders a collapsed variant", async () => {
+ test("If itemsBeforeCollapse value is below two, ignores it and renders a collapsed variant", () => {
const { getByText, getByRole } = render();
expect(getByText("Home")).toBeTruthy();
expect(getByRole("button")).toBeTruthy();
@@ -76,7 +76,7 @@ describe("Breadcrumbs component tests", () => {
userEvent.click(getByText("Home"));
expect(onItemClick).toHaveBeenCalledWith("/home");
});
- test("The onClick prop from an item is properly called (collapsed)", async () => {
+ test("The onClick prop from an item is properly called (collapsed)", () => {
const onItemClick = jest.fn();
const { getByText, getByRole } = render(
{
itemsBeforeCollapse={2}
/>
);
- await userEvent.click(getByRole("button"));
- await userEvent.click(getByText("Preferences"));
+ userEvent.click(getByRole("button"));
+ userEvent.click(getByText("Preferences"));
expect(onItemClick).toHaveBeenCalledWith("/");
});
});
diff --git a/packages/lib/src/bulleted-list/BulletedList.stories.tsx b/packages/lib/src/bulleted-list/BulletedList.stories.tsx
index e98d12caee..d3ea7c7e0b 100644
--- a/packages/lib/src/bulleted-list/BulletedList.stories.tsx
+++ b/packages/lib/src/bulleted-list/BulletedList.stories.tsx
@@ -1,8 +1,8 @@
+import { Meta, StoryObj } from "@storybook/react";
import styled from "@emotion/styled";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcBulletedList from "./BulletedList";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Bulleted List",
diff --git a/packages/lib/src/bulleted-list/BulletedList.test.tsx b/packages/lib/src/bulleted-list/BulletedList.test.tsx
index 0ac2c5fe7b..dbe805ea2c 100644
--- a/packages/lib/src/bulleted-list/BulletedList.test.tsx
+++ b/packages/lib/src/bulleted-list/BulletedList.test.tsx
@@ -1,6 +1,5 @@
import { render } from "@testing-library/react";
import DxcBulletedList from "./BulletedList";
-import DxcIcon from "../icon/Icon";
describe("Bulleted list component tests", () => {
test("The component renders properly", () => {
diff --git a/packages/lib/src/bulleted-list/BulletedList.tsx b/packages/lib/src/bulleted-list/BulletedList.tsx
index ecd64b6845..b044ae37f9 100644
--- a/packages/lib/src/bulleted-list/BulletedList.tsx
+++ b/packages/lib/src/bulleted-list/BulletedList.tsx
@@ -1,4 +1,4 @@
-import { Children, useContext } from "react";
+import { Children } from "react";
import styled from "@emotion/styled";
import DxcFlex from "../flex/Flex";
import DxcTypography from "../typography/Typography";
diff --git a/packages/lib/src/bulleted-list/types.ts b/packages/lib/src/bulleted-list/types.ts
index 280d444429..24ae1f1125 100644
--- a/packages/lib/src/bulleted-list/types.ts
+++ b/packages/lib/src/bulleted-list/types.ts
@@ -33,11 +33,11 @@ type OtherProps = {
type Props = IconProps | OtherProps;
-export default Props;
-
export type BulletedListItemPropsType = {
/**
* Text to be shown in the list.
*/
children?: ReactNode;
};
+
+export default Props;
diff --git a/packages/lib/src/button/Button.stories.tsx b/packages/lib/src/button/Button.stories.tsx
index a91e42c359..7f4e806a4b 100644
--- a/packages/lib/src/button/Button.stories.tsx
+++ b/packages/lib/src/button/Button.stories.tsx
@@ -1,11 +1,11 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import DxcButton from "./Button";
import DxcFlex from "../flex/Flex";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcInset from "../inset/Inset";
import DxcTooltip from "../tooltip/Tooltip";
-import { userEvent, within } from "@storybook/test";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Button",
diff --git a/packages/lib/src/button/Button.test.tsx b/packages/lib/src/button/Button.test.tsx
index bb51167def..4e44478090 100644
--- a/packages/lib/src/button/Button.test.tsx
+++ b/packages/lib/src/button/Button.test.tsx
@@ -4,7 +4,9 @@ import DxcButton from "./Button";
describe("Button component tests", () => {
test("Calls correct function on click", () => {
const onClick = jest.fn();
- const { getByText } = render();
+ const { getByText } = render(
+
+ );
const button = getByText("Button");
fireEvent.click(button);
expect(onClick).toHaveBeenCalled();
diff --git a/packages/lib/src/button/utils.ts b/packages/lib/src/button/utils.ts
index 13a3b1ec0c..8d6374de3b 100644
--- a/packages/lib/src/button/utils.ts
+++ b/packages/lib/src/button/utils.ts
@@ -1,11 +1,7 @@
import { getMargin } from "../common/utils";
import ButtonPropsType, { Mode, Semantic, Size } from "./types";
-export const getButtonStyles = (
- mode: Mode,
- semantic: Semantic | "unselected" | "selected",
- size: Size,
-) => {
+export const getButtonStyles = (mode: Mode, semantic: Semantic | "unselected" | "selected", size: Size) => {
let enabled = "";
let hover = "";
let active = "";
@@ -227,4 +223,4 @@ export const getHeight = (height: Size["height"]) => {
default:
return "var(--height-xl)";
}
-};
\ No newline at end of file
+};
diff --git a/packages/lib/src/card/Card.stories.tsx b/packages/lib/src/card/Card.stories.tsx
index 5be6aa4e0b..76f516465d 100644
--- a/packages/lib/src/card/Card.stories.tsx
+++ b/packages/lib/src/card/Card.stories.tsx
@@ -1,8 +1,8 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcCard from "./Card";
-import { userEvent, within } from "@storybook/test";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Card",
@@ -159,7 +159,9 @@ export const ActionCardStates: Story = {
const canvas = within(canvasElement);
await userEvent.tab();
const card = canvas.getAllByText("Hovered default with action")[1];
- card != null && (await userEvent.hover(card));
+ if (card != null) {
+ await userEvent.hover(card);
+ }
},
};
@@ -168,7 +170,11 @@ export const Chromatic: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const linkCards = canvas.getAllByRole("link");
- linkCards[1] != null && linkCards[1].focus();
- linkCards[2] != null && (await userEvent.hover(linkCards[2]));
+ if (linkCards[1] != null) {
+ linkCards[1].focus();
+ }
+ if (linkCards[2] != null) {
+ await userEvent.hover(linkCards[2]);
+ }
},
};
diff --git a/packages/lib/src/card/Card.tsx b/packages/lib/src/card/Card.tsx
index 6de93bc718..1723c5b6c2 100644
--- a/packages/lib/src/card/Card.tsx
+++ b/packages/lib/src/card/Card.tsx
@@ -42,7 +42,10 @@ const CardContainer = styled.div<{
}
`;
-const TagImage = styled.img<{ imagePadding: CardPropsType["imagePadding"]; imageCover: CardPropsType["imageCover"] }>`
+const TagImage = styled.img<{
+ imagePadding: CardPropsType["imagePadding"];
+ imageCover: CardPropsType["imageCover"];
+}>`
height: ${({ imagePadding }) =>
!imagePadding
? "100%"
@@ -102,7 +105,7 @@ const DxcCard = ({
href={linkHref ? linkHref : undefined}
shadowDepth={!outlined ? 0 : isHovered && (onClick || linkHref) ? 2 : 1}
>
-
+
{imageSrc && (
diff --git a/packages/lib/src/checkbox/Checkbox.stories.tsx b/packages/lib/src/checkbox/Checkbox.stories.tsx
index e32ac5831c..448e996608 100644
--- a/packages/lib/src/checkbox/Checkbox.stories.tsx
+++ b/packages/lib/src/checkbox/Checkbox.stories.tsx
@@ -1,8 +1,8 @@
+import { Meta, StoryObj } from "@storybook/react";
import styled from "@emotion/styled";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcCheckbox from "./Checkbox";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Checkbox",
@@ -193,7 +193,7 @@ type Story = StoryObj;
export const Chromatic: Story = {
render: Checkbox,
- play: async () => {
+ play: () => {
document.getElementById("scroll-container")?.scrollTo({ top: 50 });
},
};
diff --git a/packages/lib/src/checkbox/Checkbox.test.tsx b/packages/lib/src/checkbox/Checkbox.test.tsx
index a240718022..a0d9ca6dfd 100644
--- a/packages/lib/src/checkbox/Checkbox.test.tsx
+++ b/packages/lib/src/checkbox/Checkbox.test.tsx
@@ -35,10 +35,10 @@ describe("Checkbox component tests", () => {
fireEvent.click(checkbox);
expect(onChange).not.toHaveBeenCalled();
});
- test("Read-only checkbox sends its value on submit", async () => {
- const handlerOnSubmit = jest.fn((e) => {
+ test("Read-only checkbox sends its value on submit", () => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({ data: "checked" });
});
@@ -49,7 +49,7 @@ describe("Checkbox component tests", () => {
);
const submit = getByText("Submit");
- await userEvent.click(submit);
+ userEvent.click(submit);
expect(handlerOnSubmit).toHaveBeenCalled();
});
test("Read-only checkbox doesn't change its value with Space key", () => {
@@ -58,7 +58,12 @@ describe("Checkbox component tests", () => {
const checkbox = getByRole("checkbox");
userEvent.tab();
expect(document.activeElement === checkbox).toBeTruthy();
- fireEvent.keyDown(checkbox, { key: " ", code: "Space", keyCode: 32, charCode: 32 });
+ fireEvent.keyDown(checkbox, {
+ key: " ",
+ code: "Space",
+ keyCode: 32,
+ charCode: 32,
+ });
expect(onChange).not.toHaveBeenCalled();
});
test("Uncontrolled checkbox", () => {
@@ -97,7 +102,7 @@ describe("Checkbox component tests", () => {
expect(checkbox.getAttribute("aria-checked")).toBe("true");
expect(submitInput?.checked).toBe(true);
});
- test("Test disable keyboard and mouse interactions", () => {
+ test("Disable keyboard and mouse interactions", () => {
const onChange = jest.fn();
const { getByRole, getByText, container } = render(
@@ -113,13 +118,18 @@ describe("Checkbox component tests", () => {
userEvent.tab();
expect(document.activeElement === input).toBeFalsy();
});
- test("Test keyboard interactions", () => {
+ test("Keyboard interactions", () => {
const onChange = jest.fn();
const { getByRole } = render();
const checkbox = getByRole("checkbox");
userEvent.tab();
expect(document.activeElement === checkbox).toBeTruthy();
- fireEvent.keyDown(checkbox, { key: " ", code: "Space", keyCode: 32, charCode: 32 });
+ fireEvent.keyDown(checkbox, {
+ key: " ",
+ code: "Space",
+ keyCode: 32,
+ charCode: 32,
+ });
expect(onChange).toHaveBeenCalledWith(true);
});
});
diff --git a/packages/lib/src/checkbox/Checkbox.tsx b/packages/lib/src/checkbox/Checkbox.tsx
index f2285798e8..7f747f0463 100644
--- a/packages/lib/src/checkbox/Checkbox.tsx
+++ b/packages/lib/src/checkbox/Checkbox.tsx
@@ -160,4 +160,6 @@ const DxcCheckbox = forwardRef(
}
);
+DxcCheckbox.displayName = "DxcCheckbox";
+
export default DxcCheckbox;
diff --git a/packages/lib/src/chip/Chip.stories.tsx b/packages/lib/src/chip/Chip.stories.tsx
index c9edd3e333..c3c2ba4f69 100644
--- a/packages/lib/src/chip/Chip.stories.tsx
+++ b/packages/lib/src/chip/Chip.stories.tsx
@@ -1,8 +1,8 @@
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent } from "@storybook/test";
-import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
+import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcChip from "./Chip";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Chip",
diff --git a/packages/lib/src/container/Container.stories.tsx b/packages/lib/src/container/Container.stories.tsx
index 8846f18be0..cceee3f85c 100644
--- a/packages/lib/src/container/Container.stories.tsx
+++ b/packages/lib/src/container/Container.stories.tsx
@@ -1,8 +1,8 @@
+import { Meta, StoryObj } from "@storybook/react";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcContainer from "./Container";
import DxcTypography from "../typography/Typography";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Container",
@@ -26,7 +26,10 @@ const Listbox = ({ suggestions = [] }: { suggestions: string[] }): JSX.Element =
width="250px"
>
{suggestions.map((suggestion, index) => (
-
+
`
${({ border }) => {
let styles = "";
if (border != null) {
- switch (true) {
- case "width" in border:
- styles += border.width ? `border-width: ${border.width};` : "";
- case "style" in border:
- styles += border.style ? `border-style: ${border.style};` : "";
- case "color" in border:
- styles += border.color ? `border-color: ${border.color};` : "";
+ if ("width" in border) {
+ styles += border.width ? `border-width: ${border.width};` : "";
+ }
+ if ("style" in border) {
+ styles += border.style ? `border-style: ${border.style};` : "";
+ }
+ if ("color" in border) {
+ styles += border.color ? `border-color: ${border.color};` : "";
}
}
return styles;
@@ -49,15 +50,17 @@ const Container = styled.div`
${({ border }) => {
let styles = "";
if (border != null) {
- switch (true) {
- case "top" in border:
- styles += border.top ? getBorderStyles("top", border.top) : "";
- case "right" in border:
- styles += border.right ? getBorderStyles("right", border.right) : "";
- case "left" in border:
- styles += border.left ? getBorderStyles("left", border.left) : "";
- case "bottom" in border:
- styles += border.bottom ? getBorderStyles("bottom", border.bottom) : "";
+ if ("top" in border) {
+ styles += border.top ? getBorderStyles("top", border.top) : "";
+ }
+ if ("right" in border) {
+ styles += border.right ? getBorderStyles("right", border.right) : "";
+ }
+ if ("left" in border) {
+ styles += border.left ? getBorderStyles("left", border.left) : "";
+ }
+ if ("bottom" in border) {
+ styles += border.bottom ? getBorderStyles("bottom", border.bottom) : "";
}
}
return styles;
diff --git a/packages/lib/src/container/types.ts b/packages/lib/src/container/types.ts
index 2346f54204..dc27f4b6fd 100644
--- a/packages/lib/src/container/types.ts
+++ b/packages/lib/src/container/types.ts
@@ -33,11 +33,11 @@ export type BorderProperties = {
type Border =
| BorderProperties
| {
- top?: BorderProperties;
- right?: BorderProperties;
- bottom?: BorderProperties;
- left?: BorderProperties;
- };
+ top?: BorderProperties;
+ right?: BorderProperties;
+ bottom?: BorderProperties;
+ left?: BorderProperties;
+ };
type Outline = BorderProperties & {
offset?: string;
diff --git a/packages/lib/src/contextual-menu/ContextualMenu.accessibility.test.tsx b/packages/lib/src/contextual-menu/ContextualMenu.accessibility.test.tsx
index 352698ffaf..5f546dbcfa 100644
--- a/packages/lib/src/contextual-menu/ContextualMenu.accessibility.test.tsx
+++ b/packages/lib/src/contextual-menu/ContextualMenu.accessibility.test.tsx
@@ -3,7 +3,7 @@ import { axe } from "../../test/accessibility/axe-helper";
import DxcBadge from "../badge/Badge";
import DxcContextualMenu from "./ContextualMenu";
-const badge_icon = (
+const badgeIcon = (
);
-const key_icon = (
+const keyIcon = (
);
-const fav_icon = (
+const favIcon = (
@@ -26,8 +26,8 @@ const fav_icon = (
const itemsWithTruncatedText = [
{
label: "Item with a very long label that should be truncated",
- slot: ,
- icon: key_icon,
+ slot: ,
+ icon: keyIcon,
},
{
label: "Item 2",
@@ -39,7 +39,7 @@ const itemsWithTruncatedText = [
/>
),
- icon: fav_icon,
+ icon: favIcon,
},
];
@@ -74,17 +74,15 @@ const items = [
{
label: "Sales performance",
},
- {
- label: "Key metrics"
+ {
+ label: "Key metrics",
},
],
},
],
},
{
- items: [
- { label: "Support", icon: "support_agent" },
- ],
+ items: [{ label: "Support", icon: "support_agent" }],
},
];
diff --git a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx
index 6f7d9f2610..b7f09bbc61 100644
--- a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx
+++ b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx
@@ -1,12 +1,12 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcBadge from "../badge/Badge";
import DxcContainer from "../container/Container";
import DxcContextualMenu from "./ContextualMenu";
import SingleItem from "./SingleItem";
-import { userEvent, within } from "@storybook/test";
import ContextualMenuContext from "./ContextualMenuContext";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Contextual Menu",
diff --git a/packages/lib/src/contextual-menu/ContextualMenu.test.tsx b/packages/lib/src/contextual-menu/ContextualMenu.test.tsx
index 2270d2ea32..59af5b6fd8 100644
--- a/packages/lib/src/contextual-menu/ContextualMenu.test.tsx
+++ b/packages/lib/src/contextual-menu/ContextualMenu.test.tsx
@@ -35,7 +35,9 @@ describe("Contextual menu component tests", () => {
const { getAllByRole, getByRole } = render();
expect(getAllByRole("menuitem").length).toBe(4);
const actions = getAllByRole("button");
- actions[0] != null && userEvent.click(actions[0]);
+ if (actions[0] != null) {
+ userEvent.click(actions[0]);
+ }
expect(actions[0]?.getAttribute("aria-pressed")).toBeTruthy();
expect(getByRole("menu")).toBeTruthy();
});
@@ -66,16 +68,24 @@ describe("Contextual menu component tests", () => {
test("Group — Renders with correct aria attributes", () => {
const { getAllByRole } = render();
const group1 = getAllByRole("button")[0];
- group1 != null && userEvent.click(group1);
+ if (group1 != null) {
+ userEvent.click(group1);
+ }
expect(group1?.getAttribute("aria-expanded")).toBeTruthy();
expect(group1?.getAttribute("aria-controls")).toBe(group1?.nextElementSibling?.id);
const expandedGroupItem1 = getAllByRole("button")[2];
- expandedGroupItem1 != null && userEvent.click(expandedGroupItem1);
+ if (expandedGroupItem1 != null) {
+ userEvent.click(expandedGroupItem1);
+ }
const expandedGroupedItem2 = getAllByRole("button")[6];
- expandedGroupedItem2 != null && userEvent.click(expandedGroupedItem2);
+ if (expandedGroupedItem2 != null) {
+ userEvent.click(expandedGroupedItem2);
+ }
expect(getAllByRole("menuitem").length).toBe(10);
const optionToBeClicked = getAllByRole("button")[4];
- optionToBeClicked != null && userEvent.click(optionToBeClicked);
+ if (optionToBeClicked != null) {
+ userEvent.click(optionToBeClicked);
+ }
expect(optionToBeClicked?.getAttribute("aria-pressed")).toBeTruthy();
});
test("Group — A grouped item, selected by default, must be visible (expanded group) in the first render of the component", () => {
@@ -92,17 +102,27 @@ describe("Contextual menu component tests", () => {
test("Group — Collapsed groups render as selected when containing a selected item", () => {
const { getAllByRole } = render();
const group1 = getAllByRole("button")[0];
- group1 != null && userEvent.click(group1);
+ if (group1 != null) {
+ userEvent.click(group1);
+ }
const group2 = getAllByRole("button")[2];
- group2 != null && userEvent.click(group2);
+ if (group2 != null) {
+ userEvent.click(group2);
+ }
const item = getAllByRole("button")[3];
- item != null && userEvent.click(item);
+ if (item != null) {
+ userEvent.click(item);
+ }
expect(item?.getAttribute("aria-pressed")).toBeTruthy();
expect(group1?.getAttribute("aria-pressed")).toBe("false");
expect(group2?.getAttribute("aria-pressed")).toBe("false");
- group2 != null && userEvent.click(group2);
+ if (group2 != null) {
+ userEvent.click(group2);
+ }
expect(group2?.getAttribute("aria-pressed")).toBe("true");
- group1 != null && userEvent.click(group1);
+ if (group1 != null) {
+ userEvent.click(group1);
+ }
expect(group1?.getAttribute("aria-pressed")).toBe("true");
});
test("Sections — Renders with correct aria attributes", () => {
@@ -110,7 +130,9 @@ describe("Contextual menu component tests", () => {
expect(getAllByRole("region").length).toBe(2);
expect(getAllByRole("menuitem").length).toBe(6);
const actions = getAllByRole("button");
- actions[0] != null && userEvent.click(actions[0]);
+ if (actions[0] != null) {
+ userEvent.click(actions[0]);
+ }
expect(actions[0]?.getAttribute("aria-pressed")).toBeTruthy();
expect(getAllByRole("menu").length).toBe(2);
expect(getAllByRole("region")[0]?.getAttribute("aria-labelledby")).toBe(getByText("Section title").id);
diff --git a/packages/lib/src/contextual-menu/ContextualMenu.tsx b/packages/lib/src/contextual-menu/ContextualMenu.tsx
index ae95747ce9..13f58b4172 100644
--- a/packages/lib/src/contextual-menu/ContextualMenu.tsx
+++ b/packages/lib/src/contextual-menu/ContextualMenu.tsx
@@ -4,7 +4,7 @@ import MenuItem from "./MenuItem";
import ContextualMenuPropsType, { GroupItemWithId, ItemWithId, SectionWithId } from "./types";
import Section from "./Section";
import ContextualMenuContext from "./ContextualMenuContext";
-import { scrollbarStyles } from "../styles/scroll";
+import scrollbarStyles from "../styles/scroll";
import { addIdToItems, isSection } from "./utils";
import SubMenu from "./SubMenu";
@@ -34,10 +34,12 @@ export default function DxcContextualMenu({ items }: ContextualMenuPropsType) {
useLayoutEffect(() => {
if (selectedItemId !== -1 && firstUpdate) {
const contextualMenuEl = contextualMenuRef.current;
- const selectedItemEl = contextualMenuEl?.querySelector("[aria-pressed='true']") as HTMLButtonElement;
- contextualMenuEl?.scrollTo?.({
- top: (selectedItemEl?.offsetTop ?? 0) - (contextualMenuEl?.clientHeight ?? 0) / 2,
- });
+ const selectedItemEl = contextualMenuEl?.querySelector("[aria-pressed='true']");
+ if (selectedItemEl instanceof HTMLButtonElement) {
+ contextualMenuEl?.scrollTo?.({
+ top: (selectedItemEl?.offsetTop ?? 0) - (contextualMenuEl?.clientHeight ?? 0) / 2,
+ });
+ }
setFirstUpdate(false);
}
}, [firstUpdate, selectedItemId]);
diff --git a/packages/lib/src/contextual-menu/GroupItem.tsx b/packages/lib/src/contextual-menu/GroupItem.tsx
index ebf2c79f9b..ba794fd617 100644
--- a/packages/lib/src/contextual-menu/GroupItem.tsx
+++ b/packages/lib/src/contextual-menu/GroupItem.tsx
@@ -1,4 +1,4 @@
-import { useContext, useMemo, useState, memo, useId } from "react";
+import { useContext, useMemo, useState, useId } from "react";
import DxcIcon from "../icon/Icon";
import SubMenu from "./SubMenu";
import ItemAction from "./ItemAction";
@@ -7,7 +7,7 @@ import { GroupItemProps } from "./types";
import ContextualMenuContext from "./ContextualMenuContext";
import { isGroupSelected } from "./utils";
-export default function GroupItem({ items, ...props }: GroupItemProps) {
+const GroupItem = ({ items, ...props }: GroupItemProps) => {
const groupMenuId = `group-menu-${useId()}`;
const { selectedItemId } = useContext(ContextualMenuContext) ?? {};
const groupSelected = useMemo(() => isGroupSelected(items, selectedItemId), [items, selectedItemId]);
@@ -20,9 +20,7 @@ export default function GroupItem({ items, ...props }: GroupItemProps) {
aria-expanded={isOpen ? true : undefined}
aria-pressed={groupSelected && !isOpen}
collapseIcon={isOpen ? : }
- onClick={() => {
- setIsOpen((isCurrentlyOpen) => !isCurrentlyOpen);
- }}
+ onClick={() => setIsOpen((isCurrentlyOpen) => !isCurrentlyOpen)}
selected={groupSelected && !isOpen}
{...props}
/>
@@ -36,3 +34,5 @@ export default function GroupItem({ items, ...props }: GroupItemProps) {
>
);
};
+
+export default GroupItem;
diff --git a/packages/lib/src/contextual-menu/ItemAction.tsx b/packages/lib/src/contextual-menu/ItemAction.tsx
index c8c1294926..7476819964 100644
--- a/packages/lib/src/contextual-menu/ItemAction.tsx
+++ b/packages/lib/src/contextual-menu/ItemAction.tsx
@@ -63,7 +63,7 @@ const Text = styled.span<{ selected: ItemActionProps["selected"] }>`
overflow: hidden;
`;
-export default memo(function ItemAction({ badge, collapseIcon, depthLevel, icon, label, ...props }: ItemActionProps) {
+const ItemAction = memo(({ badge, collapseIcon, depthLevel, icon, label, ...props }: ItemActionProps) => {
const [hasTooltip, setHasTooltip] = useState(false);
const modifiedBadge = badge && cloneElement(badge, { size: "small" });
@@ -88,3 +88,7 @@ export default memo(function ItemAction({ badge, collapseIcon, depthLevel, icon,
);
});
+
+ItemAction.displayName = "ItemAction";
+
+export default ItemAction;
diff --git a/packages/lib/src/contextual-menu/Section.tsx b/packages/lib/src/contextual-menu/Section.tsx
index ae8afdabfc..8cade2fba0 100644
--- a/packages/lib/src/contextual-menu/Section.tsx
+++ b/packages/lib/src/contextual-menu/Section.tsx
@@ -1,10 +1,10 @@
+import { useId } from "react";
import styled from "@emotion/styled";
import { DxcInset } from "..";
import DxcDivider from "../divider/Divider";
import SubMenu from "./SubMenu";
import MenuItem from "./MenuItem";
import { SectionProps } from "./types";
-import { useId } from "react";
const SectionContainer = styled.section`
display: grid;
@@ -27,8 +27,8 @@ export default function Section({ index, length, section }: SectionProps) {
{section.title && {section.title}}
- {section.items.map((item, index) => (
-
+ {section.items.map((item, i) => (
+
))}
{index !== length - 1 && (
diff --git a/packages/lib/src/contextual-menu/SingleItem.tsx b/packages/lib/src/contextual-menu/SingleItem.tsx
index df86ea61da..5fcd304d91 100644
--- a/packages/lib/src/contextual-menu/SingleItem.tsx
+++ b/packages/lib/src/contextual-menu/SingleItem.tsx
@@ -21,7 +21,9 @@ export default function SingleItem({ id, onSelect, selectedByDefault = false, ..
);
diff --git a/packages/lib/src/contextual-menu/types.ts b/packages/lib/src/contextual-menu/types.ts
index c6107f8ae6..e9599a7f89 100644
--- a/packages/lib/src/contextual-menu/types.ts
+++ b/packages/lib/src/contextual-menu/types.ts
@@ -53,7 +53,6 @@ type ContextualMenuContextProps = {
setSelectedItemId: Dispatch>;
};
-export default Props;
export type {
ContextualMenuContextProps,
GroupItem,
@@ -69,3 +68,5 @@ export type {
SectionProps,
SingleItemProps,
};
+
+export default Props;
diff --git a/packages/lib/src/contextual-menu/utils.ts b/packages/lib/src/contextual-menu/utils.ts
index a77c213b0b..3dfe2fb6d8 100644
--- a/packages/lib/src/contextual-menu/utils.ts
+++ b/packages/lib/src/contextual-menu/utils.ts
@@ -10,21 +10,23 @@ import ContextualMenuPropsType, {
export const isGroupItem = (item: Item | GroupItem): item is GroupItem => "items" in item;
-export const isSection = (item: SectionType | Item | GroupItem): item is SectionType => "items" in item && !("label" in item);
+export const isSection = (item: SectionType | Item | GroupItem): item is SectionType =>
+ "items" in item && !("label" in item);
-export const addIdToItems = (items: ContextualMenuPropsType["items"]): (ItemWithId | GroupItemWithId | SectionWithId)[] => {
+export const addIdToItems = (
+ items: ContextualMenuPropsType["items"]
+): (ItemWithId | GroupItemWithId | SectionWithId)[] => {
let accId = 0;
const innerAddIdToItems = (
items: ContextualMenuPropsType["items"]
- ): (ItemWithId | GroupItemWithId | SectionWithId)[] => {
- return items.map((item: Item | GroupItem | SectionType) =>
+ ): (ItemWithId | GroupItemWithId | SectionWithId)[] =>
+ items.map((item: Item | GroupItem | SectionType) =>
isSection(item)
? ({ ...item, items: innerAddIdToItems(item.items) } as SectionWithId)
: isGroupItem(item)
? ({ ...item, items: innerAddIdToItems(item.items) } as GroupItemWithId)
: { ...item, id: accId++ }
);
- };
return innerAddIdToItems(items);
};
@@ -32,5 +34,5 @@ export const isGroupSelected = (items: GroupItemProps["items"], selectedItemId?:
items.some((item) => {
if ("items" in item) return isGroupSelected(item.items, selectedItemId);
else if (selectedItemId !== -1) return item.id === selectedItemId;
- else return (item as ItemWithId).selectedByDefault;
- });
\ No newline at end of file
+ else return item.selectedByDefault;
+ });
diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx
index 7f63a8fb41..263dc3a98c 100644
--- a/packages/lib/src/data-grid/DataGrid.stories.tsx
+++ b/packages/lib/src/data-grid/DataGrid.stories.tsx
@@ -1,15 +1,15 @@
+import { isValidElement, useState } from "react";
+import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcDataGrid from "./DataGrid";
import DxcContainer from "../container/Container";
-import { GridColumn, GridRow, HierarchyGridRow } from "./types";
-import { isValidElement, useState } from "react";
-import { disabledRules } from "../../test/accessibility/rules/specific/data-grid/disabledRules";
+import disabledRules from "../../test/accessibility/rules/specific/data-grid/disabledRules";
+import { GridColumn, HierarchyGridRow } from "./types";
import preview from "../../.storybook/preview";
-import { userEvent, within } from "@storybook/test";
import DxcBadge from "../badge/Badge";
import { ActionsCellPropsType } from "../table/types";
-import { Meta, StoryObj } from "@storybook/react";
import { isKeyOfRow } from "./utils";
export default {
@@ -20,7 +20,7 @@ export default {
config: {
rules: [
...disabledRules.map((ruleId) => ({ id: ruleId, reviewOnFail: true })),
- ...preview?.parameters?.a11y?.config?.rules,
+ ...(preview?.parameters?.a11y?.config?.rules || []),
],
},
},
@@ -450,15 +450,15 @@ const childrenTrigger = (open: boolean, triggerRow: HierarchyGridRow) => {
setTimeout(() => {
resolve([
{
- name: `${triggerRow.name} Child 1`,
- value: triggerRow.value,
- id: `${triggerRow.id}-child-1`,
+ name: `${triggerRow.name as string} Child 1`,
+ value: triggerRow.value as string,
+ id: `${triggerRow.id as string}-child-1`,
childrenTrigger,
},
{
- name: `${triggerRow.name} Child 2`,
- value: triggerRow.value,
- id: `${triggerRow.id}-child-2`,
+ name: `${triggerRow.name as string} Child 2`,
+ value: triggerRow.value as string,
+ id: `${triggerRow.id as string}-child-2`,
childrenTrigger,
},
] as unknown as HierarchyGridRow[]);
@@ -643,7 +643,7 @@ const customSortColumns: GridColumn[] = [
summaryKey: "total",
sortable: true,
sortFn: (a, b) => {
- if (isValidElement(a) && isValidElement(b)) {
+ if (isValidElement<{ label: string }>(a) && isValidElement<{ label: string }>(b)) {
return a.props.label < b.props.label ? -1 : a.props.label > b.props.label ? 1 : 0;
}
return 0;
@@ -657,27 +657,27 @@ const customSortRows = [
task: "Task 1",
complete: 46,
priority: "High",
- component: ,
+ component: ,
},
{
id: 2,
task: "Task 2",
complete: 51,
priority: "High",
- component: ,
+ component: ,
},
{
id: 3,
task: "Task 3",
complete: 40,
priority: "High",
- component: ,
+ component: ,
},
{
id: 4,
task: "Task 4",
complete: 10,
- component: ,
+ component: ,
priority: "High",
},
{
@@ -685,21 +685,21 @@ const customSortRows = [
task: "Task 5",
complete: 68,
priority: "High",
- component: ,
+ component: ,
},
{
id: 6,
task: "Task 6",
complete: 37,
priority: "High",
- component: ,
+ component: ,
},
{
id: 7,
task: "Task 7",
complete: 73,
priority: "Medium",
- component: ,
+ component: ,
},
];
@@ -816,8 +816,8 @@ const DataGridControlled = () => {
if (sortColumn) {
const { columnKey, direction } = sortColumn;
console.log(`Sorting the column '${columnKey}' by '${direction}' direction`);
- setRowsControlled((currentRows) => {
- return currentRows.sort((a, b) => {
+ setRowsControlled((currentRows) =>
+ currentRows.sort((a, b) => {
if (isKeyOfRow(columnKey, a) && isKeyOfRow(columnKey, b)) {
const valueA = a[columnKey];
const valueB = b[columnKey];
@@ -833,8 +833,8 @@ const DataGridControlled = () => {
} else {
return 0;
}
- });
- });
+ })
+ );
} else {
console.log("Removed sorting criteria");
setRowsControlled(expandableRows.slice(page * itemsPerPage, page * itemsPerPage + itemsPerPage));
@@ -860,16 +860,12 @@ const DataGridControlled = () => {
);
};
-const DataGridSort = () => {
- return (
- <>
-
-
-
-
- >
- );
-};
+const DataGridSort = () => (
+
+
+
+
+);
const DataGridPaginator = () => {
const [selectedRows, setSelectedRows] = useState((): Set => new Set());
@@ -1083,27 +1079,41 @@ export const DataGridSortedWithChildren: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const checkbox0 = canvas.getAllByRole("checkbox")[0];
- checkbox0 && (await userEvent.click(checkbox0));
+ if (checkbox0) {
+ await userEvent.click(checkbox0);
+ }
await userEvent.click(canvas.getByText("Root Node 1"));
await userEvent.click(canvas.getByText("Root Node 2"));
await userEvent.click(canvas.getByText("Child Node 1.1"));
await userEvent.click(canvas.getByText("Child Node 2.1"));
let columnheader1 = canvas.getAllByRole("columnheader")[1];
- columnheader1 && (await userEvent.click(columnheader1));
+ if (columnheader1) {
+ await userEvent.click(columnheader1);
+ }
columnheader1 = canvas.getAllByRole("columnheader")[1];
- columnheader1 && (await userEvent.click(columnheader1));
+ if (columnheader1) {
+ await userEvent.click(columnheader1);
+ }
const checkbox5 = canvas.getAllByRole("checkbox")[5];
- checkbox5 && (await userEvent.click(checkbox5));
+ if (checkbox5) {
+ await userEvent.click(checkbox5);
+ }
const checkbox13 = canvas.getAllByRole("checkbox")[13];
- checkbox13 && (await userEvent.click(checkbox13));
+ if (checkbox13) {
+ await userEvent.click(checkbox13);
+ }
await userEvent.click(canvas.getByText("Paginated Node 1"));
await userEvent.click(canvas.getByText("Paginated Node 2"));
await userEvent.click(canvas.getByText("Paginated Node 1.1"));
await userEvent.click(canvas.getByText("Paginated Node 2.1"));
const columnheader4 = canvas.getAllByRole("columnheader")[4];
- columnheader4 && (await userEvent.click(columnheader4));
+ if (columnheader4) {
+ await userEvent.click(columnheader4);
+ }
const checkbox18 = canvas.getAllByRole("checkbox")[18];
- checkbox18 && (await userEvent.click(checkbox18));
+ if (checkbox18) {
+ await userEvent.click(checkbox18);
+ }
},
};
@@ -1112,25 +1122,45 @@ export const DataGridSortedExpanded: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const button0 = canvas.getAllByRole("button")[0];
- button0 && (await userEvent.click(button0));
+ if (button0) {
+ await userEvent.click(button0);
+ }
const button1 = canvas.getAllByRole("button")[1];
- button1 && (await userEvent.click(button1));
+ if (button1) {
+ await userEvent.click(button1);
+ }
const columnHeaders4 = canvas.getAllByRole("columnheader")[4];
- columnHeaders4 && (await userEvent.click(columnHeaders4));
+ if (columnHeaders4) {
+ await userEvent.click(columnHeaders4);
+ }
const button9 = canvas.getAllByRole("button")[9];
- button9 && (await userEvent.click(button9));
+ if (button9) {
+ await userEvent.click(button9);
+ }
const button10 = canvas.getAllByRole("button")[10];
- button10 && (await userEvent.click(button10));
+ if (button10) {
+ await userEvent.click(button10);
+ }
const columnHeaders10 = canvas.getAllByRole("columnheader")[10];
- columnHeaders10 && (await userEvent.click(columnHeaders10));
+ if (columnHeaders10) {
+ await userEvent.click(columnHeaders10);
+ }
const button16 = canvas.getAllByRole("button")[16];
- button16 && (await userEvent.click(button16));
+ if (button16) {
+ await userEvent.click(button16);
+ }
const button43 = canvas.getAllByRole("button")[43];
- button43 && (await userEvent.click(button43));
+ if (button43) {
+ await userEvent.click(button43);
+ }
const button36 = canvas.getAllByRole("button")[36];
- button36 && (await userEvent.click(button36));
+ if (button36) {
+ await userEvent.click(button36);
+ }
const button37 = canvas.getAllByRole("button")[37];
- button37 && (await userEvent.click(button37));
+ if (button37) {
+ await userEvent.click(button37);
+ }
},
};
@@ -1139,6 +1169,6 @@ export const UnknownUniqueId: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const editorCell = canvas.getAllByText("Task 1")[0];
- editorCell && (await userEvent.dblClick(editorCell));
+ if (editorCell) await userEvent.dblClick(editorCell);
},
};
diff --git a/packages/lib/src/data-grid/DataGrid.test.tsx b/packages/lib/src/data-grid/DataGrid.test.tsx
index fd871fdbdc..d1856090b7 100644
--- a/packages/lib/src/data-grid/DataGrid.test.tsx
+++ b/packages/lib/src/data-grid/DataGrid.test.tsx
@@ -243,14 +243,15 @@ const hierarchyRowsLazy: HierarchyGridRow[] = [
describe("Data grid component tests", () => {
beforeAll(() => {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
(global as any).CSS = {
- escape: (str: string): string => str,
+ escape: (str: string) => str,
};
window.HTMLElement.prototype.scrollIntoView = jest.fn;
});
- test("Renders with correct content", async () => {
- const { getByText, getAllByRole } = await render();
+ test("Renders with correct content", () => {
+ const { getByText, getAllByRole } = render();
expect(getByText("46")).toBeTruthy();
const rows = getAllByRole("row");
expect(rows.length).toBe(5);
@@ -273,7 +274,7 @@ describe("Data grid component tests", () => {
expect(rows.length).toBe(5);
});
- test("Triggers childrenTrigger when expanding hierarchy row", async () => {
+ test("Triggers childrenTrigger when expanding hierarchy row", () => {
const onSelectRows = jest.fn();
const selectedRows = new Set();
@@ -292,7 +293,9 @@ describe("Data grid component tests", () => {
const buttons = getAllByRole("button");
- buttons[0] && fireEvent.click(buttons[0]);
+ if (buttons[0]) {
+ fireEvent.click(buttons[0]);
+ }
expect(childrenTriggerMock).toHaveBeenCalledWith(true, expect.objectContaining({ id: "lazy-a" }));
});
@@ -303,14 +306,18 @@ describe("Data grid component tests", () => {
expect(getByText("% Complete")).toBeTruthy();
});
- test("Expands and collapses a row to show custom content", async () => {
+ test("Expands and collapses a row to show custom content", () => {
const { getAllByRole, getByText, queryByText } = render(
);
const buttons = getAllByRole("button");
- buttons[0] && fireEvent.click(buttons[0]);
+ if (buttons[0]) {
+ fireEvent.click(buttons[0]);
+ }
expect(getByText("Custom content 1")).toBeTruthy();
- buttons[0] && fireEvent.click(buttons[0]);
+ if (buttons[0]) {
+ fireEvent.click(buttons[0]);
+ }
expect(queryByText("Custom content 1")).not.toBeTruthy();
});
@@ -321,13 +328,17 @@ describe("Data grid component tests", () => {
const headers = getAllByRole("columnheader");
const sortableHeader = headers[1];
- sortableHeader && fireEvent.click(sortableHeader);
+ if (sortableHeader) {
+ fireEvent.click(sortableHeader);
+ }
expect(sortableHeader?.getAttribute("aria-sort")).toBe("ascending");
await waitFor(() => {
const cells = getAllByRole("gridcell");
expect(cells[1]?.textContent).toBe("1");
});
- sortableHeader && fireEvent.click(sortableHeader);
+ if (sortableHeader) {
+ fireEvent.click(sortableHeader);
+ }
expect(sortableHeader?.getAttribute("aria-sort")).toBe("descending");
await waitFor(() => {
const cells = getAllByRole("gridcell");
@@ -341,8 +352,12 @@ describe("Data grid component tests", () => {
);
const buttons = getAllByRole("button");
- buttons[0] && fireEvent.click(buttons[0]);
- buttons[1] && fireEvent.click(buttons[1]);
+ if (buttons[0]) {
+ fireEvent.click(buttons[0]);
+ }
+ if (buttons[1]) {
+ fireEvent.click(buttons[1]);
+ }
expect(getByText("Custom content 1")).toBeTruthy();
expect(getByText("Custom content 2")).toBeTruthy();
diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx
index ace21c1f10..82f7d0ba07 100644
--- a/packages/lib/src/data-grid/DataGrid.tsx
+++ b/packages/lib/src/data-grid/DataGrid.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useMemo, useState, ReactNode } from "react";
+import { useEffect, useMemo, useState } from "react";
import DataGrid, { SortColumn } from "react-data-grid";
import styled from "@emotion/styled";
import DataGridPropsType, { HierarchyGridRow, GridRow, ExpandableGridRow } from "./types";
@@ -22,8 +22,7 @@ import {
} from "./utils";
import DxcPaginator from "../paginator/Paginator";
import { DxcActionsCell } from "../table/Table";
-import { scrollbarStyles } from "../styles/scroll";
-
+import scrollbarStyles from "../styles/scroll";
const DataGridContainer = styled.div<{
paginatorRendered: boolean;
}>`
@@ -218,7 +217,7 @@ const DxcDataGrid = ({
renderCell({ row }) {
if (row.isExpandedChildContent) {
// if it is expanded content
- return (row.expandedChildContent as ReactNode) || null;
+ return row.expandedChildContent || null;
}
// if row has expandable content
return (
@@ -262,7 +261,7 @@ const DxcDataGrid = ({
}
return (
- {row[firstColumnKey] as ReactNode}
+ {row[firstColumnKey]}
);
},
@@ -339,7 +338,7 @@ const DxcDataGrid = ({
const reorderedColumns = useMemo(
() =>
- // Array ordered by columnsOrder
+ // Array sorted by columnsOrder
columnsOrder.map((index) => columnsToRender[index]!),
[columnsOrder, columnsToRender]
);
diff --git a/packages/lib/src/data-grid/types.ts b/packages/lib/src/data-grid/types.ts
index 5b85840d3c..1a750971a9 100644
--- a/packages/lib/src/data-grid/types.ts
+++ b/packages/lib/src/data-grid/types.ts
@@ -54,7 +54,7 @@ export type HierarchyGridRow = GridRow & {
*/
childRows?: HierarchyGridRow[] | GridRow[];
/**
- * Function called when a row with children is expanded or collapsed (based on the value of `open`).
+ * Function called when a row with children is expanded or collapsed (based on the value of `open`).
* Returns (or resolves to) the array of child rows nested under this row to display when expanded.
*/
childrenTrigger?: (
diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx
index 55fa64b92b..ef5fcc4761 100644
--- a/packages/lib/src/data-grid/utils.tsx
+++ b/packages/lib/src/data-grid/utils.tsx
@@ -1,7 +1,4 @@
-// TODO: Remove eslint disable
-/* eslint-disable no-param-reassign */
-
-import { ReactNode, SetStateAction, useState } from "react";
+import { ReactNode, SetStateAction } from "react";
import { Column, RenderSortStatusProps, SortColumn, textEditor } from "react-data-grid";
import DxcActionIcon from "../action-icon/ActionIcon";
import DxcCheckbox from "../checkbox/Checkbox";
@@ -122,6 +119,19 @@ export const renderExpandableTrigger = (
/>
);
+/**
+ * Determines if the given row is a `HierarchyGridRow`.
+ *
+ * A `HierarchyGridRow` is identified by having a `childRows` property
+ * that is an array with at least one element.
+ *
+ * @param {GridRow} row - The row to check.
+ * @returns {row is HierarchyGridRow & { childRows: HierarchyGridRow[] | GridRow[] }}
+ * Returns `true` if the row is a `HierarchyGridRow` with `childRows` defined, otherwise `false`.
+ */
+const isHierarchyGridRow = (row: GridRow): row is HierarchyGridRow & { childRows: HierarchyGridRow[] | GridRow[] } =>
+ Array.isArray(row.childRows) && row.childRows.length > 0;
+
/**
* Renders a trigger for hierarchical row expansion in the grid.
* @param {HierarchyGridRow[]} rows - List of all hierarchy grid rows.
@@ -198,47 +208,53 @@ export const renderHierarchyTrigger = (
triggerRow.childRows?.length &&
!rows.some((row) => row.parentKey === rowKeyGetter(triggerRow, uniqueRowId))
) {
- expandChildren();
+ expandChildren().catch((err) => {
+ console.error("Children expansion failed:", err);
+ });
}
const onClick = async () => {
- if (isLoading) return; // Prevent double clicks while loading
- triggerRow.visibleChildren = !triggerRow.visibleChildren;
- if (triggerRow.visibleChildren) {
- await expandChildren();
- } else {
- setRowsToRender((currentRows) => {
- // The children of the row that is being collapsed are added to an array
- const rowsToRemove: HierarchyGridRow[] = rows.filter(
- (rowToRender) => rowToRender.parentKey && rowToRender.parentKey === rowKeyGetter(triggerRow, uniqueRowId)
- );
- // The children are checked if any of them has any other children of their own
- const rowsToCheck = [...rowsToRemove];
- while (rowsToCheck.length > 0) {
- const currentRow = rowsToCheck.pop();
- const childRows = currentRow?.visibleChildren && currentRow?.childRows ? currentRow.childRows : [];
-
- rowsToRemove.push(...childRows);
- rowsToCheck.push(...childRows);
- }
-
- const newRowsToRender = currentRows.filter(
- (row) =>
- !rowsToRemove
- .map((rowToRemove) => {
- if (rowToRemove.visibleChildren) {
- rowToRemove.visibleChildren = false;
- }
- return rowKeyGetter(rowToRemove, uniqueRowId);
- })
- .includes(rowKeyGetter(row, uniqueRowId))
- );
+ try {
+ if (isLoading) return; // Prevent double clicks while loading
+ triggerRow.visibleChildren = !triggerRow.visibleChildren;
+ if (triggerRow.visibleChildren) {
+ await expandChildren();
+ } else {
+ setRowsToRender((currentRows) => {
+ // The children of the row that is being collapsed are added to an array
+ const rowsToRemove: HierarchyGridRow[] = rows.filter(
+ (rowToRender) => rowToRender.parentKey && rowToRender.parentKey === rowKeyGetter(triggerRow, uniqueRowId)
+ );
+ // The children are checked if any of them has any other children of their own
+ const rowsToCheck = [...rowsToRemove];
+ while (rowsToCheck.length > 0) {
+ const currentRow = rowsToCheck.pop();
+ const childRows = currentRow?.visibleChildren && currentRow?.childRows ? currentRow.childRows : [];
+
+ rowsToRemove.push(...childRows);
+ rowsToCheck.push(...childRows);
+ }
- return newRowsToRender;
- });
+ const newRowsToRender = currentRows.filter(
+ (row) =>
+ !rowsToRemove
+ .map((rowToRemove) => {
+ if (rowToRemove.visibleChildren) {
+ rowToRemove.visibleChildren = false;
+ }
+ return rowKeyGetter(rowToRemove, uniqueRowId);
+ })
+ .includes(rowKeyGetter(row, uniqueRowId))
+ );
+
+ return newRowsToRender;
+ });
+ }
+ } catch (err) {
+ console.error("Error toggling row:", err);
}
};
return (
-
} />);
expect(getByText("header-child-text")).toBeTruthy();
});
test("Header renders menu button in mobile", () => {
- Object.defineProperty(HTMLElement.prototype, "offsetWidth", { configurable: true, value: 425 });
+ Object.defineProperty(HTMLElement.prototype, "offsetWidth", {
+ configurable: true,
+ value: 425,
+ });
Object.defineProperty(window, "matchMedia", {
writable: true,
value: jest.fn().mockImplementation(() => ({
diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx
index 5480ebc2ec..6c8c934c01 100644
--- a/packages/lib/src/header/Header.tsx
+++ b/packages/lib/src/header/Header.tsx
@@ -1,14 +1,13 @@
-import { ComponentProps, useEffect, useRef, useState } from "react";
-import styled from "@emotion/styled";
+import { ComponentProps, useContext, useEffect, useRef, useState } from "react";
import { responsiveSizes, spaces } from "../common/variables";
import DxcDropdown from "../dropdown/Dropdown";
import DxcIcon from "../icon/Icon";
import HeaderPropsType, { Logo } from "./types";
import DxcFlex from "../flex/Flex";
-import { useContext } from "react";
import { HalstackLanguageContext } from "../HalstackContext";
import ActionIcon from "../action-icon/ActionIcon";
import { dxcLogo } from "./Icons";
+import styled from "@emotion/styled";
const HeaderDropdown = styled.div`
display: flex;
diff --git a/packages/lib/src/header/types.ts b/packages/lib/src/header/types.ts
index c175455d24..05857fa7f7 100644
--- a/packages/lib/src/header/types.ts
+++ b/packages/lib/src/header/types.ts
@@ -23,7 +23,7 @@ type Props = {
underlined?: boolean;
/**
* Content shown in the header. Take into account that the component applies styles
- * for the first child in the content, so we recommend the use of React.Fragment
+ * for the first child in the content, so we recommend the use of Fragment
* to be applied correctly. Otherwise, the styles can be modified.
*/
content?: ReactNode;
diff --git a/packages/lib/src/heading/utils.ts b/packages/lib/src/heading/utils.ts
index 046152e97b..02771a9e70 100644
--- a/packages/lib/src/heading/utils.ts
+++ b/packages/lib/src/heading/utils.ts
@@ -26,4 +26,4 @@ export const getHeadingWeight = (weight: HeadingPropsType["weight"]) => {
case "light":
return "var(--typography-heading-light)";
}
-};
\ No newline at end of file
+};
diff --git a/packages/lib/src/icon/Icon.stories.tsx b/packages/lib/src/icon/Icon.stories.tsx
index 6c82ef5066..6ef1f7ae92 100644
--- a/packages/lib/src/icon/Icon.stories.tsx
+++ b/packages/lib/src/icon/Icon.stories.tsx
@@ -1,8 +1,8 @@
+import { Meta, StoryObj } from "@storybook/react";
import DxcIcon from "./Icon";
import Title from "../../.storybook/components/Title";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcTypography from "../typography/Typography";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Icon",
diff --git a/packages/lib/src/inset/Inset.stories.tsx b/packages/lib/src/inset/Inset.stories.tsx
index 435975e35d..27dcf83808 100644
--- a/packages/lib/src/inset/Inset.stories.tsx
+++ b/packages/lib/src/inset/Inset.stories.tsx
@@ -1,9 +1,9 @@
+import { ReactNode } from "react";
+import { Meta, StoryObj } from "@storybook/react";
import Title from "../../.storybook/components/Title";
-import DxcFlex from "./../flex/Flex";
+import DxcFlex from "../flex/Flex";
import DxcInset from "./Inset";
-import { Meta, StoryObj } from "@storybook/react";
import DxcContainer from "../container/Container";
-import { ReactNode } from "react";
export default {
title: "Inset",
diff --git a/packages/lib/src/layout/ApplicationLayout.stories.tsx b/packages/lib/src/layout/ApplicationLayout.stories.tsx
index 09d0a621b0..7f6e3415ac 100644
--- a/packages/lib/src/layout/ApplicationLayout.stories.tsx
+++ b/packages/lib/src/layout/ApplicationLayout.stories.tsx
@@ -1,8 +1,8 @@
+import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import { INITIAL_VIEWPORTS } from "@storybook/addon-viewport";
import Title from "../../.storybook/components/Title";
import DxcApplicationLayout from "./ApplicationLayout";
-import { userEvent, within } from "@storybook/test";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Application Layout",
@@ -156,21 +156,19 @@ const ApplicationLayoutCustomFooter = () => (
);
const Tooltip = () => (
- <>
-
-
- SideNav Content
-
-
- }
- >
-
- Main Content
-
-
- >
+
+
+ SideNav Content
+
+
+ }
+ >
+
+ Main Content
+
+
);
type Story = StoryObj;
diff --git a/packages/lib/src/link/Link.stories.tsx b/packages/lib/src/link/Link.stories.tsx
index 6f5cfc0326..98f4d035be 100644
--- a/packages/lib/src/link/Link.stories.tsx
+++ b/packages/lib/src/link/Link.stories.tsx
@@ -1,7 +1,7 @@
+import { Meta, StoryObj } from "@storybook/react";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcLink from "./Link";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Link",
diff --git a/packages/lib/src/link/Link.tsx b/packages/lib/src/link/Link.tsx
index 1075930bee..5bb8a37b9b 100644
--- a/packages/lib/src/link/Link.tsx
+++ b/packages/lib/src/link/Link.tsx
@@ -106,4 +106,6 @@ const DxcLink = forwardRef(
)
);
+DxcLink.displayName = "DxcLink";
+
export default DxcLink;
diff --git a/packages/lib/src/nav-tabs/NavTabs.tsx b/packages/lib/src/nav-tabs/NavTabs.tsx
index 158bd2d143..d0c6b0e47d 100644
--- a/packages/lib/src/nav-tabs/NavTabs.tsx
+++ b/packages/lib/src/nav-tabs/NavTabs.tsx
@@ -1,4 +1,4 @@
-import { Children, KeyboardEvent, ReactElement, useMemo, useState } from "react";
+import { Children, KeyboardEvent, useMemo, useState } from "react";
import styled from "@emotion/styled";
import NavTabsPropsType from "./types";
import Tab from "./Tab";
@@ -22,9 +22,7 @@ const Underline = styled.div`
const DxcNavTabs = ({ iconPosition = "left", tabIndex = 0, children }: NavTabsPropsType): JSX.Element => {
const [innerFocusIndex, setInnerFocusIndex] = useState(null);
- const childArray = Children.toArray(children).filter(
- (child) => typeof child === "object" && "props" in child
- ) as ReactElement[];
+ const childArray = Children.toArray(children).filter((child) => typeof child === "object" && "props" in child);
const contextValue = useMemo(
() => ({
diff --git a/packages/lib/src/nav-tabs/Tab.tsx b/packages/lib/src/nav-tabs/Tab.tsx
index aa67645156..ac262c18ce 100644
--- a/packages/lib/src/nav-tabs/Tab.tsx
+++ b/packages/lib/src/nav-tabs/Tab.tsx
@@ -166,4 +166,6 @@ const Tab = forwardRef(
}
);
+Tab.displayName = "Tab";
+
export default Tab;
diff --git a/packages/lib/src/nav-tabs/types.ts b/packages/lib/src/nav-tabs/types.ts
index abde4211db..83fafddc2b 100644
--- a/packages/lib/src/nav-tabs/types.ts
+++ b/packages/lib/src/nav-tabs/types.ts
@@ -1,4 +1,4 @@
-import { ReactNode, SVGProps } from "react";
+import { ReactNode } from "react";
import { SVG } from "../common/utils";
export type NavTabsContextProps = {
diff --git a/packages/lib/src/nav-tabs/utils.ts b/packages/lib/src/nav-tabs/utils.ts
index 1cc2452f85..74cd3f2f60 100644
--- a/packages/lib/src/nav-tabs/utils.ts
+++ b/packages/lib/src/nav-tabs/utils.ts
@@ -1,12 +1,18 @@
-import { ReactNode, ReactElement } from "react";
-
-export const getPropInChild = (child: ReactNode, propName: string): string | undefined => {
- if (child && typeof child === "object" && "props" in child) {
- const childWithProps = child as ReactElement;
- if (childWithProps.props[propName]) {
- return childWithProps.props[propName];
- } else if (childWithProps.props.children) {
- return getPropInChild(childWithProps.props.children, propName);
+import { ReactNode, ReactElement, isValidElement } from "react";
+
+type ElementWithChildren = ReactElement<{ children?: ReactNode; [key: string]: unknown }>;
+
+export const getPropInChild = (child: ReactNode, propName: string): string | boolean | undefined => {
+ if (isValidElement(child)) {
+ const el = child as ElementWithChildren;
+ const value = el.props[propName];
+
+ if (typeof value === "string" || typeof value === "boolean") {
+ return value;
+ }
+
+ if (el.props.children) {
+ return getPropInChild(el.props.children, propName);
}
}
};
@@ -14,11 +20,16 @@ export const getPropInChild = (child: ReactNode, propName: string): string | und
export const getLabelFromTab = (child: ReactNode): string | undefined => {
if (typeof child === "string") {
return child;
- } else if (child && typeof child === "object" && "props" in child) {
- const childWithProps = child as ReactElement;
- return Array.isArray(childWithProps.props.children)
- ? getLabelFromTab(childWithProps.props.children[0])
- : getLabelFromTab(childWithProps.props.children);
+ }
+ if (isValidElement(child)) {
+ const el = child as ElementWithChildren;
+ const children = el.props.children;
+
+ if (Array.isArray(children) && isValidElement(children[0] as ReactNode)) {
+ return getLabelFromTab(children[0] as ReactNode);
+ }
+
+ return getLabelFromTab(children);
}
};
diff --git a/packages/lib/src/number-input/NumberInput.accessibility.test.tsx b/packages/lib/src/number-input/NumberInput.accessibility.test.tsx
index 71a8823ece..1177ddf2f7 100644
--- a/packages/lib/src/number-input/NumberInput.accessibility.test.tsx
+++ b/packages/lib/src/number-input/NumberInput.accessibility.test.tsx
@@ -1,17 +1,15 @@
import { render } from "@testing-library/react";
import { axe } from "../../test/accessibility/axe-helper";
import DxcNumberInput from "./NumberInput";
+import MockDOMRect from "../../test/mocks/domRectMock";
// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.DOMRect = MockDOMRect;
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Number input component accessibility tests", () => {
it("Should not have basic accessibility issues", async () => {
diff --git a/packages/lib/src/number-input/NumberInput.test.tsx b/packages/lib/src/number-input/NumberInput.test.tsx
index c49170b2b3..2b584cd0c2 100644
--- a/packages/lib/src/number-input/NumberInput.test.tsx
+++ b/packages/lib/src/number-input/NumberInput.test.tsx
@@ -1,17 +1,15 @@
import { fireEvent, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import DxcNumberInput from "./NumberInput";
+import MockDOMRect from "../../test/mocks/domRectMock";
// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.DOMRect = MockDOMRect;
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Number input component tests", () => {
test("Number input renders with label, helper text, placeholder and increment/decrement action buttons", () => {
@@ -29,15 +27,19 @@ describe("Number input component tests", () => {
const number = getByLabelText("Number label") as HTMLInputElement;
expect(number.disabled).toBeTruthy();
});
- test("Number input is read only and cannot be incremented or decremented using the actions", async () => {
+ test("Number input is read only and cannot be incremented or decremented using the actions", () => {
const { getByLabelText, getAllByRole } = render();
const number = getByLabelText("Number label") as HTMLInputElement;
expect(number.readOnly).toBeTruthy();
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("");
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("");
});
test("Number input is read only and cannot be incremented or decremented using the arrow keys", () => {
@@ -64,9 +66,15 @@ describe("Number input component tests", () => {
userEvent.clear(number);
fireEvent.blur(number);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "",
+ error: "This field is required. Please, enter a value.",
+ });
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "",
+ error: "This field is required. Please, enter a value.",
+ });
});
test("Hiding number input controls", () => {
const { queryByRole } = render();
@@ -108,24 +116,28 @@ describe("Number input component tests", () => {
userEvent.type(number, "-1");
fireEvent.blur(number);
});
- test("Cannot decrement the value if it is less than the min value", async () => {
+ test("Cannot decrement the value if it is less than the min value", () => {
const { getByLabelText, getAllByRole } = render();
const number = getByLabelText("Number input label") as HTMLInputElement;
userEvent.type(number, "1");
fireEvent.blur(number);
expect(number.value).toBe("1");
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("1");
});
- test("Increment the value when it is less than the min value", async () => {
+ test("Increment the value when it is less than the min value", () => {
const { getByLabelText, getAllByRole } = render();
const number = getByLabelText("Number input label") as HTMLInputElement;
userEvent.type(number, "1");
fireEvent.blur(number);
expect(number.value).toBe("1");
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("5");
});
test("Error message is shown if the typed value is greater than the max value", () => {
@@ -137,89 +149,123 @@ describe("Number input component tests", () => {
const number = getByLabelText("Number input label");
userEvent.type(number, "12");
expect(onChange).toHaveBeenCalledTimes(2);
- expect(onChange).toHaveBeenCalledWith({ value: "12", error: "Value must be less than or equal to 10." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "12",
+ error: "Value must be less than or equal to 10.",
+ });
fireEvent.blur(number);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "12", error: "Value must be less than or equal to 10." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "12",
+ error: "Value must be less than or equal to 10.",
+ });
});
- test("Cannot increment the value if it is greater than the max value", async () => {
+ test("Cannot increment the value if it is greater than the max value", () => {
const { getByLabelText, getAllByRole } = render();
const number = getByLabelText("Number input label") as HTMLInputElement;
userEvent.type(number, "12");
fireEvent.blur(number);
expect(number.value).toBe("12");
const decrement = getAllByRole("button")[1];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("12");
});
- test("Decrement the value when it is greater than the max value", async () => {
+ test("Decrement the value when it is greater than the max value", () => {
const { getByLabelText, getAllByRole } = render();
const number = getByLabelText("Number input label") as HTMLInputElement;
userEvent.type(number, "120");
fireEvent.blur(number);
expect(number.value).toBe("120");
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("10");
});
- test("Increment and decrement the value with min and max values", async () => {
+ test("Increment and decrement the value with min and max values", () => {
const { getByLabelText, getAllByRole } = render();
const number = getByLabelText("Number input label") as HTMLInputElement;
userEvent.type(number, "1");
fireEvent.blur(number);
expect(number.value).toBe("1");
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("1");
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("5");
- increment && (await userEvent.click(increment));
- increment && (await userEvent.click(increment));
- increment && (await userEvent.click(increment));
- increment && (await userEvent.click(increment));
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ userEvent.click(increment);
+ userEvent.click(increment);
+ userEvent.click(increment);
+ userEvent.click(increment);
+ }
expect(number.value).toBe("10");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("10");
});
- test("Increment and decrement the value with an integer step", async () => {
+ test("Increment and decrement the value with an integer step", () => {
const { getByLabelText, getAllByRole } = render();
const number = getByLabelText("Number input label") as HTMLInputElement;
userEvent.type(number, "10");
fireEvent.blur(number);
expect(number.value).toBe("10");
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("15");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("20");
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("15");
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("10");
});
- test("Increment and decrement the value with a decimal step", async () => {
+ test("Increment and decrement the value with a decimal step", () => {
const { getByLabelText, getAllByRole } = render();
const number = getByLabelText("Number input label") as HTMLInputElement;
userEvent.type(number, "-9");
fireEvent.blur(number);
expect(number.value).toBe("-9");
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("-8.5");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("-8");
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
- decrement && (await userEvent.click(decrement));
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ userEvent.click(decrement);
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("-9.5");
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("-10");
});
- test("Increment and decrement the value with min, max and step", async () => {
+ test("Increment and decrement the value with min, max and step", () => {
const onBlur = jest.fn();
const { getByLabelText, getAllByRole } = render(
@@ -227,106 +273,151 @@ describe("Number input component tests", () => {
const number = getByLabelText("Number input label") as HTMLInputElement;
userEvent.type(number, "1");
fireEvent.blur(number);
- expect(onBlur).toHaveBeenCalledWith({ value: "1", error: "Value must be greater than or equal to 5." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "1",
+ error: "Value must be greater than or equal to 5.",
+ });
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("5");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("13");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("13");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("13");
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("5");
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("5");
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
});
- test("Start incrementing from 0 when the min value is less than 0 and the max value is bigger than 0", async () => {
+ test("Start incrementing from 0 when the min value is less than 0 and the max value is bigger than 0", () => {
const onBlur = jest.fn();
const { getByLabelText, getAllByRole } = render(
);
const number = getByLabelText("Number input label") as HTMLInputElement;
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("1");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("2");
});
- test("Start incrementing from 0 when the min value is less than 0 and the max is 0", async () => {
+ test("Start incrementing from 0 when the min value is less than 0 and the max is 0", () => {
const { getByLabelText, getAllByRole } = render(
);
const number = getByLabelText("Number input label") as HTMLInputElement;
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("0");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("0");
});
- test("Start incrementing from the min value when it is bigger than 0", async () => {
+ test("Start incrementing from the min value when it is bigger than 0", () => {
const { getByLabelText, getAllByRole } = render(
);
const number = getByLabelText("Number input label") as HTMLInputElement;
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("2");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("2.5");
});
- test("Start incrementing from the max value when it is less than 0", async () => {
+ test("Start incrementing from the max value when it is less than 0", () => {
const { getByLabelText, getAllByRole } = render(
);
const number = getByLabelText("Number input label") as HTMLInputElement;
const increment = getAllByRole("button")[1];
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("-1");
- increment && (await userEvent.click(increment));
+ if (increment) {
+ userEvent.click(increment);
+ }
expect(number.value).toBe("-1");
});
- test("Start decrementing from 0 when the min value is less than 0 and the max value is bigger than 0", async () => {
+ test("Start decrementing from 0 when the min value is less than 0 and the max value is bigger than 0", () => {
const { getByLabelText, getAllByRole } = render(
);
const number = getByLabelText("Number input label") as HTMLInputElement;
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("-1");
});
- test("Start decrementing from 0 when the min value is 0 and the max value is bigger than 0", async () => {
+ test("Start decrementing from 0 when the min value is 0 and the max value is bigger than 0", () => {
const { getByLabelText, getAllByRole } = render(
);
const number = getByLabelText("Number input label") as HTMLInputElement;
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("0");
});
- test("Start decrementing from the min value when it is bigger than 0", async () => {
+ test("Start decrementing from the min value when it is bigger than 0", () => {
const { getByLabelText, getAllByRole } = render(
);
const number = getByLabelText("Number input label") as HTMLInputElement;
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("2");
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("2");
});
- test("Start decrementing from the max value when it is less than 0", async () => {
+ test("Start decrementing from the max value when it is less than 0", () => {
const { getByLabelText, getAllByRole } = render(
);
const number = getByLabelText("Number input label") as HTMLInputElement;
const decrement = getAllByRole("button")[0];
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("-1");
- decrement && (await userEvent.click(decrement));
+ if (decrement) {
+ userEvent.click(decrement);
+ }
expect(number.value).toBe("-1.5");
});
test("Increment and decrement the value with min, max and step using the arrows in keyboard", () => {
@@ -443,10 +534,10 @@ describe("Number input component tests", () => {
const increment = getAllByRole("button")[1];
expect(increment?.getAttribute("aria-label")).toBe("Increment value");
});
- test("Number input submits correct values inside a form and actions don't trigger the submit event", async () => {
- const handlerOnSubmit = jest.fn((e) => {
+ test("Number input submits correct values inside a form and actions don't trigger the submit event", () => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({ data: "0" });
});
@@ -459,11 +550,17 @@ describe("Number input component tests", () => {
const less = getAllByRole("button")[0];
const more = getAllByRole("button")[1];
const submit = getByText("Submit");
- more && (await userEvent.click(more));
+ if (more) {
+ userEvent.click(more);
+ }
expect(handlerOnSubmit).not.toHaveBeenCalled();
- less && (await userEvent.click(less));
+ if (less) {
+ userEvent.click(less);
+ }
expect(handlerOnSubmit).not.toHaveBeenCalled();
- submit && (await userEvent.click(submit));
+ if (submit) {
+ userEvent.click(submit);
+ }
expect(handlerOnSubmit).toHaveBeenCalled();
});
});
diff --git a/packages/lib/src/number-input/NumberInput.tsx b/packages/lib/src/number-input/NumberInput.tsx
index 748594276d..372ffdd14c 100644
--- a/packages/lib/src/number-input/NumberInput.tsx
+++ b/packages/lib/src/number-input/NumberInput.tsx
@@ -63,7 +63,7 @@ const DxcNumberInput = forwardRef(
);
useEffect(() => {
- const input = numberInputRef.current?.getElementsByTagName("input")[0] as HTMLInputElement;
+ const input = numberInputRef.current?.getElementsByTagName("input")[0];
const preventDefault = (event: WheelEvent) => {
event.preventDefault();
};
@@ -104,4 +104,6 @@ const DxcNumberInput = forwardRef(
}
);
+DxcNumberInput.displayName = "DxcNumberInput";
+
export default DxcNumberInput;
diff --git a/packages/lib/src/paginator/Paginator.accessibility.test.tsx b/packages/lib/src/paginator/Paginator.accessibility.test.tsx
index aa86a22377..02ee7e41af 100644
--- a/packages/lib/src/paginator/Paginator.accessibility.test.tsx
+++ b/packages/lib/src/paginator/Paginator.accessibility.test.tsx
@@ -2,22 +2,11 @@ import { render } from "@testing-library/react";
import { axe } from "../../test/accessibility/axe-helper";
import DxcPaginator from "./Paginator";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
-
- unobserve() {}
-
- disconnect() {}
-};
-
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Paginator component accessibility tests", () => {
it("Should not have basic accessibility issues", async () => {
diff --git a/packages/lib/src/paginator/Paginator.test.tsx b/packages/lib/src/paginator/Paginator.test.tsx
index 4902eba8b9..9087431b9b 100644
--- a/packages/lib/src/paginator/Paginator.test.tsx
+++ b/packages/lib/src/paginator/Paginator.test.tsx
@@ -2,22 +2,11 @@ import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import DxcPaginator from "./Paginator";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
-
- unobserve() {}
-
- disconnect() {}
-};
-
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Paginator component tests", () => {
test("Paginator renders with default values", () => {
@@ -57,7 +46,7 @@ describe("Paginator component tests", () => {
expect(getByText("Go to page:")).toBeTruthy();
});
- test("Paginator goToPage call correct function", async () => {
+ test("Paginator goToPage call correct function", () => {
const onClick = jest.fn();
window.HTMLElement.prototype.scrollIntoView = () => {};
window.HTMLElement.prototype.scrollTo = () => {};
@@ -65,23 +54,29 @@ describe("Paginator component tests", () => {
);
const goToPageSelect = getAllByRole("combobox")[0];
- goToPageSelect && (await userEvent.click(goToPageSelect));
+ if (goToPageSelect) {
+ userEvent.click(goToPageSelect);
+ }
const goToPageOption = getByText("2");
- goToPageOption && (await userEvent.click(goToPageOption));
+ if (goToPageOption) {
+ userEvent.click(goToPageOption);
+ }
expect(onClick).toHaveBeenCalledWith(2);
});
- test("Call correct goToPageFunction", async () => {
+ test("Call correct goToPageFunction", () => {
const onClick = jest.fn();
const { getAllByRole } = render(
);
const nextButton = getAllByRole("button")[2];
- nextButton && (await userEvent.click(nextButton));
+ if (nextButton) {
+ userEvent.click(nextButton);
+ }
expect(onClick).toHaveBeenCalled();
});
- test("Call correct itemsPerPageFunction", async () => {
+ test("Call correct itemsPerPageFunction", () => {
const onClick = jest.fn();
window.HTMLElement.prototype.scrollIntoView = () => {};
window.HTMLElement.prototype.scrollTo = () => {};
@@ -95,53 +90,65 @@ describe("Paginator component tests", () => {
/>
);
const select = getAllByText("10")[0];
- select && (await userEvent.click(select));
+ if (select) {
+ userEvent.click(select);
+ }
const itemPerPageOption = getByText("15");
- itemPerPageOption && (await userEvent.click(itemPerPageOption));
+ if (itemPerPageOption) {
+ userEvent.click(itemPerPageOption);
+ }
expect(onClick).toHaveBeenCalledWith(15);
});
- test("Next button is disable in last page", async () => {
+ test("Next button is disable in last page", () => {
const onClick = jest.fn();
const { getAllByRole } = render(
);
const nextButton = getAllByRole("button")[2];
expect(nextButton?.hasAttribute("disabled")).toBeTruthy();
- nextButton && (await userEvent.click(nextButton));
+ if (nextButton) {
+ userEvent.click(nextButton);
+ }
expect(onClick).toHaveBeenCalledTimes(0);
});
- test("Last button is disable in last page", async () => {
+ test("Last button is disable in last page", () => {
const onClick = jest.fn();
const { getAllByRole } = render(
);
const lastButton = getAllByRole("button")[3];
expect(lastButton?.hasAttribute("disabled")).toBeTruthy();
- lastButton && (await userEvent.click(lastButton));
+ if (lastButton) {
+ userEvent.click(lastButton);
+ }
expect(onClick).toHaveBeenCalledTimes(0);
});
- test("First button is disable in first page", async () => {
+ test("First button is disable in first page", () => {
const onClick = jest.fn();
const { getAllByRole } = render(
);
const lastButton = getAllByRole("button")[0];
expect(lastButton?.hasAttribute("disabled")).toBeTruthy();
- lastButton && (await userEvent.click(lastButton));
+ if (lastButton) {
+ userEvent.click(lastButton);
+ }
expect(onClick).toHaveBeenCalledTimes(0);
});
- test("Previous button is disable in first page", async () => {
+ test("Previous button is disable in first page", () => {
const onClick = jest.fn();
const { getAllByRole } = render(
);
const lastButton = getAllByRole("button")[1];
expect(lastButton?.hasAttribute("disabled")).toBeTruthy();
- lastButton && (await userEvent.click(lastButton));
+ if (lastButton) {
+ userEvent.click(lastButton);
+ }
expect(onClick).toHaveBeenCalledTimes(0);
});
diff --git a/packages/lib/src/password-input/PasswordInput.accessibility.test.tsx b/packages/lib/src/password-input/PasswordInput.accessibility.test.tsx
index 36491e870f..18962a0f35 100644
--- a/packages/lib/src/password-input/PasswordInput.accessibility.test.tsx
+++ b/packages/lib/src/password-input/PasswordInput.accessibility.test.tsx
@@ -2,16 +2,11 @@ import { render } from "@testing-library/react";
import { axe } from "../../test/accessibility/axe-helper";
import DxcPasswordInput from "./PasswordInput";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Password input component accessibility tests", () => {
it("Should not have basic accessibility issues", async () => {
diff --git a/packages/lib/src/password-input/PasswordInput.stories.tsx b/packages/lib/src/password-input/PasswordInput.stories.tsx
index eebf6602ce..fca39f385c 100644
--- a/packages/lib/src/password-input/PasswordInput.stories.tsx
+++ b/packages/lib/src/password-input/PasswordInput.stories.tsx
@@ -1,9 +1,9 @@
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcFlex from "../flex/Flex";
import DxcPasswordInput from "./PasswordInput";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Password Input",
diff --git a/packages/lib/src/password-input/PasswordInput.test.tsx b/packages/lib/src/password-input/PasswordInput.test.tsx
index 06b82f8583..71ef613e40 100644
--- a/packages/lib/src/password-input/PasswordInput.test.tsx
+++ b/packages/lib/src/password-input/PasswordInput.test.tsx
@@ -2,16 +2,11 @@ import { fireEvent, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import DxcPasswordInput from "./PasswordInput";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Password input component tests", () => {
test("Password input renders with label and helper text", () => {
@@ -44,17 +39,19 @@ describe("Password input component tests", () => {
expect(passwordInput.value).toBe("Pa$$w0rd");
});
- test("Clear password input value", async () => {
+ test("Clear password input value", () => {
const { getAllByRole, getByLabelText } = render();
const passwordInput = getByLabelText("Password input") as HTMLInputElement;
userEvent.type(passwordInput, "Pa$$w0rd");
expect(passwordInput.value).toBe("Pa$$w0rd");
const clearButton = getAllByRole("button")[0];
- clearButton && await userEvent.click(clearButton);
+ if (clearButton) {
+ userEvent.click(clearButton);
+ }
expect(passwordInput.value).toBe("");
});
- test("Non clearable password input has no clear icon", async () => {
+ test("Non clearable password input has no clear icon", () => {
const { getAllByRole, getByLabelText } = render();
const passwordInput = getByLabelText("Password input") as HTMLInputElement;
userEvent.type(passwordInput, "Pa$$w0rd");
@@ -63,24 +60,26 @@ describe("Password input component tests", () => {
expect(buttons.length).toBe(1);
});
- test("Show/hide password input button works correctly", async () => {
+ test("Show/hide password input button works correctly", () => {
const { getAllByRole, getByLabelText } = render();
const passwordInput = getByLabelText("Password input") as HTMLInputElement;
userEvent.type(passwordInput, "Pa$$w0rd");
expect(passwordInput.value).toBe("Pa$$w0rd");
expect(passwordInput.type).toBe("password");
const showButton = getAllByRole("button")[1];
- showButton && await userEvent.click(showButton);
+ if (showButton) {
+ userEvent.click(showButton);
+ }
expect(passwordInput.type).toBe("text");
});
- test("Password input has correct accessibility attributes", async () => {
+ test("Password input has correct accessibility attributes", () => {
const { getByRole, getByLabelText } = render();
const showButton = getByRole("button");
expect(getByLabelText("Password input")).toBeTruthy();
expect(showButton.getAttribute("aria-expanded")).toBe("false");
expect(showButton.getAttribute("aria-label")).toBe("Show password");
- await userEvent.click(showButton);
+ userEvent.click(showButton);
expect(showButton.getAttribute("aria-expanded")).toBe("true");
expect(showButton.getAttribute("aria-label")).toBe("Hide password");
});
diff --git a/packages/lib/src/password-input/PasswordInput.tsx b/packages/lib/src/password-input/PasswordInput.tsx
index f5ee9280d3..cd3a040fea 100644
--- a/packages/lib/src/password-input/PasswordInput.tsx
+++ b/packages/lib/src/password-input/PasswordInput.tsx
@@ -74,7 +74,7 @@ const DxcPasswordInput = forwardRef(
setIsPasswordVisible((isPasswordCurrentlyVisible) => !isPasswordCurrentlyVisible);
},
icon: isPasswordVisible ? "Visibility_Off" : "Visibility",
- title: isPasswordVisible ? passwordInput.inputHidePasswordTitle : passwordInput.inputShowPasswordTitle,
+ title: isPasswordVisible ? passwordInput?.inputHidePasswordTitle : passwordInput?.inputShowPasswordTitle,
}}
error={error}
clearable={clearable}
@@ -95,4 +95,6 @@ const DxcPasswordInput = forwardRef(
}
);
+DxcPasswordInput.displayName = "DxcPasswordInput";
+
export default DxcPasswordInput;
diff --git a/packages/lib/src/quick-nav/QuickNav.stories.tsx b/packages/lib/src/quick-nav/QuickNav.stories.tsx
index da5925c582..6e9b90bcc5 100644
--- a/packages/lib/src/quick-nav/QuickNav.stories.tsx
+++ b/packages/lib/src/quick-nav/QuickNav.stories.tsx
@@ -1,10 +1,10 @@
+import { Meta, StoryObj } from "@storybook/react";
import styled from "@emotion/styled";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcHeading from "../heading/Heading";
import DxcParagraph from "../paragraph/Paragraph";
import DxcQuickNav from "./QuickNav";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Quick Nav",
@@ -126,37 +126,38 @@ const QuickNav = () => (
- Halstack is the DXC Technology's open source design system for insurance products and digital experiences.
- Our system provides all the tools and resources needed to create superior, beautiful but above all,
- functional user experiences. Halstack is the DXC Technology's open source design system for insurance
- products and digital experiences. Our system provides all the tools and resources needed to create
- superior, beautiful but above all, functional user experiences.Halstack is the DXC Technology's open
- source design system for insurance products and digital experiences. Our system provides all the tools and
- resources needed to create superior, beautiful but above all, functional user experiences.Halstack is the
- DXC Technology's open source design system for insurance products and digital experiences. Our system
- provides all the tools and resources needed to create superior, beautiful but above all, functional user
- experiences.Halstack is the DXC Technology's open source design system for insurance products and digital
+ Halstack is the DXC Technology's open source design system for insurance products and digital
experiences. Our system provides all the tools and resources needed to create superior, beautiful but
- above all, functional user experiences.Halstack is the DXC Technology's open source design system for
- insurance products and digital experiences. Our system provides all the tools and resources needed to
- create superior, beautiful but above all, functional user experiences.Halstack is the DXC Technology's
- open source design system for insurance products and digital experiences. Our system provides all the
- tools and resources needed to create superior, beautiful but above all, functional user experiences.
+ above all, functional user experiences. Halstack is the DXC Technology's open source design system
+ for insurance products and digital experiences. Our system provides all the tools and resources needed to
+ create superior, beautiful but above all, functional user experiences.Halstack is the DXC
+ Technology's open source design system for insurance products and digital experiences. Our system
+ provides all the tools and resources needed to create superior, beautiful but above all, functional user
+ experiences.Halstack is the DXC Technology's open source design system for insurance products and
+ digital experiences. Our system provides all the tools and resources needed to create superior, beautiful
+ but above all, functional user experiences.Halstack is the DXC Technology's open source design system
+ for insurance products and digital experiences. Our system provides all the tools and resources needed to
+ create superior, beautiful but above all, functional user experiences.Halstack is the DXC
+ Technology's open source design system for insurance products and digital experiences. Our system
+ provides all the tools and resources needed to create superior, beautiful but above all, functional user
+ experiences.Halstack is the DXC Technology's open source design system for insurance products and
+ digital experiences. Our system provides all the tools and resources needed to create superior, beautiful
+ but above all, functional user experiences.
- Design principles Halstack design principles are the fundamental part of DXC Technology's approach to
- provide guidance for development teams in order to deliver delightful and consistent user experiences to
- our customers: Balance Consistency Visual hierarchy All our components, design tokens, accessibility
+ Design principles Halstack design principles are the fundamental part of DXC Technology's approach
+ to provide guidance for development teams in order to deliver delightful and consistent user experiences
+ to our customers: Balance Consistency Visual hierarchy All our components, design tokens, accessibility
guidelines, responsive design techniques, and layout proposals have been carefully curated by DXC design
and engineering teams with the objective of creating a unique visual language and ecosystem for our
applications. This is the DXC way of creating User Experiences. Open Source Halstack is an open source
design system, this means that we work towards DXC Technology bussines needs, but it is open for anyone
to use and contribute back to. We are charmed to receive external contributions to help us find bugs,
- design new features, or help us improve the project documentation. If you're interested, definitely
+ design new features, or help us improve the project documentation. If you're interested, definitely
check out our contribution guidelines.Design principles Halstack design principles are the fundamental
- part of DXC Technology's approach to provide guidance for development teams in order to deliver
+ part of DXC Technology's approach to provide guidance for development teams in order to deliver
delightful and consistent user experiences to our customers: Balance Consistency Visual hierarchy All
our components, design tokens, accessibility guidelines, responsive design techniques, and layout
proposals have been carefully curated by DXC design and engineering teams with the objective of creating
@@ -164,16 +165,16 @@ const QuickNav = () => (
Experiences. Open Source Halstack is an open source design system, this means that we work towards DXC
Technology bussines needs, but it is open for anyone to use and contribute back to. We are charmed to
receive external contributions to help us find bugs, design new features, or help us improve the project
- documentation. If you're interested, definitely check out our contribution guidelines.Design principles
- Halstack design principles are the fundamental part of DXC Technology's approach to provide guidance for
- development teams in order to deliver delightful and consistent user experiences to our customers:
- Balance Consistency Visual hierarchy All our components, design tokens, accessibility guidelines,
- responsive design techniques, and layout proposals have been carefully curated by DXC design and
- engineering teams with the objective of creating a unique visual language and ecosystem for our
+ documentation. If you're interested, definitely check out our contribution guidelines.Design
+ principles Halstack design principles are the fundamental part of DXC Technology's approach to
+ provide guidance for development teams in order to deliver delightful and consistent user experiences to
+ our customers: Balance Consistency Visual hierarchy All our components, design tokens, accessibility
+ guidelines, responsive design techniques, and layout proposals have been carefully curated by DXC design
+ and engineering teams with the objective of creating a unique visual language and ecosystem for our
applications. This is the DXC way of creating User Experiences. Open Source Halstack is an open source
design system, this means that we work towards DXC Technology bussines needs, but it is open for anyone
to use and contribute back to. We are charmed to receive external contributions to help us find bugs,
- design new features, or help us improve the project documentation. If you're interested, definitely
+ design new features, or help us improve the project documentation. If you're interested, definitely
check out our contribution guidelines.
@@ -183,17 +184,17 @@ const QuickNav = () => (
- Design principles Halstack design principles are the fundamental part of DXC Technology's approach to
- provide guidance for development teams in order to deliver delightful and consistent user experiences to
- our customers: Balance Consistency Visual hierarchy All our components, design tokens, accessibility
+ Design principles Halstack design principles are the fundamental part of DXC Technology's approach
+ to provide guidance for development teams in order to deliver delightful and consistent user experiences
+ to our customers: Balance Consistency Visual hierarchy All our components, design tokens, accessibility
guidelines, responsive design techniques, and layout proposals have been carefully curated by DXC design
and engineering teams with the objective of creating a unique visual language and ecosystem for our
applications. This is the DXC way of creating User Experiences. Open Source Halstack is an open source
design system, this means that we work towards DXC Technology bussines needs, but it is open for anyone
to use and contribute back to. We are charmed to receive external contributions to help us find bugs,
- design new features, or help us improve the project documentation. If you're interested, definitely
+ design new features, or help us improve the project documentation. If you're interested, definitely
check out our contribution guidelines.Design principles Halstack design principles are the fundamental
- part of DXC Technology's approach to provide guidance for development teams in order to deliver
+ part of DXC Technology's approach to provide guidance for development teams in order to deliver
delightful and consistent user experiences to our customers: Balance Consistency Visual hierarchy All
our components, design tokens, accessibility guidelines, responsive design techniques, and layout
proposals have been carefully curated by DXC design and engineering teams with the objective of creating
@@ -201,16 +202,16 @@ const QuickNav = () => (
Experiences. Open Source Halstack is an open source design system, this means that we work towards DXC
Technology bussines needs, but it is open for anyone to use and contribute back to. We are charmed to
receive external contributions to help us find bugs, design new features, or help us improve the project
- documentation. If you're interested, definitely check out our contribution guidelines.Design principles
- Halstack design principles are the fundamental part of DXC Technology's approach to provide guidance for
- development teams in order to deliver delightful and consistent user experiences to our customers:
- Balance Consistency Visual hierarchy All our components, design tokens, accessibility guidelines,
- responsive design techniques, and layout proposals have been carefully curated by DXC design and
- engineering teams with the objective of creating a unique visual language and ecosystem for our
+ documentation. If you're interested, definitely check out our contribution guidelines.Design
+ principles Halstack design principles are the fundamental part of DXC Technology's approach to
+ provide guidance for development teams in order to deliver delightful and consistent user experiences to
+ our customers: Balance Consistency Visual hierarchy All our components, design tokens, accessibility
+ guidelines, responsive design techniques, and layout proposals have been carefully curated by DXC design
+ and engineering teams with the objective of creating a unique visual language and ecosystem for our
applications. This is the DXC way of creating User Experiences. Open Source Halstack is an open source
design system, this means that we work towards DXC Technology bussines needs, but it is open for anyone
to use and contribute back to. We are charmed to receive external contributions to help us find bugs,
- design new features, or help us improve the project documentation. If you're interested, definitely
+ design new features, or help us improve the project documentation. If you're interested, definitely
check out our contribution guidelines.
@@ -229,37 +230,37 @@ const QuickNav = () => (
The color palette is an essential asset as a communication resource of our design system. Halstack color
- palette brings a unified consistency and helps in guiding the user's perception order. Our color palette
- is based in the HSL model . All our color families are calculated using the lightness value of the
- standard DXC palette colors. Color Tokens Halstack uses tokens to manage color. Appart from a
+ palette brings a unified consistency and helps in guiding the user's perception order. Our color
+ palette is based in the HSL model . All our color families are calculated using the lightness value of
+ the standard DXC palette colors. Color Tokens Halstack uses tokens to manage color. Appart from a
multi-purpose greyscale family, purple and blue are the core color families used in our set of
components. Additional families as red, green and yellow help as feedback role-based color palettes and
must not be used outside this context.The color palette is an essential asset as a communication
resource of our design system. Halstack color palette brings a unified consistency and helps in guiding
- the user's perception order. Our color palette is based in the HSL model . All our color families are
- calculated using the lightness value of the standard DXC palette colors. Color Tokens Halstack uses
+ the user's perception order. Our color palette is based in the HSL model . All our color families
+ are calculated using the lightness value of the standard DXC palette colors. Color Tokens Halstack uses
tokens to manage color. Appart from a multi-purpose greyscale family, purple and blue are the core color
families used in our set of components. Additional families as red, green and yellow help as feedback
role-based color palettes and must not be used outside this context.The color palette is an essential
asset as a communication resource of our design system. Halstack color palette brings a unified
- consistency and helps in guiding the user's perception order. Our color palette is based in the HSL
+ consistency and helps in guiding the user's perception order. Our color palette is based in the HSL
model . All our color families are calculated using the lightness value of the standard DXC palette
colors. Color Tokens Halstack uses tokens to manage color. Appart from a multi-purpose greyscale family,
purple and blue are the core color families used in our set of components. Additional families as red,
green and yellow help as feedback role-based color palettes and must not be used outside this
context.The color palette is an essential asset as a communication resource of our design system.
- Halstack color palette brings a unified consistency and helps in guiding the user's perception order.
- Our color palette is based in the HSL model . All our color families are calculated using the lightness
- value of the standard DXC palette colors. Color Tokens Halstack uses tokens to manage color. Appart from
- a multi-purpose greyscale family, purple and blue are the core color families used in our set of
- components. Additional families as red, green and yellow help as feedback role-based color palettes and
- must not be used outside this context.The color palette is an essential asset as a communication
- resource of our design system. Halstack color palette brings a unified consistency and helps in guiding
- the user's perception order. Our color palette is based in the HSL model . All our color families are
- calculated using the lightness value of the standard DXC palette colors. Color Tokens Halstack uses
- tokens to manage color. Appart from a multi-purpose greyscale family, purple and blue are the core color
- families used in our set of components. Additional families as red, green and yellow help as feedback
- role-based color palettes and must not be used outside this context.
+ Halstack color palette brings a unified consistency and helps in guiding the user's perception
+ order. Our color palette is based in the HSL model . All our color families are calculated using the
+ lightness value of the standard DXC palette colors. Color Tokens Halstack uses tokens to manage color.
+ Appart from a multi-purpose greyscale family, purple and blue are the core color families used in our
+ set of components. Additional families as red, green and yellow help as feedback role-based color
+ palettes and must not be used outside this context.The color palette is an essential asset as a
+ communication resource of our design system. Halstack color palette brings a unified consistency and
+ helps in guiding the user's perception order. Our color palette is based in the HSL model . All our
+ color families are calculated using the lightness value of the standard DXC palette colors. Color Tokens
+ Halstack uses tokens to manage color. Appart from a multi-purpose greyscale family, purple and blue are
+ the core color families used in our set of components. Additional families as red, green and yellow help
+ as feedback role-based color palettes and must not be used outside this context.
@@ -287,37 +288,38 @@ const QuickNav = () => (
- Our selected typography helps in structuring our user's experience based on the visual impact that it
- has on the user interface content. It defines what is the first noticeable piece of information or data
- based on the font shape, size, color, or type and it highlights some pieces of text over the rest. Some
- typographic elements used in Halstack Design System include headers, body, taglines, captions, and
+ Our selected typography helps in structuring our user's experience based on the visual impact that
+ it has on the user interface content. It defines what is the first noticeable piece of information or
+ data based on the font shape, size, color, or type and it highlights some pieces of text over the rest.
+ Some typographic elements used in Halstack Design System include headers, body, taglines, captions, and
labels. Make sure you include all the different typographic variants in order to enhance the
- application's content structure, including the Heading component which defines different levels of page
- and section titles.Our selected typography helps in structuring our user's experience based on the
- visual impact that it has on the user interface content. It defines what is the first noticeable piece
- of information or data based on the font shape, size, color, or type and it highlights some pieces of
- text over the rest. Some typographic elements used in Halstack Design System include headers, body,
+ application's content structure, including the Heading component which defines different levels of
+ page and section titles.Our selected typography helps in structuring our user's experience based on
+ the visual impact that it has on the user interface content. It defines what is the first noticeable
+ piece of information or data based on the font shape, size, color, or type and it highlights some pieces
+ of text over the rest. Some typographic elements used in Halstack Design System include headers, body,
taglines, captions, and labels. Make sure you include all the different typographic variants in order to
- enhance the application's content structure, including the Heading component which defines different
- levels of page and section titles.Our selected typography helps in structuring our user's experience
- based on the visual impact that it has on the user interface content. It defines what is the first
- noticeable piece of information or data based on the font shape, size, color, or type and it highlights
- some pieces of text over the rest. Some typographic elements used in Halstack Design System include
- headers, body, taglines, captions, and labels. Make sure you include all the different typographic
- variants in order to enhance the application's content structure, including the Heading component which
- defines different levels of page and section titles.Our selected typography helps in structuring our
- user's experience based on the visual impact that it has on the user interface content. It defines what
- is the first noticeable piece of information or data based on the font shape, size, color, or type and
- it highlights some pieces of text over the rest. Some typographic elements used in Halstack Design
- System include headers, body, taglines, captions, and labels. Make sure you include all the different
- typographic variants in order to enhance the application's content structure, including the Heading
+ enhance the application's content structure, including the Heading component which defines
+ different levels of page and section titles.Our selected typography helps in structuring our user's
+ experience based on the visual impact that it has on the user interface content. It defines what is the
+ first noticeable piece of information or data based on the font shape, size, color, or type and it
+ highlights some pieces of text over the rest. Some typographic elements used in Halstack Design System
+ include headers, body, taglines, captions, and labels. Make sure you include all the different
+ typographic variants in order to enhance the application's content structure, including the Heading
component which defines different levels of page and section titles.Our selected typography helps in
- structuring our user's experience based on the visual impact that it has on the user interface content.
- It defines what is the first noticeable piece of information or data based on the font shape, size,
- color, or type and it highlights some pieces of text over the rest. Some typographic elements used in
- Halstack Design System include headers, body, taglines, captions, and labels. Make sure you include all
- the different typographic variants in order to enhance the application's content structure, including
- the Heading component which defines different levels of page and section titles.
+ structuring our user's experience based on the visual impact that it has on the user interface
+ content. It defines what is the first noticeable piece of information or data based on the font shape,
+ size, color, or type and it highlights some pieces of text over the rest. Some typographic elements used
+ in Halstack Design System include headers, body, taglines, captions, and labels. Make sure you include
+ all the different typographic variants in order to enhance the application's content structure,
+ including the Heading component which defines different levels of page and section titles.Our selected
+ typography helps in structuring our user's experience based on the visual impact that it has on the
+ user interface content. It defines what is the first noticeable piece of information or data based on
+ the font shape, size, color, or type and it highlights some pieces of text over the rest. Some
+ typographic elements used in Halstack Design System include headers, body, taglines, captions, and
+ labels. Make sure you include all the different typographic variants in order to enhance the
+ application's content structure, including the Heading component which defines different levels of
+ page and section titles.
diff --git a/packages/lib/src/radio-group/RadioGroup.stories.tsx b/packages/lib/src/radio-group/RadioGroup.stories.tsx
index ab8a93d340..add1cedeac 100644
--- a/packages/lib/src/radio-group/RadioGroup.stories.tsx
+++ b/packages/lib/src/radio-group/RadioGroup.stories.tsx
@@ -8,7 +8,7 @@ export default {
component: DxcRadioGroup,
} as Meta;
-const single_option = [{ label: "Option A", value: "A" }];
+const singleOption = [{ label: "Option A", value: "A" }];
const options = [
{ label: "Option 1", value: "1" },
@@ -17,44 +17,44 @@ const options = [
{ label: "Option 4", value: "4" },
];
-const single_disabled_options = [{ label: "Option A", value: "A", disabled: true }];
+const singleDisabledOptions = [{ label: "Option A", value: "A", disabled: true }];
const RadioGroup = () => (
<>
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -62,7 +62,7 @@ const RadioGroup = () => (
@@ -72,7 +72,7 @@ const RadioGroup = () => (
(
{
expect(error.getAttribute("aria-live")).toBe("off");
radios.forEach((radio, index) => {
// if no option was previously selected, first option is the focusable one
- if (index === 0) expect(radio.tabIndex).toBe(0);
- else expect(radio.tabIndex).toBe(-1);
+ expect(radio.tabIndex).toBe(index === 0 ? 0 : -1);
expect(radio.getAttribute("aria-checked")).toBe("false");
expect(radio.getAttribute("aria-disabled")).toBe("false");
});
@@ -55,10 +54,10 @@ describe("Radio Group component tests", () => {
expect(radioGroup.getAttribute("aria-orientation")).toBe("horizontal");
});
- test("Sends its value when submitted", async () => {
- const handlerOnSubmit = jest.fn((e) => {
+ test("Sends its value when submitted", () => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({ radiogroup: "5" });
});
@@ -71,9 +70,11 @@ describe("Radio Group component tests", () => {
const radioGroup = getByRole("radiogroup");
const submit = getByText("Submit");
const radio = getAllByRole("radio")[4];
- await userEvent.click(radioGroup);
- radio && (await userEvent.click(radio));
- await userEvent.click(submit);
+ userEvent.click(radioGroup);
+ if (radio) {
+ userEvent.click(radio);
+ }
+ userEvent.click(submit);
});
test("Disabled state renders with correct aria attribute, correct tabIndex values and it is not focusable by keyboard", () => {
@@ -86,9 +87,24 @@ describe("Radio Group component tests", () => {
radios.forEach((radio) => {
expect(radio.tabIndex).toBe(-1);
});
- fireEvent.keyDown(radioGroup, { key: " ", code: "Space", keyCode: 13, charCode: 13 });
- fireEvent.keyDown(radioGroup, { key: "ArrowLeft", code: "ArrowLeft", keyCode: 37, charCode: 37 });
- fireEvent.keyDown(radioGroup, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(radioGroup, {
+ key: " ",
+ code: "Space",
+ keyCode: 13,
+ charCode: 13,
+ });
+ fireEvent.keyDown(radioGroup, {
+ key: "ArrowLeft",
+ code: "ArrowLeft",
+ keyCode: 37,
+ charCode: 37,
+ });
+ fireEvent.keyDown(radioGroup, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
radios.forEach((radio) => {
expect(radio.tabIndex).toBe(-1);
});
@@ -111,10 +127,10 @@ describe("Radio Group component tests", () => {
expect(radios[2]?.tabIndex).toBe(-1);
});
- test("Disabled radio group doesn't send its value when submitted", async () => {
- const handlerOnSubmit = jest.fn((e) => {
+ test("Disabled radio group doesn't send its value when submitted", () => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({});
});
@@ -125,7 +141,7 @@ describe("Radio Group component tests", () => {
);
const submit = getByText("Submit");
- await userEvent.click(submit);
+ userEvent.click(submit);
});
test("Error state renders with correct aria attributes", () => {
@@ -139,7 +155,7 @@ describe("Radio Group component tests", () => {
expect(errorMessage.getAttribute("aria-live")).toBe("assertive");
});
- test("Radio group with required constraint and 'undefined' as value, sends an error", async () => {
+ test("Radio group with required constraint and 'undefined' as value, sends an error", () => {
const onChange = jest.fn();
const onBlur = jest.fn();
const { getByRole, getAllByRole } = render(
@@ -149,15 +165,19 @@ describe("Radio Group component tests", () => {
const radio = getAllByRole("radio")[0];
expect(radioGroup.getAttribute("aria-required")).toBe("true");
fireEvent.blur(radioGroup);
- expect(onBlur).toHaveBeenCalledWith({ error: "This field is required. Please, choose an option." });
- await userEvent.click(radioGroup);
- radio && (await userEvent.click(radio));
+ expect(onBlur).toHaveBeenCalledWith({
+ error: "This field is required. Please, choose an option.",
+ });
+ userEvent.click(radioGroup);
+ if (radio) {
+ userEvent.click(radio);
+ }
expect(onChange).toHaveBeenCalledWith("1");
fireEvent.blur(radioGroup);
expect(onBlur).toHaveBeenCalledWith({ value: "1" });
});
- test("Radio group with required constraint and empty string as value, sends an error", async () => {
+ test("Radio group with required constraint and empty string as value, sends an error", () => {
const onChange = jest.fn();
const onBlur = jest.fn();
const { getByRole, getAllByRole } = render(
@@ -168,7 +188,9 @@ describe("Radio Group component tests", () => {
expect(radioGroup.getAttribute("aria-required")).toBe("true");
fireEvent.blur(radioGroup);
expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, choose an option." });
- radio && (await userEvent.click(radio));
+ if (radio) {
+ userEvent.click(radio);
+ }
expect(onChange).toHaveBeenCalledWith("1");
});
@@ -191,7 +213,7 @@ describe("Radio Group component tests", () => {
expect(submitInput?.value).toBe("2");
});
- test("Optional radio group conditions: onBlur event doesn't send an error when no radio was checked, has correct aria attributes, custom label and its value is the empty string", async () => {
+ test("Optional radio group conditions: onBlur event doesn't send an error when no radio was checked, has correct aria attributes, custom label and its value is the empty string", () => {
const onChange = jest.fn();
const onBlur = jest.fn();
const { getByRole, getByText, container } = render(
@@ -213,12 +235,12 @@ describe("Radio Group component tests", () => {
expect(radioGroup.getAttribute("aria-invalid")).toBe("false");
const optionalLabel = getByText("No selection");
const submitInput = container.querySelector(`input[name="test"]`);
- await userEvent.click(optionalLabel);
+ userEvent.click(optionalLabel);
expect(onChange).toHaveBeenCalledWith("");
expect(submitInput?.value).toBe("");
});
- test("Controlled radio group", async () => {
+ test("Controlled radio group", () => {
const onChange = jest.fn();
const onBlur = jest.fn();
const { getByRole, getAllByRole, container } = render(
@@ -238,13 +260,15 @@ describe("Radio Group component tests", () => {
expect(submitInput?.value).toBe("2");
expect(radios[1]?.tabIndex).toBe(0);
expect(radios[1]?.getAttribute("aria-checked")).toBe("true");
- radios[6] && (await userEvent.click(radios[6]));
+ if (radios[6]) {
+ userEvent.click(radios[6]);
+ }
expect(onChange).toHaveBeenCalledWith("7");
fireEvent.blur(radioGroup);
expect(onBlur).toHaveBeenCalledWith({ value: "2" });
});
- test("Select an option by clicking on its label", async () => {
+ test("Select an option by clicking on its label", () => {
const onChange = jest.fn();
const { getByText, getAllByRole, container } = render(
{
const checkedRadio = getAllByRole("radio")[8];
const submitInput = container.querySelector(`input[name="test"]`);
expect(checkedRadio?.tabIndex).toBe(-1);
- await userEvent.click(radioLabel);
+ userEvent.click(radioLabel);
expect(onChange).toHaveBeenCalledWith("9");
expect(checkedRadio?.getAttribute("aria-checked")).toBe("true");
expect(checkedRadio?.tabIndex).toBe(0);
@@ -267,7 +291,7 @@ describe("Radio Group component tests", () => {
expect(submitInput?.value).toBe("9");
});
- test("Select an option by clicking on its radio input", async () => {
+ test("Select an option by clicking on its radio input", () => {
const onChange = jest.fn();
const { getAllByRole, container } = render(
{
const checkedRadio = getAllByRole("radio")[6];
const submitInput = container.querySelector(`input[name="test"]`);
expect(checkedRadio?.tabIndex).toBe(-1);
- checkedRadio && (await userEvent.click(checkedRadio));
+ if (checkedRadio) {
+ userEvent.click(checkedRadio);
+ }
expect(onChange).toHaveBeenCalledWith("7");
expect(checkedRadio?.getAttribute("aria-checked")).toBe("true");
expect(checkedRadio?.tabIndex).toBe(0);
@@ -289,7 +315,7 @@ describe("Radio Group component tests", () => {
expect(submitInput?.value).toBe("7");
});
- test("Select an option that is already checked does not call onChange event but gives the focus", async () => {
+ test("Select an option that is already checked does not call onChange event but gives the focus", () => {
const onChange = jest.fn();
const { getAllByRole } = render(
{
const checkedRadio = getAllByRole("radio")[1];
expect(checkedRadio?.tabIndex).toBe(0);
expect(checkedRadio?.getAttribute("aria-checked")).toBe("true");
- checkedRadio && (await userEvent.click(checkedRadio));
+ if (checkedRadio) {
+ userEvent.click(checkedRadio);
+ }
expect(onChange).not.toHaveBeenCalled();
expect(document.activeElement).toEqual(checkedRadio);
});
@@ -323,7 +351,12 @@ describe("Radio Group component tests", () => {
const radioGroup = getByRole("radiogroup");
const checkedRadio = getAllByRole("radio")[0];
const submitInput = container.querySelector(`input[name="test"]`);
- fireEvent.keyDown(radioGroup, { key: " ", code: "Space", keyCode: 32, charCode: 32 });
+ fireEvent.keyDown(radioGroup, {
+ key: " ",
+ code: "Space",
+ keyCode: 32,
+ charCode: 32,
+ });
expect(onChange).toHaveBeenCalledWith("1");
expect(checkedRadio?.getAttribute("aria-checked")).toBe("true");
expect(checkedRadio?.tabIndex).toBe(0);
@@ -353,7 +386,12 @@ describe("Radio Group component tests", () => {
expect(checkedRadio?.tabIndex).toBe(0);
expect(checkedRadio?.getAttribute("aria-checked")).toBe("false");
expect(document.activeElement).toEqual(checkedRadio);
- fireEvent.keyDown(radioGroup, { key: "ArrowRight", code: "ArrowRight", keyCode: 39, charCode: 39 });
+ fireEvent.keyDown(radioGroup, {
+ key: "ArrowRight",
+ code: "ArrowRight",
+ keyCode: 39,
+ charCode: 39,
+ });
expect(onBlur).not.toHaveBeenCalled();
expect(onChange).toHaveBeenCalledTimes(1);
expect(radios[1]?.getAttribute("aria-checked")).toBe("true");
@@ -379,7 +417,12 @@ describe("Radio Group component tests", () => {
const radioGroup = getByRole("radiogroup");
const radios = getAllByRole("radio");
const submitInput = container.querySelector(`input[name="test"]`);
- fireEvent.keyDown(radioGroup, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(radioGroup, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(onBlur).not.toHaveBeenCalled();
expect(onChange).toHaveBeenCalledTimes(1);
expect(radios[8]?.getAttribute("aria-checked")).toBe("true");
@@ -412,7 +455,12 @@ describe("Radio Group component tests", () => {
const radioGroup = getByRole("radiogroup");
const radios = getAllByRole("radio");
const submitInput = container.querySelector(`input[name="test"]`);
- fireEvent.keyDown(radioGroup, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(radioGroup, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(onBlur).not.toHaveBeenCalled();
expect(onChange).toHaveBeenCalledTimes(1);
expect(radios[0]?.getAttribute("aria-checked")).toBe("true");
@@ -428,7 +476,7 @@ describe("Radio Group component tests", () => {
expect(submitInput?.value).toBe("9");
});
- test("Keyboard focus movement continues from the last radio input clicked", async () => {
+ test("Keyboard focus movement continues from the last radio input clicked", () => {
const onChange = jest.fn();
const { getByRole, getAllByRole, container } = render(
{
const radioGroup = getByRole("radiogroup");
const radios = getAllByRole("radio");
const submitInput = container.querySelector(`input[name="test"]`);
- radios[3] && (await userEvent.click(radios[3]));
+ if (radios[3]) {
+ userEvent.click(radios[3]);
+ }
fireEvent.keyDown(radioGroup, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
expect(onChange).toHaveBeenCalledWith("5");
expect(radios[4]?.getAttribute("aria-checked")).toBe("true");
expect(document.activeElement).toEqual(radios[4]);
expect(radios[4]?.tabIndex).toBe(0);
expect(submitInput?.value).toBe("5");
- radios[8] && (await userEvent.click(radios[8]));
+ if (radios[8]) {
+ userEvent.click(radios[8]);
+ }
fireEvent.keyDown(radioGroup, { key: "ArrowLeft", code: "ArrowLeft", keyCode: 37, charCode: 37 });
expect(onChange).toHaveBeenCalledWith("8");
expect(radios[7]?.getAttribute("aria-checked")).toBe("true");
@@ -458,7 +510,7 @@ describe("Radio Group component tests", () => {
expect(submitInput?.value).toBe("8");
});
- test("Read-only radio group lets the user move the focus, but neither click nor keyboard press changes the value", async () => {
+ test("Read-only radio group lets the user move the focus, but neither click nor keyboard press changes the value", () => {
const onChange = jest.fn();
const { getByRole, getAllByRole, container } = render(
{
const radioGroup = getByRole("radiogroup");
const radios = getAllByRole("radio");
const submitInput = container.querySelector(`input[name="test"]`);
- radios[5] && (await userEvent.click(radios[5]));
+ if (radios[5]) {
+ userEvent.click(radios[5]);
+ }
expect(onChange).not.toHaveBeenCalled();
expect(radios[5]?.getAttribute("aria-checked")).toBe("false");
expect(document.activeElement).toEqual(radios[5]);
@@ -487,10 +541,10 @@ describe("Radio Group component tests", () => {
expect(submitInput?.value).toBe("");
});
- test("Read-only radio group sends its value on submit", async () => {
- const handlerOnSubmit = jest.fn((e) => {
+ test("Read-only radio group sends its value on submit", () => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({ radiogroup: "data" });
});
@@ -501,6 +555,6 @@ describe("Radio Group component tests", () => {
);
const submit = getByText("Submit");
- await userEvent.click(submit);
+ userEvent.click(submit);
});
});
diff --git a/packages/lib/src/radio-group/RadioGroup.tsx b/packages/lib/src/radio-group/RadioGroup.tsx
index 7d83f25cb1..357a276ca6 100644
--- a/packages/lib/src/radio-group/RadioGroup.tsx
+++ b/packages/lib/src/radio-group/RadioGroup.tsx
@@ -81,7 +81,7 @@ const DxcRadioGroup = forwardRef(
const handleOnBlur = (event: FocusEvent) => {
// If the radio group loses the focus to an element not contained inside it...
- if (!event.currentTarget.contains(event.relatedTarget as Node)) {
+ if (!event.currentTarget.contains(event.relatedTarget)) {
setFirstTimeFocus(true);
const currentValue = value ?? innerValue;
onBlur?.({
@@ -196,4 +196,6 @@ const DxcRadioGroup = forwardRef(
}
);
+DxcRadioGroup.displayName = "DxcRadioGroup";
+
export default DxcRadioGroup;
diff --git a/packages/lib/src/radio-group/RadioInput.tsx b/packages/lib/src/radio-group/RadioInput.tsx
index 876df95466..c48be3d49d 100644
--- a/packages/lib/src/radio-group/RadioInput.tsx
+++ b/packages/lib/src/radio-group/RadioInput.tsx
@@ -56,12 +56,12 @@ const RadioInput = ({ checked, disabled, error, focused, label, onClick, readOnl
setFirstUpdate(false);
return;
}
- focused && ref.current?.focus();
+ if (focused) ref.current?.focus();
}, [focused]);
const handleOnClick = () => {
onClick();
- document.activeElement !== ref.current && ref.current?.focus();
+ if (document.activeElement !== ref.current) ref.current?.focus();
};
return (
diff --git a/packages/lib/src/resultset-table/ResultsetTable.accessibility.test.tsx b/packages/lib/src/resultset-table/ResultsetTable.accessibility.test.tsx
index 521062051a..8083335520 100644
--- a/packages/lib/src/resultset-table/ResultsetTable.accessibility.test.tsx
+++ b/packages/lib/src/resultset-table/ResultsetTable.accessibility.test.tsx
@@ -3,7 +3,7 @@ import { axe, formatRules } from "../../test/accessibility/axe-helper";
import DxcResultsetTable from "./ResultsetTable";
// TODO: REMOVE
-import { disabledRules as rules } from "../../test/accessibility/rules/specific/resultset-table/disabledRules";
+import rules from "../../test/accessibility/rules/specific/resultset-table/disabledRules";
const disabledRules = {
rules: formatRules(rules),
@@ -16,16 +16,11 @@ const deleteIcon = (
);
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const actions = [
{
diff --git a/packages/lib/src/resultset-table/ResultsetTable.stories.tsx b/packages/lib/src/resultset-table/ResultsetTable.stories.tsx
index 5b661e2591..e7e00d1c41 100644
--- a/packages/lib/src/resultset-table/ResultsetTable.stories.tsx
+++ b/packages/lib/src/resultset-table/ResultsetTable.stories.tsx
@@ -1,11 +1,11 @@
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
import styled from "@emotion/styled";
-import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
+import ExampleContainer from "../../.storybook/components/ExampleContainer";
+import disabledRules from "../../test/accessibility/rules/specific/resultset-table/disabledRules";
import preview from "../../.storybook/preview";
-import { disabledRules } from "../../test/accessibility/rules/specific/resultset-table/disabledRules";
import DxcResultsetTable from "./ResultsetTable";
-import { Meta, StoryObj } from "@storybook/react";
import DxcFlex from "../flex/Flex";
export default {
@@ -15,8 +15,11 @@ export default {
a11y: {
config: {
rules: [
- ...disabledRules.map((ruleId) => ({ id: ruleId, reviewOnFail: true })),
- ...preview?.parameters?.a11y?.config?.rules,
+ ...disabledRules.map((ruleId) => ({
+ id: ruleId,
+ reviewOnFail: true,
+ })),
+ ...(preview?.parameters?.a11y?.config?.rules || []),
],
},
},
@@ -632,31 +635,31 @@ const ResultsetTable = () => (
-
+
-
+
-
+
-
+
-
+
-
+
-
+
>
);
@@ -722,8 +725,8 @@ export const AscendentSorting: Story = {
const canvas = within(canvasElement);
const idHeader = canvas.getAllByRole("button")[0];
const idHeader2 = canvas.getAllByRole("button")[2];
- idHeader && (await userEvent.click(idHeader));
- idHeader2 && (await userEvent.click(idHeader2));
+ if (idHeader) await userEvent.click(idHeader);
+ if (idHeader2) await userEvent.click(idHeader2);
},
};
@@ -733,10 +736,10 @@ export const DescendantSorting: Story = {
const canvas = within(canvasElement);
const nameHeader = canvas.getAllByRole("button")[1];
const nameHeader2 = canvas.getAllByRole("button")[3];
- nameHeader && (await userEvent.click(nameHeader));
- nameHeader && (await userEvent.click(nameHeader));
- nameHeader2 && (await userEvent.click(nameHeader2));
- nameHeader2 && (await userEvent.click(nameHeader2));
+ if (nameHeader) await userEvent.click(nameHeader);
+ if (nameHeader) await userEvent.click(nameHeader);
+ if (nameHeader2) await userEvent.click(nameHeader2);
+ if (nameHeader2) await userEvent.click(nameHeader2);
},
};
@@ -745,7 +748,9 @@ export const MiddlePage: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const nextButton = canvas.getAllByRole("button")[2];
- nextButton && (await userEvent.click(nextButton));
+ if (nextButton) {
+ await userEvent.click(nextButton);
+ }
},
};
@@ -754,7 +759,9 @@ export const LastPage: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const nextButton = canvas.getAllByRole("button")[3];
- nextButton && (await userEvent.click(nextButton));
+ if (nextButton) {
+ await userEvent.click(nextButton);
+ }
},
};
@@ -763,7 +770,9 @@ export const DropdownAction: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const dropdown = canvas.getAllByRole("button")[5];
- dropdown && userEvent.click(dropdown);
+ if (dropdown) {
+ await userEvent.click(dropdown);
+ }
},
};
diff --git a/packages/lib/src/resultset-table/ResultsetTable.test.tsx b/packages/lib/src/resultset-table/ResultsetTable.test.tsx
index 92cb387853..8a63e1ee9e 100644
--- a/packages/lib/src/resultset-table/ResultsetTable.test.tsx
+++ b/packages/lib/src/resultset-table/ResultsetTable.test.tsx
@@ -3,16 +3,11 @@ import userEvent from "@testing-library/user-event";
import DxcCheckbox from "../checkbox/Checkbox";
import DxcResultsetTable from "./ResultsetTable";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const icon = (
@@ -656,7 +650,7 @@ const MultipleSearchable = () => (
const TooltipValue = () => (
-
+
);
@@ -688,7 +682,7 @@ const TooltipOption = () => (
const TooltipClear = () => (
-
+
);
@@ -700,7 +694,7 @@ const SelectAll = () => (
enableSelectAll
label="Select an option"
multiple
- options={group_options}
+ options={groupOptions}
placeholder="Select an available option"
searchable
/>
@@ -714,7 +708,9 @@ export const Chromatic: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const combobox = canvas.getAllByRole("combobox")[24];
- combobox && (await userEvent.click(combobox));
+ if (combobox) {
+ await userEvent.click(combobox);
+ }
},
};
@@ -757,7 +753,7 @@ export const MultipleSearchableWithValue: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const combobox = canvas.getAllByRole("combobox")[0];
- combobox && (await userEvent.click(combobox));
+ if (combobox) await userEvent.click(combobox);
},
};
@@ -775,7 +771,7 @@ export const MultipleOptionsDisplayed: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const combobox = canvas.getAllByRole("combobox")[0];
- combobox && (await userEvent.click(combobox));
+ if (combobox) await userEvent.click(combobox);
},
};
diff --git a/packages/lib/src/select/Select.test.tsx b/packages/lib/src/select/Select.test.tsx
index e07a320e51..6278b8696e 100644
--- a/packages/lib/src/select/Select.test.tsx
+++ b/packages/lib/src/select/Select.test.tsx
@@ -1,17 +1,15 @@
import { act, fireEvent, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import DxcSelect from "./Select";
+import MockDOMRect from "../../test/mocks/domRectMock";
// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.DOMRect = MockDOMRect;
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const reducedSingleOptions = [
{ label: "Option 01", value: "1" },
@@ -170,7 +168,9 @@ describe("Select component tests", () => {
userEvent.click(select);
const options = getAllByRole("option");
expect(options[3]?.getAttribute("aria-selected")).toBe("true");
- options[7] && userEvent.click(options[7]);
+ if (options[7]) {
+ userEvent.click(options[7]);
+ }
expect(getByText("Option 08")).toBeTruthy();
expect(submitInput?.value).toBe("8");
});
@@ -191,14 +191,16 @@ describe("Select component tests", () => {
expect(submitInput?.value).toBe("4,2,6");
userEvent.click(select);
const options = getAllByRole("option");
- options[2] && userEvent.click(options[2]);
+ if (options[2]) {
+ userEvent.click(options[2]);
+ }
expect(getByText("Option 02, Option 03, Option 04, Option 06")).toBeTruthy();
expect(submitInput?.value).toBe("4,2,6,3");
});
test("Sends its value when submitted", () => {
- const handlerOnSubmit = jest.fn((e) => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({ options: "1,5,3" });
});
@@ -218,7 +220,9 @@ describe("Select component tests", () => {
const submit = getByText("Submit");
userEvent.click(select);
const options = getAllByRole("option");
- options[2] && userEvent.click(options[2]);
+ if (options[2]) {
+ userEvent.click(options[2]);
+ }
userEvent.click(submit);
});
test("Searching for a value with an empty list of options passed doesn't open the listbox", () => {
@@ -229,7 +233,9 @@ describe("Select component tests", () => {
const searchInput = container.querySelectorAll("input")[1];
userEvent.click(select);
act(() => {
- searchInput && userEvent.type(searchInput, "test");
+ if (searchInput) {
+ userEvent.type(searchInput, "test");
+ }
});
expect(queryByRole("listbox")).toBeFalsy();
expect(select.getAttribute("aria-expanded")).toBe("false");
@@ -271,9 +277,9 @@ describe("Select component tests", () => {
expect(document.activeElement === select).toBeFalsy();
});
test("Disabled select — Doesn't send its value when submitted", () => {
- const handlerOnSubmit = jest.fn((e) => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({});
});
@@ -297,10 +303,15 @@ describe("Select component tests", () => {
fireEvent.focus(select);
fireEvent.blur(select);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "",
+ error: "This field is required. Please, enter a value.",
+ });
userEvent.click(select);
const options = getAllByRole("option");
- options[0] && userEvent.click(options[0]);
+ if (options[0]) {
+ userEvent.click(options[0]);
+ }
expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({ value: "1" });
fireEvent.blur(select);
@@ -318,11 +329,18 @@ describe("Select component tests", () => {
fireEvent.focus(select);
fireEvent.blur(select);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: [], error: "This field is required. Please, enter a value." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: [],
+ error: "This field is required. Please, enter a value.",
+ });
userEvent.click(select);
let options = getAllByRole("option");
- options[0] && userEvent.click(options[0]);
- options[1] && userEvent.click(options[1]);
+ if (options[0]) {
+ userEvent.click(options[0]);
+ }
+ if (options[1]) {
+ userEvent.click(options[1]);
+ }
expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({ value: ["1", "2"] });
fireEvent.blur(select);
@@ -330,13 +348,23 @@ describe("Select component tests", () => {
expect(onBlur).toHaveBeenCalledWith({ value: ["1", "2"] });
userEvent.click(select);
options = getAllByRole("option");
- options[0] && userEvent.click(options[0]);
- options[1] && userEvent.click(options[1]);
+ if (options[0]) {
+ userEvent.click(options[0]);
+ }
+ if (options[1]) {
+ userEvent.click(options[1]);
+ }
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: [], error: "This field is required. Please, enter a value." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: [],
+ error: "This field is required. Please, enter a value.",
+ });
fireEvent.blur(select);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: [], error: "This field is required. Please, enter a value." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: [],
+ error: "This field is required. Please, enter a value.",
+ });
});
test("Controlled — Optional constraint", () => {
const onChange = jest.fn();
@@ -385,7 +413,9 @@ describe("Select component tests", () => {
const submitInput = container.querySelector(`input[name="test"]`);
userEvent.click(select);
let options = getAllByRole("option");
- options[2] && userEvent.click(options[2]);
+ if (options[2]) {
+ userEvent.click(options[2]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: "3" });
expect(queryByRole("listbox")).toBeFalsy();
expect(getByText("Option 03")).toBeTruthy();
@@ -410,15 +440,32 @@ describe("Select component tests", () => {
expect(getAllByText("Choose an option").length).toBe(2);
const options = getAllByRole("option");
expect(options[0]?.getAttribute("aria-selected")).toBe("true");
- options[0] && userEvent.click(options[0]);
+ if (options[0]) {
+ userEvent.click(options[0]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: "" });
expect(getAllByText("Choose an option").length).toBe(1);
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-0");
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(onChange).toHaveBeenCalledWith({ value: "" });
expect(getAllByText("Choose an option").length).toBe(1);
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-0");
});
test("Non-Grouped Options — Filtering options never affects the optional item until there are no coincidences", () => {
@@ -433,12 +480,16 @@ describe("Select component tests", () => {
);
const searchInput = container.querySelectorAll("input")[1];
act(() => {
- searchInput && userEvent.type(searchInput, "1");
+ if (searchInput) {
+ userEvent.type(searchInput, "1");
+ }
});
expect(getByText("Placeholder example")).toBeTruthy();
expect(getAllByRole("option").length).toBe(12);
act(() => {
- searchInput && userEvent.type(searchInput, "123");
+ if (searchInput) {
+ userEvent.type(searchInput, "123");
+ }
});
expect(queryByText("Placeholder example")).toBeFalsy();
expect(getByText("No matches found")).toBeTruthy();
@@ -446,30 +497,60 @@ describe("Select component tests", () => {
test("Non-Grouped Options: Arrow up key — Opens the listbox and visually focus the last option", () => {
const { getByRole, queryByRole } = render();
const select = getByRole("combobox");
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(queryByRole("listbox")).toBeTruthy();
expect(select.getAttribute("aria-activedescendant")).toBe("option-19");
});
test("Non-Grouped Options: Arrow up key — Puts the focus in last option when the first one is visually focused", () => {
const { getByRole, queryByRole } = render();
const select = getByRole("combobox");
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(queryByRole("listbox")).toBeTruthy();
expect(select.getAttribute("aria-activedescendant")).toBe("option-19");
});
test("Non-Grouped Options: Arrow down key — Opens the listbox and visually focus the first option", () => {
const { getByRole, queryByRole } = render();
const select = getByRole("combobox");
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(queryByRole("listbox")).toBeTruthy();
expect(select.getAttribute("aria-activedescendant")).toBe("option-0");
});
test("Non-Grouped Options: Arrow down key — Puts the focus in the first option when the last one is visually focused", () => {
const { getByRole, queryByRole } = render();
const select = getByRole("combobox");
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(queryByRole("listbox")).toBeTruthy();
expect(select.getAttribute("aria-activedescendant")).toBe("option-0");
});
@@ -479,11 +560,36 @@ describe("Select component tests", () => {
);
const select = getByRole("combobox");
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(onChange).toHaveBeenCalledWith({ value: "20" });
expect(queryByRole("listbox")).toBeFalsy();
expect(getByText("Option 20")).toBeTruthy();
@@ -500,7 +606,9 @@ describe("Select component tests", () => {
const searchInput = container.querySelectorAll("input")[1];
userEvent.click(select);
expect(getByRole("listbox")).toBeTruthy();
- searchInput && userEvent.type(searchInput, "08");
+ if (searchInput) {
+ userEvent.type(searchInput, "08");
+ }
userEvent.click(getByRole("option"));
expect(onChange).toHaveBeenCalledWith({ value: "8" });
expect(queryByRole("listbox")).toBeFalsy();
@@ -519,7 +627,9 @@ describe("Select component tests", () => {
const searchInput = container.querySelectorAll("input")[1];
userEvent.click(select);
expect(getByRole("listbox")).toBeTruthy();
- searchInput && userEvent.type(searchInput, "abc");
+ if (searchInput) {
+ userEvent.type(searchInput, "abc");
+ }
expect(getByText("No matches found")).toBeTruthy();
});
test("Non-Grouped Options: Searchable — Clicking the select, when the list is open, clears the search value", () => {
@@ -530,7 +640,9 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
const searchInput = container.querySelectorAll("input")[1];
act(() => {
- searchInput && userEvent.type(searchInput, "2");
+ if (searchInput) {
+ userEvent.type(searchInput, "2");
+ }
});
expect(getByRole("listbox")).toBeTruthy();
expect(getByText("Option 02")).toBeTruthy();
@@ -552,7 +664,9 @@ describe("Select component tests", () => {
userEvent.click(select);
userEvent.click(select);
expect(queryByRole("listbox")).toBeFalsy();
- searchInput && userEvent.type(searchInput, "2");
+ if (searchInput) {
+ userEvent.type(searchInput, "2");
+ }
expect(getByRole("listbox")).toBeTruthy();
});
test("Non-Grouped Options: Searchable — Key Esc cleans the search value and closes the options", () => {
@@ -562,7 +676,9 @@ describe("Select component tests", () => {
);
const select = getByRole("combobox");
const searchInput = container.querySelectorAll("input")[1];
- searchInput && userEvent.type(searchInput, "Option 02");
+ if (searchInput) {
+ userEvent.type(searchInput, "Option 02");
+ }
fireEvent.keyDown(select, { key: "Esc", code: "Esc", keyCode: 27, charCode: 27 });
expect(searchInput?.value).toBe("");
expect(queryByRole("listbox")).toBeFalsy();
@@ -573,7 +689,9 @@ describe("Select component tests", () => {
);
const searchInput = container.querySelectorAll("input")[1];
- searchInput && userEvent.type(searchInput, "Option 02");
+ if (searchInput) {
+ userEvent.type(searchInput, "Option 02");
+ }
expect(getAllByRole("option").length).toBe(1);
const clearSearchButton = getByRole("button");
expect(clearSearchButton.getAttribute("aria-label")).toBe("Clear search");
@@ -592,13 +710,30 @@ describe("Select component tests", () => {
userEvent.click(select);
expect(getByRole("listbox").getAttribute("aria-multiselectable")).toBe("true");
const options = getAllByRole("option");
- options[10] && userEvent.click(options[10]);
+ if (options[10]) {
+ userEvent.click(options[10]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: ["11"] });
expect(queryByRole("listbox")).toBeTruthy();
expect(getAllByText("Option 11").length).toBe(2);
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(onChange).toHaveBeenCalledWith({ value: ["11", "19"] });
expect(queryByRole("listbox")).toBeTruthy();
expect(getByText("Option 11, Option 19")).toBeTruthy();
@@ -612,9 +747,15 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const options = getAllByRole("option");
- options[5] && userEvent.click(options[5]);
- options[8] && userEvent.click(options[8]);
- options[13] && userEvent.click(options[13]);
+ if (options[5]) {
+ userEvent.click(options[5]);
+ }
+ if (options[8]) {
+ userEvent.click(options[8]);
+ }
+ if (options[13]) {
+ userEvent.click(options[13]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: ["6", "9", "14"] });
expect(queryByRole("listbox")).toBeTruthy();
expect(getByText("Option 06, Option 09, Option 14")).toBeTruthy();
@@ -645,7 +786,9 @@ describe("Select component tests", () => {
userEvent.click(select);
expect(getAllByText("Choose an option").length).toBe(1);
const options = getAllByRole("option");
- options[0] && userEvent.click(options[0]);
+ if (options[0]) {
+ userEvent.click(options[0]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: ["1"] });
expect(getAllByText("Option 01").length).toBe(2);
});
@@ -656,18 +799,55 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const options = getAllByRole("option");
- options[4] && userEvent.click(options[4]);
+ if (options[4]) {
+ userEvent.click(options[4]);
+ }
expect(getByText("Option 05")).toBeTruthy();
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-4");
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(getByText("Option 04")).toBeTruthy();
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-3");
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(getByText("Option 06")).toBeTruthy();
});
test("Non-Grouped Options — If an options was previously selected when its opened (by click and key press), the visual focus appears always in the selected option", () => {
@@ -677,21 +857,53 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const options = getAllByRole("option");
- options[15] && userEvent.click(options[15]);
+ if (options[15]) {
+ userEvent.click(options[15]);
+ }
expect(queryByRole("listbox")).toBeFalsy();
expect(getByText("Option 16")).toBeTruthy();
userEvent.click(select);
expect(select.getAttribute("aria-activedescendant")).toBeNull();
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-15");
userEvent.click(select);
expect(queryByRole("listbox")).toBeFalsy();
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-15");
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(getByText("Option 17")).toBeTruthy();
});
test("Grouped Options — Opens listbox and renders it correctly or closes it with a click on select", () => {
@@ -745,7 +957,9 @@ describe("Select component tests", () => {
const submitInput = container.querySelector(`input[name="test"]`);
userEvent.click(select);
let options = getAllByRole("option");
- options[8] && userEvent.click(options[8]);
+ if (options[8]) {
+ userEvent.click(options[8]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: "oviedo" });
expect(queryByRole("listbox")).toBeFalsy();
expect(getByText("Oviedo")).toBeTruthy();
@@ -770,15 +984,32 @@ describe("Select component tests", () => {
expect(getAllByText("Placeholder example").length).toBe(2);
const options = getAllByRole("option");
expect(options[0]?.getAttribute("aria-selected")).toBe("true");
- options[0] && userEvent.click(options[0]);
+ if (options[0]) {
+ userEvent.click(options[0]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: "" });
expect(getAllByText("Placeholder example").length).toBe(1);
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-0");
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(onChange).toHaveBeenCalledWith({ value: "" });
expect(getAllByText("Placeholder example").length).toBe(1);
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-0");
});
test("Grouped Options — Filtering options never affects the optional item until there are no coincidence", () => {
@@ -794,10 +1025,14 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
const searchInput = container.querySelectorAll("input")[1];
userEvent.click(select);
- searchInput && userEvent.type(searchInput, "ro");
+ if (searchInput) {
+ userEvent.type(searchInput, "ro");
+ }
expect(getByText("Placeholder example")).toBeTruthy();
expect(getAllByRole("option").length).toBe(6);
- searchInput && userEvent.type(searchInput, "roro");
+ if (searchInput) {
+ userEvent.type(searchInput, "roro");
+ }
expect(queryByText("Placeholder example")).toBeFalsy();
expect(getByText("No matches found")).toBeTruthy();
});
@@ -837,11 +1072,36 @@ describe("Select component tests", () => {
);
const select = getByRole("combobox");
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(onChange).toHaveBeenCalledWith({ value: "ebro" });
expect(queryByRole("listbox")).toBeFalsy();
expect(getByText("Ebro")).toBeTruthy();
@@ -858,13 +1118,17 @@ describe("Select component tests", () => {
const searchInput = container.querySelectorAll("input")[1];
userEvent.click(select);
expect(getByRole("listbox")).toBeTruthy();
- searchInput && userEvent.type(searchInput, "ro");
+ if (searchInput) {
+ userEvent.type(searchInput, "ro");
+ }
expect(getAllByRole("presentation").length).toBe(2);
expect(getAllByRole("option").length).toBe(5);
expect(getByText("Colores")).toBeTruthy();
expect(getByText("Ríos españoles")).toBeTruthy();
let options = getAllByRole("option");
- options[4] && userEvent.click(options[4]);
+ if (options[4]) {
+ userEvent.click(options[4]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: "ebro" });
expect(queryByRole("listbox")).toBeFalsy();
expect(getByText("Ebro")).toBeTruthy();
@@ -882,7 +1146,9 @@ describe("Select component tests", () => {
const searchInput = container.querySelectorAll("input")[1];
userEvent.click(select);
expect(getByRole("listbox")).toBeTruthy();
- searchInput && userEvent.type(searchInput, "very long string");
+ if (searchInput) {
+ userEvent.type(searchInput, "very long string");
+ }
expect(getByText("No matches found")).toBeTruthy();
});
test("Grouped Options: Multiple selection — Displays a checkbox per option and enables the multi-selection", () => {
@@ -894,7 +1160,9 @@ describe("Select component tests", () => {
const submitInput = container.querySelector(`input[name="test"]`);
userEvent.click(select);
const options = getAllByRole("option");
- options[10] && userEvent.click(options[10]);
+ if (options[10]) {
+ userEvent.click(options[10]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: ["bilbao"] });
expect(queryByRole("listbox")).toBeTruthy();
expect(getAllByText("Bilbao").length).toBe(2);
@@ -914,10 +1182,18 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const options = getAllByRole("option");
- options[5] && userEvent.click(options[5]);
- options[8] && userEvent.click(options[8]);
- options[13] && userEvent.click(options[13]);
- options[17] && userEvent.click(options[17]);
+ if (options[5]) {
+ userEvent.click(options[5]);
+ }
+ if (options[8]) {
+ userEvent.click(options[8]);
+ }
+ if (options[13]) {
+ userEvent.click(options[13]);
+ }
+ if (options[17]) {
+ userEvent.click(options[17]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: ["blanco", "oviedo", "duero", "ebro"] });
expect(queryByRole("listbox")).toBeTruthy();
expect(getByText("Blanco, Oviedo, Duero, Ebro")).toBeTruthy();
@@ -947,7 +1223,9 @@ describe("Select component tests", () => {
userEvent.click(select);
expect(getAllByText("Choose an option").length).toBe(1);
const options = getAllByRole("option");
- options[0] && userEvent.click(options[0]);
+ if (options[0]) {
+ userEvent.click(options[0]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: ["azul"] });
expect(getAllByText("Azul").length).toBe(2);
});
@@ -958,18 +1236,55 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const options = getAllByRole("option");
- options[2] && userEvent.click(options[2]);
+ if (options[2]) {
+ userEvent.click(options[2]);
+ }
expect(getByText("Rosa")).toBeTruthy();
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-2");
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(getByText("Rojo")).toBeTruthy();
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-1");
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(getByText("Verde")).toBeTruthy();
});
test("Grouped Options — If an options was previously selected when its opened (by click and key press), the visual focus appears always in the selected option", () => {
@@ -979,19 +1294,46 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const options = getAllByRole("option");
- options[17] && userEvent.click(options[17]);
+ if (options[17]) {
+ userEvent.click(options[17]);
+ }
expect(getByText("Ebro")).toBeTruthy();
userEvent.click(select);
expect(select.getAttribute("aria-activedescendant")).toBeNull();
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(select.getAttribute("aria-activedescendant")).toBe("option-17");
userEvent.click(select);
fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
expect(select.getAttribute("aria-activedescendant")).toBe("option-17");
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(select, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(select, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(select, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(getByText("Azul")).toBeTruthy();
});
test("Multiple selection and optional — Clear action cleans every selected option but does not display an error", () => {
@@ -1002,9 +1344,15 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const options = getAllByRole("option");
- options[5] && userEvent.click(options[5]);
- options[8] && userEvent.click(options[8]);
- options[13] && userEvent.click(options[13]);
+ if (options[5]) {
+ userEvent.click(options[5]);
+ }
+ if (options[8]) {
+ userEvent.click(options[8]);
+ }
+ if (options[13]) {
+ userEvent.click(options[13]);
+ }
expect(onChange).toHaveBeenCalledWith({ value: ["6", "9", "14"] });
const clearSelectionButton = getByRole("button");
expect(clearSelectionButton.getAttribute("aria-label")).toBe("Clear selection");
@@ -1027,9 +1375,13 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const selectAllOption = getByText("Select all");
- selectAllOption && userEvent.click(selectAllOption);
+ if (selectAllOption) {
+ userEvent.click(selectAllOption);
+ }
expect(onChange).toHaveBeenCalledWith({ value: ["1", "2", "3", "4"] });
- selectAllOption && userEvent.click(selectAllOption);
+ if (selectAllOption) {
+ userEvent.click(selectAllOption);
+ }
expect(onChange).toHaveBeenCalledWith({ value: [] });
});
test("Select all (groups) — 'Select all' option is included and (un)selects all the options available", () => {
@@ -1047,11 +1399,15 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const selectAllOption = getByText("Select all");
- selectAllOption && userEvent.click(selectAllOption);
+ if (selectAllOption) {
+ userEvent.click(selectAllOption);
+ }
expect(onChange).toHaveBeenCalledWith({
value: ["azul", "rojo", "rosa", "madrid", "oviedo", "sevilla", "miño", "duero", "tajo"],
});
- selectAllOption && userEvent.click(selectAllOption);
+ if (selectAllOption) {
+ userEvent.click(selectAllOption);
+ }
expect(onChange).toHaveBeenCalledWith({ error: "This field is required. Please, enter a value.", value: [] });
});
test("Select all — Keyboard navigation is correct", () => {
@@ -1092,11 +1448,15 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const selectAllOption = getByText("Select all");
- selectAllOption && userEvent.click(selectAllOption);
+ if (selectAllOption) {
+ userEvent.click(selectAllOption);
+ }
expect(onChange).toHaveBeenCalledWith({
value: ["azul", "rojo", "rosa", "madrid", "oviedo", "sevilla", "miño", "duero", "tajo"],
});
- selectAllOption && userEvent.click(selectAllOption);
+ if (selectAllOption) {
+ userEvent.click(selectAllOption);
+ }
expect(onChange).toHaveBeenCalledWith({ error: "This field is required. Please, enter a value.", value: [] });
});
test("Select all options from a group — The header of a group is selectable and (un)selects all the options from its group", () => {
@@ -1114,11 +1474,15 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const thirdGroupHeader = getByText("Ríos españoles");
- thirdGroupHeader && userEvent.click(thirdGroupHeader);
+ if (thirdGroupHeader) {
+ userEvent.click(thirdGroupHeader);
+ }
expect(onChange).toHaveBeenCalledWith({
value: ["miño", "duero", "tajo"],
});
- thirdGroupHeader && userEvent.click(thirdGroupHeader);
+ if (thirdGroupHeader) {
+ userEvent.click(thirdGroupHeader);
+ }
expect(onChange).toHaveBeenCalledWith({ error: "This field is required. Please, enter a value.", value: [] });
});
test("Select all options from a group — The header of a group selects all the options when there's a partial selection", () => {
@@ -1137,11 +1501,15 @@ describe("Select component tests", () => {
const select = getByRole("combobox");
userEvent.click(select);
const thirdGroupHeader = getByText("Ríos españoles");
- thirdGroupHeader && userEvent.click(thirdGroupHeader);
+ if (thirdGroupHeader) {
+ userEvent.click(thirdGroupHeader);
+ }
expect(onChange).toHaveBeenCalledWith({
value: ["miño", "duero", "tajo"],
});
- thirdGroupHeader && userEvent.click(thirdGroupHeader);
+ if (thirdGroupHeader) {
+ userEvent.click(thirdGroupHeader);
+ }
expect(onChange).toHaveBeenCalledWith({ error: "This field is required. Please, enter a value.", value: [] });
});
test("Select all options from a group — Keyboard navigation is correct", () => {
diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx
index 2740777052..2dd32f93b6 100644
--- a/packages/lib/src/select/Select.tsx
+++ b/packages/lib/src/select/Select.tsx
@@ -40,7 +40,7 @@ import DxcFlex from "../flex/Flex";
import ErrorMessage from "../styles/forms/ErrorMessage";
import HelperText from "../styles/forms/HelperText";
import Label from "../styles/forms/Label";
-import { inputStylesByState } from "../styles/forms/inputStylesByState";
+import inputStylesByState from "../styles/forms/inputStylesByState";
const SelectContainer = styled.div<{
margin: SelectPropsType["margin"];
@@ -634,4 +634,6 @@ const DxcSelect = forwardRef(
}
);
+DxcSelect.displayName = "DxcSelect";
+
export default DxcSelect;
diff --git a/packages/lib/src/select/utils.ts b/packages/lib/src/select/utils.ts
index c73e248bdd..83af7d14bb 100644
--- a/packages/lib/src/select/utils.ts
+++ b/packages/lib/src/select/utils.ts
@@ -102,7 +102,7 @@ export const getSelectedOption = (
optional: boolean,
optionalItem: ListOptionType
) => {
- let selectedOption: ListOptionType | ListOptionType[] = multiple ? [] : ({} as ListOptionType);
+ let selectedOption: ListOptionType | ListOptionType[] | null = multiple ? [] : null;
let singleSelectionIndex: number | null = null;
if (multiple) {
@@ -135,12 +135,14 @@ export const getSelectedOption = (
groupIndex++;
return false;
});
+ return false;
} else if (option.value === value) {
selectedOption = option;
singleSelectionIndex = optional ? index + 1 : index;
return true;
+ } else {
+ return false;
}
- return false;
});
}
@@ -153,12 +155,15 @@ export const getSelectedOption = (
/**
* Return the label or labels of the selected option(s), separated by commas.
*/
-export const getSelectedOptionLabel = (placeholder: string, selectedOption: ListOptionType | ListOptionType[]) =>
+export const getSelectedOptionLabel = (
+ placeholder: string,
+ selectedOption: ListOptionType | ListOptionType[] | null
+) =>
Array.isArray(selectedOption)
? selectedOption.length === 0
? placeholder
: selectedOption.map((option) => option.label).join(", ")
- : (selectedOption.label ?? placeholder);
+ : (selectedOption?.label ?? placeholder);
/**
* Returns a determined string value depending on the amount of options selected:
@@ -209,9 +214,9 @@ export const getSelectableOptionsValues = (options: Props["options"]) =>
/**
* (Un)Selects the option passed as parameter.
- * @param currentValue
- * @param newOption
- * @returns
+ * @param currentValue
+ * @param newOption
+ * @returns
*/
export const computeNewValue = (currentValue: string[], newOption: ListOptionType) =>
currentValue.includes(newOption.value)
diff --git a/packages/lib/src/sidenav/Sidenav.stories.tsx b/packages/lib/src/sidenav/Sidenav.stories.tsx
index 8549eaec3c..2902a38129 100644
--- a/packages/lib/src/sidenav/Sidenav.stories.tsx
+++ b/packages/lib/src/sidenav/Sidenav.stories.tsx
@@ -1,10 +1,10 @@
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
-import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
+import ExampleContainer from "../../.storybook/components/ExampleContainer";
import DxcInset from "../inset/Inset";
import DxcSelect from "../select/Select";
import DxcSidenav from "./Sidenav";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Sidenav",
@@ -34,16 +34,6 @@ c-10.663,0-17.467,1.853-20.417,5.568c-2.949,3.711-4.428,10.23-4.428,19.558v31.11
);
-const TitleComponent = () => {
- return Dxc technology;
-};
-
-const opinionatedTheme = {
- sidenav: {
- baseColor: "#f2f2f2",
- },
-};
-
const SideNav = () => (
<>
@@ -112,32 +102,30 @@ const SideNav = () => (
);
const CollapsedGroupSidenav = () => (
- <>
-
-
- Dxc technology}>
-
-
- Group Link
- Group Link
- Group Link
- Group Link
-
-
-
-
- Group Link
- Group Link
-
-
- Group Link
- Group Link
- Group Link
-
-
-
-
- >
+
+
+ Dxc technology}>
+
+
+ Group Link
+ Group Link
+ Group Link
+ Group Link
+
+
+
+
+ Group Link
+ Group Link
+
+
+ Group Link
+ Group Link
+ Group Link
+
+
+
+
);
const HoveredGroupSidenav = () => (
@@ -225,9 +213,9 @@ export const CollapsableGroup: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const collapsableGroups = canvas.getAllByText("Collapsed Group");
- collapsableGroups.forEach((group) => {
- userEvent.click(group);
- });
+ for (const group of collapsableGroups) {
+ await userEvent.click(group);
+ }
},
};
@@ -236,10 +224,12 @@ export const CollapsedHoverGroup: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const collapsableGroups = canvas.getAllByText("Collapsed Group");
- collapsableGroups.forEach((group) => {
- userEvent.click(group);
+ for (const group of collapsableGroups) {
+ await userEvent.click(group);
+ }
+ await new Promise((resolve) => {
+ setTimeout(resolve, 1000);
});
- await new Promise((resolve) => setTimeout(resolve, 1000));
},
};
@@ -248,6 +238,8 @@ export const CollapsedActiveGroup: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const collapsableGroups = canvas.getAllByText("Collapsed Group");
- collapsableGroups[0] && userEvent.click(collapsableGroups[0]);
+ if (collapsableGroups[0]) {
+ await userEvent.click(collapsableGroups[0]);
+ }
},
};
diff --git a/packages/lib/src/sidenav/Sidenav.tsx b/packages/lib/src/sidenav/Sidenav.tsx
index 843a4ea4d1..814dd5b576 100644
--- a/packages/lib/src/sidenav/Sidenav.tsx
+++ b/packages/lib/src/sidenav/Sidenav.tsx
@@ -10,7 +10,7 @@ import SidenavPropsType, {
SidenavSectionPropsType,
SidenavTitlePropsType,
} from "./types";
-import { scrollbarStyles } from "../styles/scroll";
+import scrollbarStyles from "../styles/scroll";
import DxcDivider from "../divider/Divider";
import DxcInset from "../inset/Inset";
@@ -221,7 +221,7 @@ const Link = forwardRef(
return (
({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Slider component accessibility tests", () => {
it("Should not have basic accessibility issues", async () => {
diff --git a/packages/lib/src/slider/Slider.test.tsx b/packages/lib/src/slider/Slider.test.tsx
index e872b29bec..b2ead8feb2 100644
--- a/packages/lib/src/slider/Slider.test.tsx
+++ b/packages/lib/src/slider/Slider.test.tsx
@@ -1,16 +1,11 @@
-import { act, fireEvent, render, waitFor } from "@testing-library/react";
+import { act, fireEvent, render } from "@testing-library/react";
import DxcSlider from "./Slider";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Slider component tests", () => {
test("Slider renders with correct text and label id", () => {
@@ -34,7 +29,7 @@ describe("Slider component tests", () => {
expect(slider.getAttribute("aria-valuenow")).toBe("30");
expect(input.value).toBe("30");
});
- test("Slider correct limit values", async () => {
+ test("Slider correct limit values", () => {
const { getByRole, getByText } = render(
);
diff --git a/packages/lib/src/slider/Slider.tsx b/packages/lib/src/slider/Slider.tsx
index bf53352b0d..626a2c01e1 100644
--- a/packages/lib/src/slider/Slider.tsx
+++ b/packages/lib/src/slider/Slider.tsx
@@ -272,4 +272,6 @@ const DxcSlider = forwardRef(
}
);
+DxcSlider.displayName = "DxcSlider";
+
export default DxcSlider;
diff --git a/packages/lib/src/slider/utils.ts b/packages/lib/src/slider/utils.ts
index f6572672b0..a1345438a6 100644
--- a/packages/lib/src/slider/utils.ts
+++ b/packages/lib/src/slider/utils.ts
@@ -15,9 +15,9 @@ export const calculateWidth = (margin: SliderPropsType["margin"], size: SliderPr
/**
* Rounds a number to a specific number of decimal places.
* this function tries to avoid floating point inaccuracies, present in JS:
- *
+ *
* 0.1 + 0.2 === 0.3 // false
- *
+ *
* @param number the number to round
* @param step slider step value that defines the number of decimal places
* @returns the rounded number
@@ -29,7 +29,7 @@ export const stepPrecision = (target: number, step: number) => {
/**
* This function calculates the closest tick value to the target value within the range [min, max].
- *
+ *
* @param target the target value to round up
* @param step the step value that defines the ticks from the range
* @param min the minimum value of the range
@@ -42,17 +42,18 @@ export const roundUp = (target: number, step: number, min: number, max: number):
else if (target >= max) return max;
else if (step === 1) return Math.round(target);
- const ticks = Array.from({ length: Math.floor((max - min) / step) + 1 }, (_, index) => stepPrecision(min + index * step, step));
+ const ticks = Array.from({ length: Math.floor((max - min) / step) + 1 }, (_, index) =>
+ stepPrecision(min + index * step, step)
+ );
if (ticks.includes(target)) return target;
- let rounded = 0;
- let acc = Infinity;
- for (const tick of ticks) {
- const diff = Math.abs(stepPrecision(target - tick, target));
- if (diff < Math.abs(acc) || (diff === Math.abs(acc) && target > 0)) {
- rounded = tick;
- acc = diff;
- } else break;
- };
- return rounded;
+ return ticks?.reduce((closest, tick) => {
+ const currentDiff = Math.abs(stepPrecision(target - tick, target));
+ const closestDiff = Math.abs(stepPrecision(target - closest, target));
+
+ if (currentDiff < closestDiff || (currentDiff === closestDiff && target > 0)) {
+ return tick;
+ }
+ return closest;
+ }, ticks[0] ?? 0);
};
diff --git a/packages/lib/src/spinner/Spinner.stories.tsx b/packages/lib/src/spinner/Spinner.stories.tsx
index 1499c9f792..9c1e1a6364 100644
--- a/packages/lib/src/spinner/Spinner.stories.tsx
+++ b/packages/lib/src/spinner/Spinner.stories.tsx
@@ -1,8 +1,8 @@
import { Meta, StoryObj } from "@storybook/react";
+import { userEvent, within } from "@storybook/test";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcSpinner from "./Spinner";
-import { userEvent, within } from "@storybook/test";
export default {
title: "Spinner",
diff --git a/packages/lib/src/spinner/Spinner.test.tsx b/packages/lib/src/spinner/Spinner.test.tsx
index 3d1f5a8456..0c952049ce 100644
--- a/packages/lib/src/spinner/Spinner.test.tsx
+++ b/packages/lib/src/spinner/Spinner.test.tsx
@@ -25,13 +25,13 @@ describe("Spinner component tests", () => {
const { getByRole } = render();
expect(getByRole("progressbar")).toBeTruthy();
});
- test("Test spinner aria-label to be undefined", () => {
+ test("Spinner aria-label must be undefined for large size", () => {
const { getByRole } = render();
const spinner = getByRole("progressbar");
expect(spinner.getAttribute("aria-label")).toBeNull();
expect(spinner.getAttribute("aria-labelledby")).toBeTruthy();
});
- test("Test spinner aria-label to be applied correctly when mode is small", () => {
+ test("Spinner aria-label is applied correctly when mode is small", () => {
const { getByRole } = render(
);
diff --git a/packages/lib/src/spinner/types.ts b/packages/lib/src/spinner/types.ts
index d10d8f7c3c..129d70d81c 100644
--- a/packages/lib/src/spinner/types.ts
+++ b/packages/lib/src/spinner/types.ts
@@ -6,7 +6,7 @@ type Props = {
*/
ariaLabel?: string;
/**
- * If true, the color is inherited from the closest parent with a defined color. This allows users to adapt the spinner
+ * If true, the color is inherited from the closest parent with a defined color. This allows users to adapt the spinner
* to the semantic color of the use case in which it is used.
*/
inheritColor?: boolean;
diff --git a/packages/lib/src/status-light/StatusLight.test.tsx b/packages/lib/src/status-light/StatusLight.test.tsx
index 4403f4e29d..81177e3bd5 100644
--- a/packages/lib/src/status-light/StatusLight.test.tsx
+++ b/packages/lib/src/status-light/StatusLight.test.tsx
@@ -10,7 +10,7 @@ describe("StatusLight component tests", () => {
const { getByRole } = render();
expect(getByRole("status")).toBeTruthy();
});
-
+
test("StatusLight dot is aria-hidden", () => {
const { container } = render();
const dot = container.querySelector("div[aria-hidden='true']");
diff --git a/packages/lib/src/styles/forms/inputStylesByState.tsx b/packages/lib/src/styles/forms/inputStylesByState.tsx
index 39d5ac7067..5804cc20e2 100644
--- a/packages/lib/src/styles/forms/inputStylesByState.tsx
+++ b/packages/lib/src/styles/forms/inputStylesByState.tsx
@@ -1,6 +1,6 @@
import { css } from "@emotion/react";
-export const inputStylesByState = (disabled: boolean, error: boolean, readOnly: boolean) => css`
+const inputStylesByState = (disabled: boolean, error: boolean, readOnly: boolean) => css`
background-color: ${disabled ? `var(--color-bg-neutral-lighter)` : `transparent`};
border-radius: var(--border-radius-s);
border: ${!disabled && error ? "var(--border-width-m)" : "var(--border-width-s)"} var(--border-style-default)
@@ -27,3 +27,5 @@ export const inputStylesByState = (disabled: boolean, error: boolean, readOnly:
}`
: "cursor: not-allowed;"};
`;
+
+export default inputStylesByState;
diff --git a/packages/lib/src/styles/scroll.tsx b/packages/lib/src/styles/scroll.tsx
index 5cfd9358c5..87442e74d1 100644
--- a/packages/lib/src/styles/scroll.tsx
+++ b/packages/lib/src/styles/scroll.tsx
@@ -1,6 +1,6 @@
import { css } from "@emotion/react";
-export const scrollbarStyles = css`
+const scrollbarStyles = css`
&::-webkit-scrollbar {
width: 8px;
height: 8px;
@@ -14,3 +14,5 @@ export const scrollbarStyles = css`
border-radius: var(--border-radius-s);
}
`;
+
+export default scrollbarStyles;
diff --git a/packages/lib/src/styles/tables/tablesStyles.tsx b/packages/lib/src/styles/tables/tablesStyles.tsx
index 5e4cab0ab2..e6d4eaa8ce 100644
--- a/packages/lib/src/styles/tables/tablesStyles.tsx
+++ b/packages/lib/src/styles/tables/tablesStyles.tsx
@@ -1,6 +1,6 @@
import styled from "@emotion/styled";
import TablePropsType from "../../table/types";
-import { scrollbarStyles } from "../scroll";
+import scrollbarStyles from "../scroll";
import { calculateWidth } from "../../table/utils";
import { spaces } from "../../common/variables";
diff --git a/packages/lib/src/styles/variables.css b/packages/lib/src/styles/variables.css
index 1c4661cda9..685c6887e2 100644
--- a/packages/lib/src/styles/variables.css
+++ b/packages/lib/src/styles/variables.css
@@ -288,9 +288,12 @@
--height-xxl: var(--dimensions-48);
--height-xxxl: var(--dimensions-56);
--shadow-100: var(--dimensions-0) var(--dimensions-2) var(--dimensions-2) var(--dimensions-0) var(--color-alpha-400-a);
- --shadow-200: var(--dimensions-0) var(--dimensions-12) var(--dimensions-12) var(--dimensions-0) var(--color-alpha-300-a);
- --shadow-300: var(--dimensions-0) var(--dimensions-24) var(--dimensions-24) var(--dimensions-0) var(--color-alpha-300-a);
- --shadow-400: var(--dimensions-0) var(--dimensions-48) var(--dimensions-48) var(--dimensions-0) var(--color-alpha-300-a);
+ --shadow-200: var(--dimensions-0) var(--dimensions-12) var(--dimensions-12) var(--dimensions-0)
+ var(--color-alpha-300-a);
+ --shadow-300: var(--dimensions-0) var(--dimensions-24) var(--dimensions-24) var(--dimensions-0)
+ var(--color-alpha-300-a);
+ --shadow-400: var(--dimensions-0) var(--dimensions-48) var(--dimensions-48) var(--dimensions-0)
+ var(--color-alpha-300-a);
--spacing-gap-none: var(--dimensions-0);
--spacing-gap-xxs: var(--dimensions-2);
--spacing-gap-xs: var(--dimensions-4);
diff --git a/packages/lib/src/switch/Switch.accessibility.test.tsx b/packages/lib/src/switch/Switch.accessibility.test.tsx
index 49445e1c95..1840669207 100644
--- a/packages/lib/src/switch/Switch.accessibility.test.tsx
+++ b/packages/lib/src/switch/Switch.accessibility.test.tsx
@@ -1,7 +1,7 @@
import { render } from "@testing-library/react";
import { axe, formatRules } from "../../test/accessibility/axe-helper";
-import { disabledRules as rules } from "../../test/accessibility/rules/specific/switch/disabledRules";
import DxcSwitch from "./Switch";
+import rules from "../../test/accessibility/rules/specific/switch/disabledRules";
const disabledRules = {
rules: formatRules(rules),
diff --git a/packages/lib/src/switch/Switch.stories.tsx b/packages/lib/src/switch/Switch.stories.tsx
index 7eaa4cca8e..575b6585e9 100644
--- a/packages/lib/src/switch/Switch.stories.tsx
+++ b/packages/lib/src/switch/Switch.stories.tsx
@@ -1,8 +1,8 @@
import { Meta, StoryObj } from "@storybook/react";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
+import disabledRules from "../../test/accessibility/rules/specific/switch/disabledRules";
import Title from "../../.storybook/components/Title";
import preview from "../../.storybook/preview";
-import { disabledRules } from "../../test/accessibility/rules/specific/switch/disabledRules";
import DxcSwitch from "./Switch";
export default {
@@ -13,7 +13,7 @@ export default {
config: {
rules: [
...disabledRules.map((ruleId) => ({ id: ruleId, enabled: false })),
- ...preview?.parameters?.a11y?.config?.rules,
+ ...(preview?.parameters?.a11y?.config?.rules || []),
],
},
},
diff --git a/packages/lib/src/switch/Switch.test.tsx b/packages/lib/src/switch/Switch.test.tsx
index 23fbfd0c34..b8fd6aef6a 100644
--- a/packages/lib/src/switch/Switch.test.tsx
+++ b/packages/lib/src/switch/Switch.test.tsx
@@ -29,8 +29,12 @@ describe("Switch component tests", () => {
const { getByText } = render();
fireEvent.click(getByText("SwitchComponent"));
fireEvent.click(getByText("SwitchComponent"));
- expect(onChange.mock.calls[0][0]).toBe(true);
- expect(onChange.mock.calls[1][0]).toBe(true);
+
+ const firstCall = onChange.mock.calls[0] as [boolean];
+ const secondCall = onChange.mock.calls[1] as [boolean];
+
+ expect(firstCall[0]).toBe(true);
+ expect(secondCall[0]).toBe(true);
});
test("Every time the user use enter in the component, the onchange function is called with the correct value CONTROLLED COMPONENT", () => {
const onChange = jest.fn();
@@ -38,8 +42,12 @@ describe("Switch component tests", () => {
fireEvent.focus(getByText("SwitchComponent"));
fireEvent.keyDown(getByText("SwitchComponent"), { key: "Enter" });
fireEvent.keyDown(getByText("SwitchComponent"), { key: "Enter" });
- expect(onChange.mock.calls[0][0]).toBe(true);
- expect(onChange.mock.calls[1][0]).toBe(true);
+
+ const firstCall = onChange.mock.calls[0] as [boolean];
+ const secondCall = onChange.mock.calls[1] as [boolean];
+
+ expect(firstCall[0]).toBe(true);
+ expect(secondCall[0]).toBe(true);
});
test("Every time the user use space in the component, the onchange function is called with the correct value CONTROLLED COMPONENT", () => {
const onChange = jest.fn();
@@ -47,16 +55,24 @@ describe("Switch component tests", () => {
fireEvent.focus(getByText("SwitchComponent"));
fireEvent.keyDown(getByText("SwitchComponent"), { key: " " });
fireEvent.keyDown(getByText("SwitchComponent"), { key: " " });
- expect(onChange.mock.calls[0][0]).toBe(true);
- expect(onChange.mock.calls[1][0]).toBe(true);
+
+ const firstCall = onChange.mock.calls[0] as [boolean];
+ const secondCall = onChange.mock.calls[1] as [boolean];
+
+ expect(firstCall[0]).toBe(true);
+ expect(secondCall[0]).toBe(true);
});
test("Every time the user clicks the component the onchange function is called with the correct value UNCONTROLLED COMPONENT", () => {
const onChange = jest.fn();
const { getByText } = render();
fireEvent.click(getByText("SwitchComponent"));
fireEvent.click(getByText("SwitchComponent"));
- expect(onChange.mock.calls[0][0]).toBe(true);
- expect(onChange.mock.calls[1][0]).toBe(false);
+
+ const firstCall = onChange.mock.calls[0] as [boolean];
+ const secondCall = onChange.mock.calls[1] as [boolean];
+
+ expect(firstCall[0]).toBe(true);
+ expect(secondCall[0]).toBe(false);
});
test("Every time the user use enter in the component, the onchange function is called with the correct value UNCONTROLLED COMPONENT", () => {
const onChange = jest.fn();
@@ -64,8 +80,12 @@ describe("Switch component tests", () => {
fireEvent.focus(getByText("SwitchComponent"));
fireEvent.keyDown(getByText("SwitchComponent"), { key: "Enter" });
fireEvent.keyDown(getByText("SwitchComponent"), { key: "Enter" });
- expect(onChange.mock.calls[0][0]).toBe(true);
- expect(onChange.mock.calls[1][0]).toBe(false);
+
+ const firstCall = onChange.mock.calls[0] as [boolean];
+ const secondCall = onChange.mock.calls[1] as [boolean];
+
+ expect(firstCall[0]).toBe(true);
+ expect(secondCall[0]).toBe(false);
});
test("Every time the user use space in the component, the onchange function is called with the correct value UNCONTROLLED COMPONENT", () => {
const onChange = jest.fn();
@@ -73,8 +93,12 @@ describe("Switch component tests", () => {
fireEvent.focus(getByText("SwitchComponent"));
fireEvent.keyDown(getByText("SwitchComponent"), { key: " " });
fireEvent.keyDown(getByText("SwitchComponent"), { key: " " });
- expect(onChange.mock.calls[0][0]).toBe(true);
- expect(onChange.mock.calls[1][0]).toBe(false);
+
+ const firstCall = onChange.mock.calls[0] as [boolean];
+ const secondCall = onChange.mock.calls[1] as [boolean];
+
+ expect(firstCall[0]).toBe(true);
+ expect(secondCall[0]).toBe(false);
});
test("Renders with correct initial value and initial state when it is uncontrolled", () => {
const component = render(
diff --git a/packages/lib/src/switch/Switch.tsx b/packages/lib/src/switch/Switch.tsx
index 5d3629c016..704db66dcc 100644
--- a/packages/lib/src/switch/Switch.tsx
+++ b/packages/lib/src/switch/Switch.tsx
@@ -188,4 +188,6 @@ const DxcSwitch = forwardRef(
}
);
+DxcSwitch.displayName = "DxcSwitch";
+
export default DxcSwitch;
diff --git a/packages/lib/src/table/Table.accessibility.test.tsx b/packages/lib/src/table/Table.accessibility.test.tsx
index 9b59f0dcee..5cbceedeeb 100644
--- a/packages/lib/src/table/Table.accessibility.test.tsx
+++ b/packages/lib/src/table/Table.accessibility.test.tsx
@@ -3,22 +3,17 @@ import { axe, formatRules } from "../../test/accessibility/axe-helper";
import DxcTable from "./Table";
// TODO: REMOVE
-import { disabledRules as rules } from "../../test/accessibility/rules/specific/table/disabledRules";
+import rules from "../../test/accessibility/rules/specific/table/disabledRules";
const disabledRules = {
rules: formatRules(rules),
};
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Table component accessibility tests", () => {
it("Should not have basic accessibility issues", async () => {
diff --git a/packages/lib/src/table/Table.stories.tsx b/packages/lib/src/table/Table.stories.tsx
index 8f6afd8356..fc7fdf0181 100644
--- a/packages/lib/src/table/Table.stories.tsx
+++ b/packages/lib/src/table/Table.stories.tsx
@@ -1,10 +1,10 @@
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
+import disabledRules from "../../test/accessibility/rules/specific/table/disabledRules";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import preview from "../../.storybook/preview";
-import { disabledRules } from "../../test/accessibility/rules/specific/table/disabledRules";
import DxcTable from "./Table";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Table",
@@ -13,8 +13,11 @@ export default {
a11y: {
config: {
rules: [
- ...disabledRules.map((ruleId) => ({ id: ruleId, reviewOnFail: true })),
- ...preview?.parameters?.a11y?.config?.rules,
+ ...disabledRules.map((ruleId) => ({
+ id: ruleId,
+ reviewOnFail: true,
+ })),
+ ...(preview?.parameters?.a11y?.config?.rules || []),
],
},
},
@@ -101,69 +104,83 @@ const Table = () => (
-
- | header 1 |
- header 2 |
- actions |
-
-
- | cell 1 |
- cell 2 |
-
-
- |
-
-
- | cell 4 |
- cell 5 |
-
-
- |
-
-
- | cell 7 |
- cell 8 |
-
-
- |
-
+
+
+ | header 1 |
+ header 2 |
+ actions |
+
+
+
+
+ | cell 1 |
+ cell 2 |
+
+
+ |
+
+
+ | cell 4 |
+ cell 5 |
+
+
+ |
+
+
+ | cell 7 |
+ cell 8 |
+
+
+ |
+
+
-
- | header 1 |
- header 2 |
- actions |
-
-
- | cell 1 |
- cell 2 |
-
-
- |
-
-
- | cell 4 |
- cell 5 |
-
-
- |
-
-
- | cell 7 |
- cell 8 |
-
-
- |
-
+
+
+ | header 1 |
+ header 2 |
+ actions |
+
+
+
+
+ | cell 1 |
+ cell 2 |
+
+
+ |
+
+
+ | cell 4 |
+ cell 5 |
+
+
+ |
+
+
+ | cell 7 |
+ cell 8 |
+
+
+ |
+
+
@@ -267,7 +284,13 @@ const Table = () => (
@@ -354,21 +377,21 @@ const Table = () => (
| cell 1 |
cell 2 |
-
+ |
|
| cell 4 |
cell 5 |
-
+ |
|
| cell 7 |
cell 8 |
-
+ |
|
@@ -553,21 +576,21 @@ const ActionsCellDropdown = () => (
| cell 1 |
cell 2 |
-
+ |
|
| cell 4 |
cell 5 |
-
+ |
|
| cell 7 |
cell 8 |
-
+ |
|
@@ -586,6 +609,8 @@ export const DropdownAction: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const nextButton = canvas.getAllByRole("button")[8];
- nextButton && (await userEvent.click(nextButton));
+ if (nextButton) {
+ await userEvent.click(nextButton);
+ }
},
};
diff --git a/packages/lib/src/table/Table.test.tsx b/packages/lib/src/table/Table.test.tsx
index b92c9bbb2a..3c0f4fc56f 100644
--- a/packages/lib/src/table/Table.test.tsx
+++ b/packages/lib/src/table/Table.test.tsx
@@ -2,16 +2,11 @@ import { act, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import DxcTable from "./Table";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const icon = (
@@ -55,7 +50,7 @@ describe("Table component tests", () => {
expect(getByText("cell-6")).toBeTruthy();
});
- test("Table ActionsCell", async () => {
+ test("Table ActionsCell", () => {
const onSelectOption = jest.fn();
const onClick = jest.fn();
const actions = [
@@ -78,9 +73,9 @@ describe("Table component tests", () => {
],
},
{
- icon: icon,
+ icon,
title: "icon2",
- onClick: onClick,
+ onClick,
},
];
const { getAllByRole, getByRole, getByText } = render(
@@ -101,7 +96,7 @@ describe("Table component tests", () => {
| cell-4 |
cell-5 |
-
+ |
|
@@ -111,14 +106,18 @@ describe("Table component tests", () => {
const dropdown = getAllByRole("button")[1];
act(() => {
- dropdown && userEvent.click(dropdown);
+ if (dropdown) {
+ userEvent.click(dropdown);
+ }
});
expect(getByRole("menu")).toBeTruthy();
const option = getByText("Aliexpress");
userEvent.click(option);
expect(onSelectOption).toHaveBeenCalledWith("3");
const action = getAllByRole("button")[0];
- action && userEvent.click(action);
+ if (action) {
+ userEvent.click(action);
+ }
expect(onClick).toHaveBeenCalled();
});
});
diff --git a/packages/lib/src/table/Table.tsx b/packages/lib/src/table/Table.tsx
index 2e812ceac5..e2bb019158 100644
--- a/packages/lib/src/table/Table.tsx
+++ b/packages/lib/src/table/Table.tsx
@@ -1,9 +1,72 @@
import styled from "@emotion/styled";
+import { spaces } from "../common/variables";
+import { getMargin } from "../common/utils";
import DxcDropdown from "../dropdown/Dropdown";
import DxcActionIcon from "../action-icon/ActionIcon";
import TablePropsType, { ActionsCellPropsType } from "./types";
+import scrollbarStyles from "../styles/scroll";
import { useMemo } from "react";
-import { Table, TableContainer } from "../styles/tables/tablesStyles";
+
+const calculateWidth = (margin: TablePropsType["margin"]) =>
+ `calc(100% - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})`;
+
+const TableContainer = styled.div<{ margin: TablePropsType["margin"] }>`
+ width: ${({ margin }) => calculateWidth(margin)};
+ margin: ${({ margin }) => (margin && typeof margin !== "object" ? spaces[margin] : "0px")};
+ margin-top: ${({ margin }) => (margin && typeof margin === "object" && margin.top ? spaces[margin.top] : "")};
+ margin-right: ${({ margin }) => (margin && typeof margin === "object" && margin.right ? spaces[margin.right] : "")};
+ margin-bottom: ${({ margin }) =>
+ margin && typeof margin === "object" && margin.bottom ? spaces[margin.bottom] : ""};
+ margin-left: ${({ margin }) => (margin && typeof margin === "object" && margin.left ? spaces[margin.left] : "")};
+ overflow: auto;
+ ${scrollbarStyles}
+`;
+
+const Table = styled.table<{ mode: TablePropsType["mode"] }>`
+ border-collapse: collapse;
+ width: 100%;
+
+ & tr {
+ border-bottom: var(--border-width-s) solid var(--border-color-neutral-lighter);
+ height: ${({ mode }) => (mode === "default" ? "var(--height-xxl)" : "var(--height-l)")};
+ }
+ & td {
+ background-color: var(--color-fg-neutral-bright);
+ color: var(--color-fg-neutral-dark);
+ font-family: var(--typography-font-family);
+ font-size: var(--typography-label-m);
+ font-style: normal;
+ font-weight: var(--typography-label-regular);
+ line-height: normal;
+ padding: var(--spacing-padding-s) var(--spacing-padding-m);
+ text-align: start;
+ }
+ & th {
+ background-color: var(--color-fg-primary-strong);
+ color: var(--color-fg-neutral-bright);
+ font-family: var(--typography-font-family);
+ font-size: var(--typography-label-m);
+ font-style: normal;
+ font-weight: var(--typography-label-regular);
+ line-height: normal;
+ padding: var(--spacing-padding-s) var(--spacing-padding-m);
+ text-align: start;
+ }
+ & th:first-child {
+ border-top-left-radius: var(--border-radius-s);
+ padding-left: var(--spacing-padding-ml);
+ }
+ & th:last-child {
+ border-top-right-radius: var(--border-radius-s);
+ padding-right: var(--spacing-padding-ml);
+ }
+ & td:first-child {
+ padding-left: var(--spacing-padding-ml);
+ }
+ & td:last-child {
+ padding-right: var(--spacing-padding-ml);
+ }
+`;
const ActionsContainer = styled.div`
display: flex;
diff --git a/packages/lib/src/tabs/Tab.tsx b/packages/lib/src/tabs/Tab.tsx
index 54973d282e..2f97d91919 100644
--- a/packages/lib/src/tabs/Tab.tsx
+++ b/packages/lib/src/tabs/Tab.tsx
@@ -177,4 +177,6 @@ const DxcTab = forwardRef(
}
);
+DxcTab.displayName = "DxcTab";
+
export default DxcTab;
diff --git a/packages/lib/src/tabs/Tabs.accessibility.test.tsx b/packages/lib/src/tabs/Tabs.accessibility.test.tsx
index 27a8f562ed..6ed057616c 100644
--- a/packages/lib/src/tabs/Tabs.accessibility.test.tsx
+++ b/packages/lib/src/tabs/Tabs.accessibility.test.tsx
@@ -2,11 +2,11 @@ import { render } from "@testing-library/react";
import { axe } from "../../test/accessibility/axe-helper";
import DxcTabs from "./Tabs";
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const iconSVG = (
diff --git a/packages/lib/src/tabs/Tabs.stories.tsx b/packages/lib/src/tabs/Tabs.stories.tsx
index 7ede59472d..44d44861af 100644
--- a/packages/lib/src/tabs/Tabs.stories.tsx
+++ b/packages/lib/src/tabs/Tabs.stories.tsx
@@ -1,10 +1,10 @@
+import { Meta, StoryObj } from "@storybook/react";
import { INITIAL_VIEWPORTS } from "@storybook/addon-viewport";
+import { userEvent, within } from "@storybook/test";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcTabs from "./Tabs";
import type { Margin, Space } from "../common/utils";
-import { Meta, StoryObj } from "@storybook/react/*";
-import { userEvent, within } from "@storybook/test";
export default {
title: "Tabs",
diff --git a/packages/lib/src/tabs/Tabs.test.tsx b/packages/lib/src/tabs/Tabs.test.tsx
index 6a2e59658b..66f4f4815c 100644
--- a/packages/lib/src/tabs/Tabs.test.tsx
+++ b/packages/lib/src/tabs/Tabs.test.tsx
@@ -2,11 +2,11 @@ import "@testing-library/jest-dom";
import { fireEvent, render } from "@testing-library/react";
import DxcTabs from "./Tabs";
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const sampleTabs = (
@@ -154,17 +154,23 @@ describe("Tabs component tests", () => {
const onTabClick = [jest.fn(), jest.fn(), jest.fn()];
const { getAllByRole } = render(sampleTabsInteraction(onTabClick));
const tabs = getAllByRole("tab");
- tabs[0] && fireEvent.click(tabs[0]);
+ if (tabs[0]) {
+ fireEvent.click(tabs[0]);
+ }
expect(onTabClick[0]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
- tabs[1] && fireEvent.click(tabs[1]);
+ if (tabs[1]) {
+ fireEvent.click(tabs[1]);
+ }
expect(onTabClick[1]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
- tabs[2] && fireEvent.click(tabs[2]);
+ if (tabs[2]) {
+ fireEvent.click(tabs[2]);
+ }
expect(onTabClick[2]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
@@ -196,42 +202,54 @@ describe("Tabs component tests", () => {
expect(onTabClick[0]).toHaveBeenCalled();
fireEvent.keyDown(tabList, { key: "ArrowRight" });
expect(tabs[1]).toHaveFocus();
- tabs[1] && fireEvent.keyDown(tabs[1], { key: "Enter" });
+ if (tabs[1]) {
+ fireEvent.keyDown(tabs[1], { key: "Enter" });
+ }
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
expect(onTabClick[1]).toHaveBeenCalled();
fireEvent.keyDown(tabList, { key: "ArrowRight" });
expect(tabs[2]).toHaveFocus();
- tabs[2] && fireEvent.keyDown(tabs[2], { key: "Enter" });
+ if (tabs[2]) {
+ fireEvent.keyDown(tabs[2], { key: "Enter" });
+ }
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("true");
expect(onTabClick[2]).toHaveBeenCalled();
fireEvent.keyDown(tabList, { key: "ArrowLeft" });
expect(tabs[1]).toHaveFocus();
- tabs[1] && fireEvent.keyDown(tabs[1], { key: "Enter" });
+ if (tabs[1]) {
+ fireEvent.keyDown(tabs[1], { key: "Enter" });
+ }
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
expect(onTabClick[1]).toHaveBeenCalled();
fireEvent.keyDown(tabList, { key: "ArrowLeft" });
expect(tabs[0]).toHaveFocus();
- tabs[0] && fireEvent.keyDown(tabs[0], { key: "Enter" });
+ if (tabs[0]) {
+ fireEvent.keyDown(tabs[0], { key: "Enter" });
+ }
expect(tabs[0]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
expect(onTabClick[0]).toHaveBeenCalled();
fireEvent.keyDown(tabList, { key: "ArrowLeft" });
expect(tabs[2]).toHaveFocus();
- tabs[2] && fireEvent.keyDown(tabs[2], { key: "Enter" });
+ if (tabs[2]) {
+ fireEvent.keyDown(tabs[2], { key: "Enter" });
+ }
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("true");
expect(onTabClick[2]).toHaveBeenCalled();
fireEvent.keyDown(tabList, { key: "ArrowRight" });
expect(tabs[0]).toHaveFocus();
- tabs[0] && fireEvent.keyDown(tabs[0], { key: "Enter" });
+ if (tabs[0]) {
+ fireEvent.keyDown(tabs[0], { key: "Enter" });
+ }
expect(tabs[0]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
@@ -251,7 +269,9 @@ describe("Tabs component tests", () => {
expect(onTabClick[0]).toHaveBeenCalled();
fireEvent.keyDown(tabList, { key: "ArrowRight" });
expect(tabs[2]).toHaveFocus();
- tabs[2] && fireEvent.keyDown(tabs[2], { key: " " });
+ if (tabs[2]) {
+ fireEvent.keyDown(tabs[2], { key: " " });
+ }
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("true");
@@ -262,17 +282,23 @@ describe("Tabs component tests", () => {
const onTabClick = [jest.fn(), jest.fn(), jest.fn()];
const { getAllByRole } = render(sampleControlledTabsInteraction(onTabClick));
const tabs = getAllByRole("tab");
- tabs[0] && fireEvent.click(tabs[0]);
+ if (tabs[0]) {
+ fireEvent.click(tabs[0]);
+ }
expect(onTabClick[0]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
- tabs[1] && fireEvent.click(tabs[1]);
+ if (tabs[1]) {
+ fireEvent.click(tabs[1]);
+ }
expect(onTabClick[1]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
- tabs[2] && fireEvent.click(tabs[2]);
+ if (tabs[2]) {
+ fireEvent.click(tabs[2]);
+ }
expect(onTabClick[2]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
@@ -283,17 +309,23 @@ describe("Tabs component tests", () => {
const onTabClick = [jest.fn(), jest.fn(), jest.fn()];
const { getAllByRole } = render(sampleTabsWithoutLabel(onTabClick));
const tabs = getAllByRole("tab");
- tabs[0] && fireEvent.click(tabs[0]);
+ if (tabs[0]) {
+ fireEvent.click(tabs[0]);
+ }
expect(onTabClick[0]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
- tabs[1] && fireEvent.click(tabs[1]);
+ if (tabs[1]) {
+ fireEvent.click(tabs[1]);
+ }
expect(onTabClick[1]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("true");
expect(tabs[2]?.getAttribute("aria-selected")).toBe("false");
- tabs[2] && fireEvent.click(tabs[2]);
+ if (tabs[2]) {
+ fireEvent.click(tabs[2]);
+ }
expect(onTabClick[2]).toHaveBeenCalled();
expect(tabs[0]?.getAttribute("aria-selected")).toBe("false");
expect(tabs[1]?.getAttribute("aria-selected")).toBe("false");
diff --git a/packages/lib/src/tabs/Tabs.tsx b/packages/lib/src/tabs/Tabs.tsx
index 06b6831a9a..2d028d83d6 100644
--- a/packages/lib/src/tabs/Tabs.tsx
+++ b/packages/lib/src/tabs/Tabs.tsx
@@ -3,9 +3,9 @@ import {
isValidElement,
KeyboardEvent,
ReactElement,
+ ReactNode,
useContext,
useEffect,
- useLayoutEffect,
useMemo,
useRef,
useState,
@@ -89,21 +89,14 @@ const ScrollableTabsList = styled.div<{
`;
const DxcTabs = ({ children, iconPosition = "left", margin, tabIndex = 0 }: TabsPropsType) => {
- const childrenArray: ReactElement[] = useMemo(
- () => Children.toArray(children) as ReactElement[],
- [children]
- );
- const [activeTabId, setActiveTabId] = useState(() => {
- const hasActiveChild = childrenArray.some(
- (child) => isValidElement(child) && (child.props.active || child.props.defaultActive) && !child.props.disabled
+ const isTabElement = (child: ReactNode): child is ReactElement => isValidElement(child);
+ const childrenArray = useMemo(() => Children.toArray(children).filter(isTabElement), [children]);
+ const [activeTabId, setActiveTabId] = useState(() => {
+ const activeChild = childrenArray.find(
+ (child) => (child.props.active || child.props.defaultActive) && !child.props.disabled
);
- const initialActiveTab = hasActiveChild
- ? childrenArray.find(
- (child) => isValidElement(child) && (child.props.active || child.props.defaultActive) && !child.props.disabled
- )
- : childrenArray.find((child) => isValidElement(child) && !child.props.disabled);
-
- return isValidElement(initialActiveTab) ? (initialActiveTab.props.label ?? initialActiveTab.props.tabId) : "";
+ const initialTab = activeChild ?? childrenArray.find((child) => !child.props.disabled);
+ return initialTab?.props.label ?? initialTab?.props.tabId ?? "";
});
const [innerFocusIndex, setInnerFocusIndex] = useState(null);
const [scrollLeftEnabled, setScrollLeftEnabled] = useState(false);
@@ -119,7 +112,7 @@ const DxcTabs = ({ children, iconPosition = "left", margin, tabIndex = 0 }: Tabs
activeTabId: activeTabId,
focusedTabId: isValidElement(focusedChild) ? (focusedChild.props.label ?? focusedChild.props.tabId) : "",
iconPosition,
- isControlled: childrenArray.some((child) => isValidElement(child) && typeof child.props.active !== "undefined"),
+ isControlled: childrenArray.some((child) => typeof child.props.active !== "undefined"),
setActiveTabId: setActiveTabId,
tabIndex,
};
@@ -153,9 +146,7 @@ const DxcTabs = ({ children, iconPosition = "left", margin, tabIndex = 0 }: Tabs
};
const handleOnKeyDown = (event: KeyboardEvent) => {
- const activeTab = childrenArray.findIndex(
- (child: ReactElement) => (child.props.label ?? child.props.tabId) === activeTabId
- );
+ const activeTab = childrenArray.findIndex((child) => (child.props.label ?? child.props.tabId) === activeTabId);
let index;
switch (event.key) {
case "Left":
@@ -238,7 +229,7 @@ const DxcTabs = ({ children, iconPosition = "left", margin, tabIndex = 0 }: Tabs
{Children.map(children, (child) =>
- isValidElement(child) && child.props.tabId === activeTabId ? child.props.children : null
+ isTabElement(child) && child.props.tabId === activeTabId ? child.props.children : null
)}
>
);
diff --git a/packages/lib/src/text-input/Suggestion.tsx b/packages/lib/src/text-input/Suggestion.tsx
index f680ebbfc3..20cdc03761 100644
--- a/packages/lib/src/text-input/Suggestion.tsx
+++ b/packages/lib/src/text-input/Suggestion.tsx
@@ -36,7 +36,10 @@ const StyledSuggestion = styled.span`
const Suggestion = ({ highlighted, id, isLast, onClick, suggestion, value, visuallyFocused }: SuggestionProps) => {
const matchedSuggestion = useMemo(() => {
const regEx = new RegExp(transformSpecialChars(value), "i");
- return { matchedWords: suggestion.match(regEx), noMatchedWords: suggestion.replace(regEx, "") };
+ return {
+ matchedWords: suggestion.match(regEx),
+ noMatchedWords: suggestion.replace(regEx, ""),
+ };
}, [value, suggestion]);
return (
diff --git a/packages/lib/src/text-input/Suggestions.tsx b/packages/lib/src/text-input/Suggestions.tsx
index cd6563c32a..7757d106b0 100644
--- a/packages/lib/src/text-input/Suggestions.tsx
+++ b/packages/lib/src/text-input/Suggestions.tsx
@@ -4,7 +4,7 @@ import { HalstackLanguageContext } from "../HalstackContext";
import Suggestion from "./Suggestion";
import { SuggestionsProps } from "./types";
import DxcIcon from "../icon/Icon";
-import { scrollbarStyles } from "../styles/scroll";
+import scrollbarStyles from "../styles/scroll";
const SuggestionsContainer = styled.div`
box-sizing: border-box;
diff --git a/packages/lib/src/text-input/TextInput.accessibility.test.tsx b/packages/lib/src/text-input/TextInput.accessibility.test.tsx
index 4045e1b50f..d7d3a68a98 100644
--- a/packages/lib/src/text-input/TextInput.accessibility.test.tsx
+++ b/packages/lib/src/text-input/TextInput.accessibility.test.tsx
@@ -1,6 +1,7 @@
import { render } from "@testing-library/react";
import { axe } from "../../test/accessibility/axe-helper";
import DxcTextInput from "./TextInput";
+import MockDOMRect from "../../test/mocks/domRectMock";
const countries = [
"Afghanistan",
@@ -39,15 +40,12 @@ const action = {
};
// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.DOMRect = MockDOMRect;
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("TextInput component accessibility tests", () => {
it("Should not have basic accessibility issues", async () => {
diff --git a/packages/lib/src/text-input/TextInput.stories.tsx b/packages/lib/src/text-input/TextInput.stories.tsx
index 0f09c5f9c0..88c61e4d0c 100644
--- a/packages/lib/src/text-input/TextInput.stories.tsx
+++ b/packages/lib/src/text-input/TextInput.stories.tsx
@@ -1,10 +1,11 @@
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcFlex from "../flex/Flex";
import Suggestions from "./Suggestions";
import DxcTextInput from "./TextInput";
-import { Meta, StoryObj } from "@storybook/react";
+
export default {
title: "Text Input",
component: DxcTextInput,
@@ -262,7 +263,9 @@ const AutosuggestListbox = () => (
placeholder="Choose an option"
size="fillParent"
/>
- Submit
+
+ Submit
+
diff --git a/packages/lib/src/text-input/TextInput.test.tsx b/packages/lib/src/text-input/TextInput.test.tsx
index 7b8a6d2100..9f8dc8140b 100644
--- a/packages/lib/src/text-input/TextInput.test.tsx
+++ b/packages/lib/src/text-input/TextInput.test.tsx
@@ -1,17 +1,15 @@
import { act, fireEvent, render, waitForElementToBeRemoved } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import DxcTextInput from "./TextInput";
+import MockDOMRect from "../../test/mocks/domRectMock";
// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.DOMRect = MockDOMRect;
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
const countries = [
"Afghanistan",
@@ -79,7 +77,10 @@ describe("TextInput component tests", () => {
fireEvent.focus(input);
fireEvent.blur(input);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "",
+ error: "This field is required. Please, enter a value.",
+ });
fireEvent.change(input, { target: { value: "Test" } });
fireEvent.blur(input);
expect(onBlur).toHaveBeenCalled();
@@ -97,7 +98,10 @@ describe("TextInput component tests", () => {
expect(onChange).toHaveBeenCalledWith({ value: "Test" });
userEvent.clear(input);
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "",
+ error: "This field is required. Please, enter a value.",
+ });
});
test("Pattern constraint", () => {
@@ -117,10 +121,16 @@ describe("TextInput component tests", () => {
const input = getByRole("textbox");
fireEvent.change(input, { target: { value: "pattern test" } });
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "pattern test", error: "Please match the format requested." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "pattern test",
+ error: "Please match the format requested.",
+ });
fireEvent.blur(input);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "pattern test", error: "Please match the format requested." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "pattern test",
+ error: "Please match the format requested.",
+ });
userEvent.clear(input);
fireEvent.change(input, { target: { value: "pattern4&" } });
expect(onChange).toHaveBeenCalled();
@@ -148,10 +158,16 @@ 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." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "test",
+ error: "Min length 5, max length 10.",
+ });
fireEvent.blur(input);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "test", error: "Min length 5, max length 10." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "test",
+ error: "Min length 5, max length 10.",
+ });
userEvent.clear(input);
fireEvent.change(input, { target: { value: "length" } });
expect(onChange).toHaveBeenCalled();
@@ -180,16 +196,28 @@ 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." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "test",
+ error: "Min length 5, max length 10.",
+ });
fireEvent.blur(input);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "test", error: "Min length 5, max length 10." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "test",
+ error: "Min length 5, max length 10.",
+ });
fireEvent.change(input, { target: { value: "tests" } });
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "tests", error: "Please match the format requested." });
+ 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." });
+ 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&" });
@@ -219,12 +247,12 @@ describe("TextInput component tests", () => {
expect(onBlur).toHaveBeenCalledWith({ value: "Blur test" });
});
- test("Clear action onClick cleans the input", async () => {
+ test("Clear action onClick cleans the input", () => {
const { getByRole } = render();
const input = getByRole("textbox") as HTMLInputElement;
userEvent.type(input, "Test");
const closeAction = getByRole("button");
- await userEvent.click(closeAction);
+ userEvent.click(closeAction);
expect(input.value).toBe("");
});
@@ -236,10 +264,10 @@ describe("TextInput component tests", () => {
expect(onChange).not.toHaveBeenCalled();
});
- test("Disabled text input (action must be shown but not clickable)", async () => {
+ test("Disabled text input (action must be shown but not clickable)", () => {
const onClick = jest.fn();
const action = {
- onClick: onClick,
+ onClick,
icon: (
{
const { getByRole } = render();
const input = getByRole("textbox") as HTMLInputElement;
expect(input.disabled).toBeTruthy();
- await userEvent.click(getByRole("button"));
+ userEvent.click(getByRole("button"));
expect(onClick).not.toHaveBeenCalled();
});
@@ -297,10 +325,10 @@ describe("TextInput component tests", () => {
expect(onChange).not.toHaveBeenCalled();
});
- test("Read-only text input sends its value on submit", async () => {
- const handlerOnSubmit = jest.fn((e) => {
+ test("Read-only text input sends its value on submit", () => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({ data: "Text" });
});
@@ -311,14 +339,14 @@ describe("TextInput component tests", () => {
);
const submit = getByText("Submit");
- await userEvent.click(submit);
+ userEvent.click(submit);
expect(handlerOnSubmit).toHaveBeenCalled();
});
- test("Read-only text input doesn't trigger custom action's onClick event", async () => {
+ test("Read-only text input doesn't trigger custom action's onClick event", () => {
const onClick = jest.fn();
const action = {
- onClick: onClick,
+ onClick,
icon: (
{
title: "Search",
};
const { getByRole } = render();
- await userEvent.click(getByRole("button"));
+ userEvent.click(getByRole("button"));
expect(onClick).not.toHaveBeenCalled();
});
- test("Action prop: image displayed and onClick event", async () => {
+ test("Action prop: image displayed and onClick event", () => {
const onClick = jest.fn();
const action = {
- onClick: onClick,
+ onClick,
icon: (
{
};
const { getByRole, getByTestId } = render();
expect(getByTestId("image")).toBeTruthy();
- await userEvent.click(getByRole("button"));
+ userEvent.click(getByRole("button"));
expect(onClick).toHaveBeenCalled();
});
- test("Text input submit correctly value in form", async () => {
+ test("Text input submit correctly value in form", () => {
const onClick = jest.fn();
const action = {
- onClick: onClick,
+ onClick,
icon: (
{
),
title: "Search",
};
- const handlerOnSubmit = jest.fn((e) => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({ data: "test" });
});
@@ -400,9 +428,11 @@ describe("TextInput component tests", () => {
const input = getByRole("textbox") as HTMLInputElement;
userEvent.type(input, "test");
expect(input.value).toBe("test");
- search && (await userEvent.click(search));
+ if (search) {
+ userEvent.click(search);
+ }
expect(handlerOnSubmit).not.toHaveBeenCalled();
- await userEvent.click(submit);
+ userEvent.click(submit);
expect(handlerOnSubmit).toHaveBeenCalled();
});
@@ -415,7 +445,7 @@ describe("TextInput component tests", () => {
test("Text Input has correct aria accessibility attributes", () => {
const onClick = jest.fn();
const action = {
- onClick: onClick,
+ onClick,
icon: (
{
expect(input.getAttribute("aria-required")).toBe("true");
userEvent.type(input, "Text");
const clear = getAllByRole("button")[0];
- clear && expect(clear.getAttribute("aria-label")).toBe("Clear field");
+ expect(clear?.getAttribute("aria-label")).toBe("Clear field");
const search = getAllByRole("button")[1];
- search && expect(search.getAttribute("aria-label")).toBe("Search");
+ expect(search?.getAttribute("aria-label")).toBe("Search");
});
test("Autosuggest has correct accessibility attributes", () => {
@@ -462,7 +492,7 @@ describe("TextInput component tests", () => {
expect(input.getAttribute("aria-controls")).toBe(list.id);
expect(input.getAttribute("aria-expanded")).toBe("true");
const options = getAllByRole("option");
- options[0] && expect(options[0].getAttribute("aria-selected")).toBeNull();
+ expect(options[0]?.getAttribute("aria-selected")).toBeNull();
});
test("Mouse wheel interaction does not affect the text value", () => {
@@ -493,13 +523,13 @@ describe("TextInput component synchronous autosuggest tests", () => {
expect(getByText("Andorra")).toBeTruthy();
});
- test("Autosuggest is displayed when the user clicks the input", async () => {
+ test("Autosuggest is displayed when the user clicks the input", () => {
const onChange = jest.fn();
const { getByRole, getByText } = render(
);
const input = getByRole("combobox");
- await userEvent.click(input);
+ userEvent.click(input);
const list = getByRole("listbox");
expect(list).toBeTruthy();
expect(getByText("Afghanistan")).toBeTruthy();
@@ -508,19 +538,19 @@ describe("TextInput component synchronous autosuggest tests", () => {
expect(getByText("Andorra")).toBeTruthy();
});
- test("Autosuggest is displayed while the user is writing (if closed previously, if it is open stays open)", async () => {
+ test("Autosuggest is displayed while the user is writing (if closed previously, if it is open stays open)", () => {
const { getByRole, getByText, getAllByText } = render(
);
const input = getByRole("combobox");
- await userEvent.type(input, "Bah");
+ userEvent.type(input, "Bah");
expect(getByRole("listbox")).toBeTruthy();
expect(getAllByText("Bah").length).toBe(2);
expect(getByText("amas")).toBeTruthy();
expect(getByText("rain")).toBeTruthy();
});
- test("Read-only text input does not open the suggestions list", async () => {
+ test("Read-only text input does not open the suggestions list", () => {
const onChange = jest.fn();
const { getByRole, queryByRole } = render(
@@ -528,7 +558,7 @@ describe("TextInput component synchronous autosuggest tests", () => {
const input = getByRole("combobox");
fireEvent.focus(input);
expect(queryByRole("listbox")).toBeFalsy();
- await userEvent.click(input);
+ userEvent.click(input);
expect(queryByRole("listbox")).toBeFalsy();
});
@@ -558,76 +588,88 @@ describe("TextInput component synchronous autosuggest tests", () => {
);
const input = queryByRole("textbox");
- input && fireEvent.focus(input);
+ if (input) {
+ fireEvent.focus(input);
+ }
expect(queryByRole("listbox")).toBeFalsy();
});
- test("Autosuggest closes the listbox when there are no matches for the user's input", async () => {
+ test("Autosuggest closes the listbox when there are no matches for the user's input", () => {
const onChange = jest.fn();
const { getByRole, queryByRole } = render(
);
const input = getByRole("combobox");
- await act(async () => {
+ act(() => {
userEvent.type(input, "x");
});
expect(queryByRole("listbox")).toBeFalsy();
});
- test("Autosuggest with no matches founded doesn't let the listbox to be opened", async () => {
+ test("Autosuggest with no matches founded doesn't let the listbox to be opened", () => {
const onChange = jest.fn();
const { getByRole, queryByRole } = render(
);
const input = getByRole("combobox");
- await act(async () => {
+ act(() => {
userEvent.type(input, "x");
});
expect(queryByRole("listbox")).toBeFalsy();
fireEvent.focus(input);
expect(queryByRole("listbox")).toBeFalsy();
- fireEvent.keyDown(input, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(input, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(queryByRole("listbox")).toBeFalsy();
- fireEvent.keyDown(input, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(input, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(queryByRole("listbox")).toBeFalsy();
});
- test("Autosuggest uncontrolled — Suggestion selected by click", async () => {
+ test("Autosuggest uncontrolled — Suggestion selected by click", () => {
const onChange = jest.fn();
const { getByRole, getByText, queryByRole } = render(
);
const input = getByRole("combobox") as HTMLInputElement;
fireEvent.focus(input);
- await act(async () => {
+ act(() => {
userEvent.type(input, "Alba");
});
expect(onChange).toHaveBeenCalled();
expect(getByText("Alba")).toBeTruthy();
expect(getByText("nia")).toBeTruthy();
- await act(async () => {
+ act(() => {
userEvent.click(getByRole("option"));
});
expect(input.value).toBe("Albania");
expect(queryByRole("listbox")).toBeFalsy();
});
- test("Autosuggest controlled — Suggestion selected by click", async () => {
+ test("Autosuggest controlled — Suggestion selected by click", () => {
const onChange = jest.fn();
const { getByRole, getByText, queryByRole } = render(
);
const input = getByRole("combobox") as HTMLInputElement;
- await userEvent.click(getByText("Autocomplete Countries"));
+ userEvent.click(getByText("Autocomplete Countries"));
expect(input.value).toBe("Andor");
expect(getByText("Andor")).toBeTruthy();
expect(getByText("ra")).toBeTruthy();
- await userEvent.click(getByRole("option"));
+ userEvent.click(getByRole("option"));
expect(onChange).toHaveBeenCalledWith({ value: "Andorra" });
expect(queryByRole("listbox")).toBeFalsy();
});
- test("Autosuggest — Pattern constraint", async () => {
+ test("Autosuggest — Pattern constraint", () => {
const onChange = jest.fn();
const onBlur = jest.fn();
const { getByRole, getByText } = render(
@@ -641,20 +683,26 @@ describe("TextInput component synchronous autosuggest tests", () => {
);
const input = getByRole("combobox");
fireEvent.focus(input);
- await act(async () => {
+ act(() => {
userEvent.type(input, "Andor");
});
expect(getByText("Andor")).toBeTruthy();
expect(getByText("ra")).toBeTruthy();
- await act(async () => {
+ act(() => {
userEvent.click(getByRole("option"));
});
- expect(onChange).toHaveBeenCalledWith({ value: "Andorra", error: "Please match the format requested." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "Andorra",
+ error: "Please match the format requested.",
+ });
fireEvent.blur(input);
- expect(onBlur).toHaveBeenCalledWith({ value: "Andorra", error: "Please match the format requested." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "Andorra",
+ error: "Please match the format requested.",
+ });
});
- test("Autosuggest — Length constraint", async () => {
+ test("Autosuggest — Length constraint", () => {
const onChange = jest.fn();
const onBlur = jest.fn();
const { getByText, getByRole } = render(
@@ -669,17 +717,23 @@ describe("TextInput component synchronous autosuggest tests", () => {
);
const input = getByRole("combobox");
fireEvent.focus(input);
- await act(async () => {
+ act(() => {
userEvent.type(input, "Cha");
});
expect(getByText("Cha")).toBeTruthy();
expect(getByText("d")).toBeTruthy();
- await act(async () => {
+ act(() => {
userEvent.click(getByRole("option"));
});
- expect(onChange).toHaveBeenCalledWith({ value: "Cha", error: "Min length 5, max length 10." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "Cha",
+ error: "Min length 5, max length 10.",
+ });
fireEvent.blur(input);
- expect(onBlur).toHaveBeenCalledWith({ value: "Chad", error: "Min length 5, max length 10." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "Chad",
+ error: "Min length 5, max length 10.",
+ });
});
test("Autosuggest keys: arrow down key opens autosuggest, active first option is selected with Enter and closes the autosuggest", () => {
@@ -688,10 +742,20 @@ describe("TextInput component synchronous autosuggest tests", () => {
);
const input = getByRole("combobox") as HTMLInputElement;
- fireEvent.keyDown(input, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(input, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
const list = getByRole("listbox");
expect(list).toBeTruthy();
- fireEvent.keyDown(input, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(input, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(input.value).toBe("Afghanistan");
expect(queryByRole("list")).toBeFalsy();
});
@@ -702,10 +766,20 @@ describe("TextInput component synchronous autosuggest tests", () => {
);
const input = getByRole("combobox") as HTMLInputElement;
- fireEvent.keyDown(input, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(input, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
const list = getByRole("listbox");
expect(list).toBeTruthy();
- fireEvent.keyDown(input, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(input, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(input.value).toBe("Djibouti");
expect(queryByRole("list")).toBeFalsy();
});
@@ -720,7 +794,12 @@ describe("TextInput component synchronous autosuggest tests", () => {
userEvent.type(input, "Bangla");
const list = getByRole("listbox");
expect(list).toBeTruthy();
- fireEvent.keyDown(input, { key: "Esc", code: "Esc", keyCode: 27, charCode: 27 });
+ fireEvent.keyDown(input, {
+ key: "Esc",
+ code: "Esc",
+ keyCode: 27,
+ charCode: 27,
+ });
expect(input.value).toBe("");
expect(queryByRole("listbox")).toBeFalsy();
});
@@ -734,28 +813,58 @@ describe("TextInput component synchronous autosuggest tests", () => {
fireEvent.focus(input);
const list = getByRole("listbox");
expect(list).toBeTruthy();
- fireEvent.keyDown(input, { key: "Enter", code: "Enter", keyCode: 27, charCode: 27 });
+ fireEvent.keyDown(input, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 27,
+ charCode: 27,
+ });
expect(input.value).toBe("");
expect(queryByRole("list")).toBeFalsy();
});
- test("Autosuggest complex key sequence: write, arrow up two times, arrow down and select with Enter. Then, clean with Esc.", async () => {
+ test("Autosuggest complex key sequence: write, arrow up two times, arrow down and select with Enter. Then, clean with Esc.", () => {
const onChange = jest.fn();
const { getByRole, queryByRole } = render(
);
const input = getByRole("combobox") as HTMLInputElement;
fireEvent.focus(input);
- await act(async () => {
+ act(() => {
userEvent.type(input, "Ba");
});
- fireEvent.keyDown(input, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(input, { key: "ArrowUp", code: "ArrowUpp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(input, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
- fireEvent.keyDown(input, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(input, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(input, {
+ key: "ArrowUp",
+ code: "ArrowUpp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(input, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
+ fireEvent.keyDown(input, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(input.value).toBe("Barbados");
expect(queryByRole("listbox")).toBeFalsy();
- fireEvent.keyDown(input, { key: "Esc", code: "Esp", keyCode: 27, charCode: 27 });
+ fireEvent.keyDown(input, {
+ key: "Esc",
+ code: "Esc",
+ keyCode: 27,
+ charCode: 27,
+ });
expect(input.value).toBe("");
expect(queryByRole("listbox")).toBeFalsy();
});
@@ -800,16 +909,16 @@ describe("TextInput component synchronous autosuggest tests", () => {
describe("TextInput component asynchronous autosuggest tests", () => {
test("Autosuggest 'Searching...' message is shown", async () => {
- const callbackFunc = jest.fn((newValue) => {
- const result = new Promise((resolve) =>
- setTimeout(() => {
- resolve(
- newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
- );
- }, 100)
- );
- return result;
- });
+ const callbackFunc = jest.fn(
+ (newValue: string) =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
+ );
+ }, 100);
+ })
+ );
const onChange = jest.fn();
const { getByRole, getByText } = render(
@@ -824,27 +933,37 @@ describe("TextInput component asynchronous autosuggest tests", () => {
expect(getByText("Albania")).toBeTruthy();
expect(getByText("Algeria")).toBeTruthy();
expect(getByText("Andorra")).toBeTruthy();
- await act(async () => {
+ act(() => {
userEvent.type(input, "Ab");
});
await waitForElementToBeRemoved(() => getByText("Searching..."));
expect(getByText("Cabo Verde")).toBeTruthy();
- fireEvent.keyDown(input, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
- fireEvent.keyDown(input, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 });
+ fireEvent.keyDown(input, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
+ fireEvent.keyDown(input, {
+ key: "Enter",
+ code: "Enter",
+ keyCode: 13,
+ charCode: 13,
+ });
expect(input.value).toBe("Cabo Verde");
});
test("Autosuggest Esc key works while 'Searching...' message is shown", () => {
- const callbackFunc = jest.fn((newValue) => {
- const result = new Promise((resolve) =>
- setTimeout(() => {
- resolve(
- newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
- );
- }, 100)
- );
- return result;
- });
+ const callbackFunc = jest.fn(
+ (newValue: string) =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
+ );
+ }, 100);
+ })
+ );
const onChange = jest.fn();
const { getByRole, getByText, queryByText, queryByRole } = render(
@@ -853,23 +972,28 @@ describe("TextInput component asynchronous autosuggest tests", () => {
fireEvent.focus(input);
expect(getByText("Searching...")).toBeTruthy();
userEvent.type(input, "Ab");
- fireEvent.keyDown(input, { key: "Esc", code: "Esc", keyCode: 27, charCode: 27 });
+ fireEvent.keyDown(input, {
+ key: "Esc",
+ code: "Esc",
+ keyCode: 27,
+ charCode: 27,
+ });
expect(queryByRole("listbox")).toBeFalsy();
expect(queryByText("Searching...")).toBeFalsy();
expect(input.value).toBe("");
});
test("Autosuggest Esc + arrow down working while 'Searching...' message is shown", async () => {
- const callbackFunc = jest.fn((newValue) => {
- const result = new Promise((resolve) =>
- setTimeout(() => {
- resolve(
- newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
- );
- }, 100)
- );
- return result;
- });
+ const callbackFunc = jest.fn(
+ (newValue: string) =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
+ );
+ }, 100);
+ })
+ );
const onChange = jest.fn();
const { getByRole, getByText, queryByText, queryByRole } = render(
@@ -878,7 +1002,12 @@ describe("TextInput component asynchronous autosuggest tests", () => {
fireEvent.focus(input);
expect(getByText("Searching...")).toBeTruthy();
userEvent.type(input, "Ab");
- fireEvent.keyDown(input, { key: "Esc", code: "Esc", keyCode: 27, charCode: 27 });
+ fireEvent.keyDown(input, {
+ key: "Esc",
+ code: "Esc",
+ keyCode: 27,
+ charCode: 27,
+ });
expect(queryByRole("listbox")).toBeFalsy();
expect(queryByText("Searching...")).toBeFalsy();
expect(input.value).toBe("");
@@ -892,16 +1021,16 @@ describe("TextInput component asynchronous autosuggest tests", () => {
});
test("Asynchronous uncontrolled autosuggest test", async () => {
- const callbackFunc = jest.fn((newValue) => {
- const result = new Promise((resolve) =>
- setTimeout(() => {
- resolve(
- newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
- );
- }, 100)
- );
- return result;
- });
+ const callbackFunc = jest.fn(
+ (newValue: string) =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
+ );
+ }, 100);
+ })
+ );
const onChange = jest.fn();
const { getByRole, getByText } = render(
@@ -911,55 +1040,55 @@ describe("TextInput component asynchronous autosuggest tests", () => {
userEvent.type(input, "Den");
await waitForElementToBeRemoved(() => getByText("Searching..."));
expect(getByText("Denmark")).toBeTruthy();
- await userEvent.click(getByRole("option"));
+ userEvent.click(getByRole("option"));
expect(onChange).toHaveBeenCalledWith({ value: "Denmark" });
expect(input.value).toBe("Denmark");
});
test("Asynchronous controlled autosuggest test", async () => {
- const callbackFunc = jest.fn((newValue) => {
- const result = new Promise((resolve) =>
- setTimeout(() => {
- resolve(
- newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
- );
- }, 100)
- );
- return result;
- });
+ const callbackFunc = jest.fn(
+ (newValue: string) =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
+ );
+ }, 100);
+ })
+ );
const onChange = jest.fn();
const { getByRole, getByText, queryByRole } = render(
);
const input = getByRole("combobox") as HTMLInputElement;
expect(input.value).toBe("Denm");
- await userEvent.click(getByText("Autosuggest Countries"));
+ userEvent.click(getByText("Autosuggest Countries"));
await waitForElementToBeRemoved(() => getByText("Searching..."));
expect(getByText("Denmark")).toBeTruthy();
fireEvent.focus(getByRole("option"));
- await userEvent.click(getByText("Denmark"));
+ userEvent.click(getByText("Denmark"));
expect(onChange).toHaveBeenCalledWith({ value: "Denmark" });
expect(queryByRole("listbox")).toBeFalsy();
});
test("Asynchronous autosuggest closes the listbox after finishing no matches search", async () => {
- const callbackFunc = jest.fn((newValue) => {
- const result = new Promise((resolve) =>
- setTimeout(() => {
- resolve(
- newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
- );
- }, 100)
- );
- return result;
- });
+ const callbackFunc = jest.fn(
+ (newValue: string) =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
+ );
+ }, 100);
+ })
+ );
const onChange = jest.fn();
const { getByText, getByRole, queryByRole } = render(
);
const input = getByRole("combobox");
fireEvent.focus(input);
- await act(async () => {
+ act(() => {
userEvent.type(input, "Example text");
});
await waitForElementToBeRemoved(() => getByText("Searching..."));
@@ -967,16 +1096,16 @@ describe("TextInput component asynchronous autosuggest tests", () => {
});
test("Asynchronous autosuggest with no matches founded doesn't let the listbox to be opened", async () => {
- const callbackFunc = jest.fn((newValue) => {
- const result = new Promise((resolve) =>
- setTimeout(() => {
- resolve(
- newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
- );
- }, 100)
- );
- return result;
- });
+ const callbackFunc = jest.fn(
+ (newValue: string) =>
+ new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(
+ newValue ? countries.filter((option) => option.toUpperCase().includes(newValue.toUpperCase())) : countries
+ );
+ }, 100);
+ })
+ );
const onChange = jest.fn();
const { getByText, getByRole, queryByRole } = render(
@@ -988,21 +1117,31 @@ describe("TextInput component asynchronous autosuggest tests", () => {
expect(queryByRole("listbox")).toBeFalsy();
fireEvent.focus(input);
expect(queryByRole("listbox")).toBeFalsy();
- fireEvent.keyDown(input, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 });
+ fireEvent.keyDown(input, {
+ key: "ArrowUp",
+ code: "ArrowUp",
+ keyCode: 38,
+ charCode: 38,
+ });
expect(queryByRole("listbox")).toBeFalsy();
- fireEvent.keyDown(input, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 });
+ fireEvent.keyDown(input, {
+ key: "ArrowDown",
+ code: "ArrowDown",
+ keyCode: 40,
+ charCode: 40,
+ });
expect(queryByRole("listbox")).toBeFalsy();
});
test("Asynchronous autosuggest request failed, shows 'Error fetching data' message", async () => {
- const errorCallbackFunc = jest.fn(() => {
- const result = new Promise((resolve, reject) =>
- setTimeout(() => {
- reject("err");
- }, 100)
- );
- return result;
- });
+ const errorCallbackFunc = jest.fn(
+ () =>
+ new Promise((resolve, reject) => {
+ setTimeout(() => {
+ reject(new Error("err"));
+ }, 100);
+ })
+ );
const onChange = jest.fn();
const { getByRole, getByText } = render(
diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx
index 9f685bccd2..579f765957 100644
--- a/packages/lib/src/text-input/TextInput.tsx
+++ b/packages/lib/src/text-input/TextInput.tsx
@@ -5,6 +5,7 @@ import {
forwardRef,
KeyboardEvent,
MouseEvent,
+ ReactNode,
useContext,
useEffect,
useId,
@@ -33,7 +34,7 @@ import {
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 inputStylesByState from "../styles/forms/inputStylesByState";
const TextInputContainer = styled.div<{
margin: TextInputPropsType["margin"];
@@ -153,6 +154,50 @@ const DxcTextInput = forwardRef(
const [visualFocusIndex, changeVisualFocusIndex] = useState(-1);
const width = useWidth(inputContainerRef);
+ const autosuggestWrapperFunction = (children: ReactNode) => (
+ 0 || isSearching || isAutosuggestError)}>
+
+ {children}
+
+
+ {
+ // Avoid select to lose focus when the list is closed
+ event.preventDefault();
+ }}
+ onOpenAutoFocus={(event) => {
+ // Avoid select to lose focus when the list is opened
+ event.preventDefault();
+ }}
+ sideOffset={4}
+ style={{ zIndex: "var(--z-textinput)" }}
+ >
+ {
+ changeValue(suggestion);
+ closeSuggestions();
+ }}
+ suggestions={filteredSuggestions}
+ styles={{ width }}
+ value={value ?? innerValue}
+ visualFocusIndex={visualFocusIndex}
+ />
+
+
+
+ );
+
const getNumberErrorMessage = (checkedValue: number) =>
numberInputContext?.minNumber != null && checkedValue < numberInputContext?.minNumber
? translatedLabels.numberInput.valueGreaterThanOrEqualToErrorMessage?.(numberInputContext.minNumber)
@@ -410,7 +455,7 @@ const DxcTextInput = forwardRef(
changeIsAutosuggestError(false);
changeFilteredSuggestions(promiseResponse);
})
- .catch((err) => {
+ .catch((err: Error) => {
if (err.message !== "Is canceled") {
changeIsSearching(false);
changeIsAutosuggestError(true);
@@ -435,7 +480,6 @@ const DxcTextInput = forwardRef(
numberInputContext.stepNumber
);
}
- return undefined;
}, [value, innerValue, suggestions, numberInputContext]);
return (
@@ -450,52 +494,7 @@ const DxcTextInput = forwardRef(
{helperText}
)}
- (
- 0 || isSearching || isAutosuggestError)}>
-
- {children}
-
-
- {
- // Avoid select to lose focus when the list is closed
- event.preventDefault();
- }}
- onOpenAutoFocus={(event) => {
- // Avoid select to lose focus when the list is opened
- event.preventDefault();
- }}
- sideOffset={4}
- style={{ zIndex: "var(--z-textinput)" }}
- >
- {
- changeValue(suggestion);
- closeSuggestions();
- }}
- suggestions={filteredSuggestions}
- styles={{ width }}
- value={value ?? innerValue}
- visualFocusIndex={visualFocusIndex}
- />
-
-
-
- )}
- >
+
(
}
);
+DxcTextInput.displayName = "DxcTextInput";
+
export default DxcTextInput;
diff --git a/packages/lib/src/text-input/types.ts b/packages/lib/src/text-input/types.ts
index 5a14901e54..2444e036ae 100644
--- a/packages/lib/src/text-input/types.ts
+++ b/packages/lib/src/text-input/types.ts
@@ -166,7 +166,7 @@ type Props = {
};
/**
- * List of suggestions of an Text Input component.
+ * List of suggestions of a Text Input component.
*/
export type SuggestionsProps = {
id: string;
@@ -186,7 +186,7 @@ export type SuggestionsProps = {
export type RefType = HTMLDivElement;
/**
- * Single suggestion of an Text Input component.
+ * Single suggestion of a Text Input component.
*/
export type SuggestionProps = {
id: string;
diff --git a/packages/lib/src/text-input/utils.ts b/packages/lib/src/text-input/utils.ts
index 3e7bb102f7..202e0f56c0 100644
--- a/packages/lib/src/text-input/utils.ts
+++ b/packages/lib/src/text-input/utils.ts
@@ -17,8 +17,22 @@ export const makeCancelable = (promise: Promise) => {
let hasCanceled_ = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
- (val) => (hasCanceled_ ? reject(Error("Is canceled")) : resolve(val)),
- (promiseError) => (hasCanceled_ ? reject(Error("Is canceled")) : reject(promiseError))
+ (val) => {
+ if (hasCanceled_) {
+ reject(new Error("Is canceled"));
+ } else {
+ resolve(val);
+ }
+ },
+ (promiseError) => {
+ if (hasCanceled_) {
+ reject(new Error("Is canceled"));
+ } else if (promiseError instanceof Error) {
+ reject(promiseError);
+ } else {
+ reject(new Error(String(promiseError)));
+ }
+ }
);
});
return {
@@ -57,8 +71,10 @@ export const transformSpecialChars = (str: string) => {
const regexAsString = specialCharsRegex.toString().split("");
const uniqueSpecialChars = regexAsString.filter((item, index) => regexAsString.indexOf(item) === index);
uniqueSpecialChars.forEach((specialChar) => {
- if (str.includes(specialChar)) value = value.replace(specialChar, "\\" + specialChar);
+ if (str.includes(specialChar)) {
+ value = value.replace(specialChar, `\\${specialChar}`);
+ }
});
}
return value;
-};
\ No newline at end of file
+};
diff --git a/packages/lib/src/textarea/Textarea.test.tsx b/packages/lib/src/textarea/Textarea.test.tsx
index 2a01439207..89a315c53c 100644
--- a/packages/lib/src/textarea/Textarea.test.tsx
+++ b/packages/lib/src/textarea/Textarea.test.tsx
@@ -86,10 +86,10 @@ describe("Textarea component tests", () => {
expect(onChange).not.toHaveBeenCalled();
});
- test("Read-only textarea sends its value on submit", async () => {
- const handlerOnSubmit = jest.fn((e) => {
+ test("Read-only textarea sends its value on submit", () => {
+ const handlerOnSubmit = jest.fn((e: React.FormEvent) => {
e.preventDefault();
- const formData = new FormData(e.target);
+ const formData = new FormData(e.currentTarget);
const formProps = Object.fromEntries(formData);
expect(formProps).toStrictEqual({ data: "Comments" });
});
@@ -100,7 +100,7 @@ describe("Textarea component tests", () => {
);
const submit = getByText("Submit");
- await userEvent.click(submit);
+ userEvent.click(submit);
expect(handlerOnSubmit).toHaveBeenCalled();
});
@@ -114,7 +114,10 @@ describe("Textarea component tests", () => {
fireEvent.focus(textarea);
fireEvent.blur(textarea);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "",
+ error: "This field is required. Please, enter a value.",
+ });
fireEvent.change(textarea, { target: { value: "Test" } });
fireEvent.blur(textarea);
expect(onBlur).toHaveBeenCalled();
@@ -133,7 +136,10 @@ describe("Textarea component tests", () => {
expect(onChange).toHaveBeenCalledWith({ value: "Test" });
userEvent.clear(textarea);
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "",
+ error: "This field is required. Please, enter a value.",
+ });
});
test("Pattern constraint", () => {
@@ -152,10 +158,16 @@ describe("Textarea component tests", () => {
const textarea = getByLabelText("Example label");
fireEvent.change(textarea, { target: { value: "pattern test" } });
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "pattern test", error: "Please match the format requested." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "pattern test",
+ error: "Please match the format requested.",
+ });
fireEvent.blur(textarea);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "pattern test", error: "Please match the format requested." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "pattern test",
+ error: "Please match the format requested.",
+ });
userEvent.clear(textarea);
fireEvent.change(textarea, { target: { value: "pattern4&" } });
expect(onChange).toHaveBeenCalled();
@@ -182,10 +194,16 @@ describe("Textarea component tests", () => {
const textarea = getByLabelText("Example label");
fireEvent.change(textarea, { target: { value: "test" } });
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "test", error: "Min length 5, max length 10." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "test",
+ error: "Min length 5, max length 10.",
+ });
fireEvent.blur(textarea);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "test", error: "Min length 5, max length 10." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "test",
+ error: "Min length 5, max length 10.",
+ });
userEvent.clear(textarea);
fireEvent.change(textarea, { target: { value: "length" } });
expect(onChange).toHaveBeenCalled();
@@ -213,16 +231,28 @@ describe("Textarea component tests", () => {
const textarea = getByLabelText("Example label");
fireEvent.change(textarea, { target: { value: "test" } });
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "test", error: "Min length 5, max length 10." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "test",
+ error: "Min length 5, max length 10.",
+ });
fireEvent.blur(textarea);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "test", error: "Min length 5, max length 10." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "test",
+ error: "Min length 5, max length 10.",
+ });
fireEvent.change(textarea, { target: { value: "tests" } });
expect(onChange).toHaveBeenCalled();
- expect(onChange).toHaveBeenCalledWith({ value: "tests", error: "Please match the format requested." });
+ expect(onChange).toHaveBeenCalledWith({
+ value: "tests",
+ error: "Please match the format requested.",
+ });
fireEvent.blur(textarea);
expect(onBlur).toHaveBeenCalled();
- expect(onBlur).toHaveBeenCalledWith({ value: "tests", error: "Please match the format requested." });
+ expect(onBlur).toHaveBeenCalledWith({
+ value: "tests",
+ error: "Please match the format requested.",
+ });
fireEvent.change(textarea, { target: { value: "tests4&" } });
expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith({ value: "tests4&" });
diff --git a/packages/lib/src/textarea/Textarea.tsx b/packages/lib/src/textarea/Textarea.tsx
index 4f2684bcf9..c2013092fc 100644
--- a/packages/lib/src/textarea/Textarea.tsx
+++ b/packages/lib/src/textarea/Textarea.tsx
@@ -4,11 +4,11 @@ import { getMargin } from "../common/utils";
import { spaces } from "../common/variables";
import { HalstackLanguageContext } from "../HalstackContext";
import TextareaPropsType, { RefType } from "./types";
-import { scrollbarStyles } from "../styles/scroll";
+import scrollbarStyles from "../styles/scroll";
import ErrorMessage from "../styles/forms/ErrorMessage";
import Label from "../styles/forms/Label";
import HelperText from "../styles/forms/HelperText";
-import { inputStylesByState } from "../styles/forms/inputStylesByState";
+import inputStylesByState from "../styles/forms/inputStylesByState";
const sizes = {
small: "240px",
@@ -198,4 +198,6 @@ const DxcTextarea = forwardRef(
}
);
+DxcTextarea.displayName = "DxcTextarea";
+
export default DxcTextarea;
diff --git a/packages/lib/src/toast/Toast.accessibility.test.tsx b/packages/lib/src/toast/Toast.accessibility.test.tsx
index d89d603fee..6ba0f8f7f0 100644
--- a/packages/lib/src/toast/Toast.accessibility.test.tsx
+++ b/packages/lib/src/toast/Toast.accessibility.test.tsx
@@ -1,10 +1,10 @@
import { render } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
import { axe } from "../../test/accessibility/axe-helper";
import DxcToast from "./Toast";
import DxcToastsQueue from "./ToastsQueue";
import useToast from "./useToast";
import DxcButton from "../button/Button";
-import userEvent from "@testing-library/user-event";
const actionIcon = {
label: "Action",
@@ -37,7 +37,9 @@ describe("Toast component accessibility tests", () => {
const { container } = render();
const results = await axe(container);
const button = container.querySelector("button");
- button && userEvent.click(button);
+ if (button) {
+ userEvent.click(button);
+ }
expect(results).toHaveNoViolations();
});
it("Should not have basic accessibility issues", async () => {
diff --git a/packages/lib/src/toast/Toast.stories.tsx b/packages/lib/src/toast/Toast.stories.tsx
index 8d2e0e234b..47c0612e1f 100644
--- a/packages/lib/src/toast/Toast.stories.tsx
+++ b/packages/lib/src/toast/Toast.stories.tsx
@@ -1,4 +1,6 @@
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
+import { INITIAL_VIEWPORTS } from "@storybook/addon-viewport";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcButton from "../button/Button";
@@ -6,8 +8,6 @@ import DxcFlex from "../flex/Flex";
import DxcToast from "./Toast";
import DxcToastsQueue from "./ToastsQueue";
import useToast from "./useToast";
-import { INITIAL_VIEWPORTS } from "@storybook/addon-viewport";
-import { Meta, StoryObj } from "@storybook/react";
import DxcDialog from "../dialog/Dialog";
import DxcInset from "../inset/Inset";
import { screen } from "@testing-library/react";
@@ -245,7 +245,7 @@ const Screens = () => {
toast.success({
message:
"This is another very long label for a Toast. Please, always try to avoid this king of messages, be brief and concise.",
- action: action,
+ action,
});
}}
/>
diff --git a/packages/lib/src/toast/Toast.test.tsx b/packages/lib/src/toast/Toast.test.tsx
index ab2f39b3cb..79709a7bc4 100644
--- a/packages/lib/src/toast/Toast.test.tsx
+++ b/packages/lib/src/toast/Toast.test.tsx
@@ -1,9 +1,8 @@
+import { act, render, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import DxcButton from "../button/Button";
import DxcToastsQueue from "./ToastsQueue";
import useToast from "./useToast";
-import { render, waitFor } from "@testing-library/react";
-import { act } from "@testing-library/react";
const ToastPage = ({ onClick }: { onClick?: () => void }) => {
const toast = useToast();
@@ -34,9 +33,11 @@ const ToastPage = ({ onClick }: { onClick?: () => void }) => {
{
- onClick
- ? toast.default({ message: "This is a simple toast.", action: { label: "Action", onClick } })
- : toast.default({ message: "This is a simple toast." });
+ if (onClick) {
+ toast.default({ message: "This is a simple toast.", action: { label: "Action", onClick } });
+ } else {
+ toast.default({ message: "This is a simple toast." });
+ }
}}
/>
@@ -57,7 +58,7 @@ describe("Toast component tests", () => {
expect(getByText("This is a simple toast.")).toBeTruthy();
});
});
- test("Toast disappears after the specified duration", async () => {
+ test("Toast disappears after the specified duration", () => {
jest.useFakeTimers();
const { getByText, queryByText } = render(
@@ -79,7 +80,7 @@ describe("Toast component tests", () => {
jest.useRealTimers();
});
- test("If duration > 5000, the toast disappears at 5000ms", async () => {
+ test("If duration > 5000, the toast disappears at 5000ms", () => {
jest.useFakeTimers();
const { getByText, queryByText } = render(
@@ -96,7 +97,7 @@ describe("Toast component tests", () => {
jest.useRealTimers();
});
- test("If duration < 3000, the toast disappears at 3000ms", async () => {
+ test("If duration < 3000, the toast disappears at 3000ms", () => {
jest.useFakeTimers();
const { getByText, queryByText } = render(
@@ -164,7 +165,7 @@ describe("Toast component tests", () => {
const defaultBtn = getByText("Show toast");
userEvent.click(infoBtn);
- waitFor(() => {
+ await waitFor(() => {
expect(getByText("This is an information toast.")).toBeTruthy();
});
for (let i = 0; i < 6; i++) {
@@ -175,7 +176,7 @@ describe("Toast component tests", () => {
expect(getAllByText("This is a simple toast.").length).toBe(5);
});
});
- test("Loading toast is never removed automatically", async () => {
+ test("Loading toast is never removed automatically", () => {
jest.useFakeTimers();
const { getByText } = render(
diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx
index 760fb5abc3..4b654494c4 100644
--- a/packages/lib/src/toast/Toast.tsx
+++ b/packages/lib/src/toast/Toast.tsx
@@ -4,9 +4,9 @@ import styled from "@emotion/styled";
import DxcActionIcon from "../action-icon/ActionIcon";
import DxcButton from "../button/Button";
import DxcFlex from "../flex/Flex";
+import { HalstackLanguageContext } from "../HalstackContext";
import ToastPropsType from "./types";
import useTimeout from "../utils/useTimeout";
-import { HalstackLanguageContext } from "../HalstackContext";
import { responsiveSizes } from "../common/variables";
import getSemantic from "./utils";
import ToastIcon from "./ToastIcon";
@@ -188,4 +188,6 @@ const DxcToast = ({
);
};
+DxcToast.displayName = "DxcToast";
+
export default memo(DxcToast);
diff --git a/packages/lib/src/toast/ToastIcon.tsx b/packages/lib/src/toast/ToastIcon.tsx
index 8cc77d0d8a..4e1652d51b 100644
--- a/packages/lib/src/toast/ToastIcon.tsx
+++ b/packages/lib/src/toast/ToastIcon.tsx
@@ -17,4 +17,6 @@ const ToastIcon = memo(
}
);
+ToastIcon.displayName = "ToastIcon";
+
export default ToastIcon;
diff --git a/packages/lib/src/toast/types.ts b/packages/lib/src/toast/types.ts
index 4fe4a92bbf..c38c156070 100644
--- a/packages/lib/src/toast/types.ts
+++ b/packages/lib/src/toast/types.ts
@@ -28,8 +28,8 @@ type CommonProps = {
};
type DefaultToast = CommonProps & {
/**
- * Material Symbol name or SVG element as the icon that will be placed next to the panel label.
- * When using Material Symbols, replace spaces with underscores.
+ * Material Symbol name or SVG element as the icon that will be placed next to the panel label.
+ * When using Material Symbols, replace spaces with underscores.
* By default they are outlined if you want it to be filled prefix the symbol name with "filled_".
*/
icon?: string | SVG;
@@ -71,7 +71,7 @@ type ToastsQueuePropsType = {
*/
children: ReactNode;
/**
- * Duration in milliseconds before a toast automatically hides itself.
+ * Duration in milliseconds before a toast automatically hides itself.
* The range goes from 3000ms to 5000ms, any other value will not be taken into consideration.
*/
duration?: number;
diff --git a/packages/lib/src/toast/useToast.tsx b/packages/lib/src/toast/useToast.tsx
index de5f5e1eac..bb75002b9d 100644
--- a/packages/lib/src/toast/useToast.tsx
+++ b/packages/lib/src/toast/useToast.tsx
@@ -11,10 +11,10 @@ export default function useToast() {
success: (toast: SemanticToast) => add?.(toast, "success"),
warning: (toast: SemanticToast) => add?.(toast, "warning"),
info: (toast: SemanticToast) => add?.(toast, "info"),
- loading: (toast: Omit) => add?.({ ...toast, loading: true } as LoadingToast, "info"),
+ loading: (toast: Omit) => add?.({ ...toast, loading: true }, "info"),
}),
[add]
);
-
+
return toast;
}
diff --git a/packages/lib/src/toast/utils.ts b/packages/lib/src/toast/utils.ts
index 6718ee6344..4a3a648a02 100644
--- a/packages/lib/src/toast/utils.ts
+++ b/packages/lib/src/toast/utils.ts
@@ -29,12 +29,12 @@ export default function getSemantic(semantic: ToastPropsType["semantic"]) {
}
}
+const idExists = (id: string, toasts: QueuedToast[]) => toasts.some((toast) => toast.id === id);
+
export function generateUniqueToastId(toasts: QueuedToast[]) {
let id = "";
- let exists = true;
- while (exists) {
+ do {
id = `${performance.now()}-${Math.random().toString(36).slice(2, 9)}`;
- exists = toasts.some((toast) => toast.id === id);
- }
+ } while (idExists(id, toasts));
return id;
-};
+}
diff --git a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx
index c0916452ee..32c1b16c8e 100644
--- a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx
+++ b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx
@@ -1,7 +1,7 @@
+import { Meta, StoryObj } from "@storybook/react";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcToggleGroup from "./ToggleGroup";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Toggle Group",
diff --git a/packages/lib/src/toggle-group/ToggleGroup.test.tsx b/packages/lib/src/toggle-group/ToggleGroup.test.tsx
index 941434b249..2aa58d02c2 100644
--- a/packages/lib/src/toggle-group/ToggleGroup.test.tsx
+++ b/packages/lib/src/toggle-group/ToggleGroup.test.tsx
@@ -27,7 +27,11 @@ describe("Toggle group component tests", () => {
const { getByRole } = render(
);
@@ -51,10 +55,16 @@ describe("Toggle group component tests", () => {
const onChange = jest.fn();
const { getAllByRole } = render();
const toggleOptions = getAllByRole("button");
- toggleOptions[0] && fireEvent.click(toggleOptions[0]);
+ if (toggleOptions[0]) {
+ fireEvent.click(toggleOptions[0]);
+ }
expect(onChange).toHaveBeenCalledWith([1]);
- toggleOptions[1] && fireEvent.click(toggleOptions[1]);
- toggleOptions[3] && fireEvent.click(toggleOptions[3]);
+ if (toggleOptions[1]) {
+ fireEvent.click(toggleOptions[1]);
+ }
+ if (toggleOptions[3]) {
+ fireEvent.click(toggleOptions[3]);
+ }
expect(onChange).toHaveBeenCalledWith([1, 2, 4]);
expect(toggleOptions[0]?.getAttribute("aria-pressed")).toBe("true");
expect(toggleOptions[1]?.getAttribute("aria-pressed")).toBe("true");
diff --git a/packages/lib/src/tooltip/Tooltip.accessibility.test.tsx b/packages/lib/src/tooltip/Tooltip.accessibility.test.tsx
index 14170fe013..c09eaa708c 100644
--- a/packages/lib/src/tooltip/Tooltip.accessibility.test.tsx
+++ b/packages/lib/src/tooltip/Tooltip.accessibility.test.tsx
@@ -3,16 +3,11 @@ import { axe } from "../../test/accessibility/axe-helper";
import DxcButton from "../button/Button";
import DxcTooltip from "./Tooltip";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Tooltip component accessibility tests", () => {
it("Should not have basic accessibility issues for bottom position", async () => {
diff --git a/packages/lib/src/tooltip/Tooltip.stories.tsx b/packages/lib/src/tooltip/Tooltip.stories.tsx
index d11dbde965..c1963152ae 100644
--- a/packages/lib/src/tooltip/Tooltip.stories.tsx
+++ b/packages/lib/src/tooltip/Tooltip.stories.tsx
@@ -1,11 +1,11 @@
+import { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
+import DxcTooltip from "./Tooltip";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcButton from "../button/Button";
import DxcFlex from "../flex/Flex";
import DxcInset from "../inset/Inset";
-import DxcTooltip from "./Tooltip";
-import { Meta, StoryObj } from "@storybook/react";
export default {
title: "Tooltip",
diff --git a/packages/lib/src/tooltip/Tooltip.test.tsx b/packages/lib/src/tooltip/Tooltip.test.tsx
index 4aa79f1b27..11227c92b7 100644
--- a/packages/lib/src/tooltip/Tooltip.test.tsx
+++ b/packages/lib/src/tooltip/Tooltip.test.tsx
@@ -1,19 +1,14 @@
-import "@testing-library/jest-dom";
-import { render, screen, waitFor } from "@testing-library/react";
+import { render, waitFor, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
-import DxcButton from "../button/Button";
import DxcTooltip from "./Tooltip";
+import DxcButton from "../button/Button";
+import "@testing-library/jest-dom";
-// Mocking DOMRect for Radix Primitive Popover
-(global as any).globalThis = global;
-(global as any).DOMRect = {
- fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0, x: 0, y: 0 }),
-};
-(global as any).ResizeObserver = class ResizeObserver {
- observe() {}
- unobserve() {}
- disconnect() {}
-};
+global.ResizeObserver = jest.fn().mockImplementation(() => ({
+ observe: jest.fn(),
+ unobserve: jest.fn(),
+ disconnect: jest.fn(),
+}));
describe("Tooltip component tests", () => {
test("Tooltip does not render by default", async () => {
@@ -36,7 +31,8 @@ describe("Tooltip component tests", () => {
);
const triggerElement = getByText("Hoverable button");
userEvent.hover(triggerElement);
- await screen.findByRole("tooltip", { name: "Tooltip Test" });
+ const tooltipElement = await screen.findByRole("tooltip", { name: "Tooltip Test" });
+ expect(tooltipElement).toBeInTheDocument();
});
test("Tooltip stops being rendered when hover is stopped", async () => {
diff --git a/packages/lib/src/tooltip/Tooltip.tsx b/packages/lib/src/tooltip/Tooltip.tsx
index 8d055a2453..9f976b2b13 100644
--- a/packages/lib/src/tooltip/Tooltip.tsx
+++ b/packages/lib/src/tooltip/Tooltip.tsx
@@ -1,9 +1,8 @@
import styled from "@emotion/styled";
-import TooltipPropsType, { TooltipWrapperProps } from "./types";
import { useContext } from "react";
-import { Root, Trigger, Portal, Arrow, Content } from "@radix-ui/react-tooltip";
-import { Provider } from "@radix-ui/react-tooltip";
-import { TooltipContext } from "./TooltipContext";
+import { Root, Trigger, Portal, Arrow, Content, Provider } from "@radix-ui/react-tooltip";
+import TooltipPropsType, { TooltipWrapperProps } from "./types";
+import TooltipContext from "./TooltipContext";
const TooltipTriggerContainer = styled.div`
position: relative;
@@ -139,6 +138,6 @@ export const Tooltip = ({
export const TooltipWrapper = ({ condition, children, label }: TooltipWrapperProps) =>
condition ? {children} : <>{children}>;
-export default function DxcTooltip(props: TooltipPropsType) {
- return ;
-}
+const DxcTooltip = (props: TooltipPropsType) => ;
+
+export default DxcTooltip;
diff --git a/packages/lib/src/tooltip/TooltipContext.tsx b/packages/lib/src/tooltip/TooltipContext.tsx
index 04e597ce47..7452e352e1 100644
--- a/packages/lib/src/tooltip/TooltipContext.tsx
+++ b/packages/lib/src/tooltip/TooltipContext.tsx
@@ -1,3 +1,3 @@
import { createContext } from "react";
-export const TooltipContext = createContext(false);
+export default createContext(false);
diff --git a/packages/lib/src/tooltip/types.tsx b/packages/lib/src/tooltip/types.ts
similarity index 100%
rename from packages/lib/src/tooltip/types.tsx
rename to packages/lib/src/tooltip/types.ts
diff --git a/packages/lib/src/typography/types.ts b/packages/lib/src/typography/types.ts
index d686ea1812..d88b56257d 100644
--- a/packages/lib/src/typography/types.ts
+++ b/packages/lib/src/typography/types.ts
@@ -35,6 +35,6 @@ export type Props = {
whiteSpace?: "normal" | "nowrap" | "pre" | "pre-line" | "pre-wrap";
};
-export default Props;
-
export type TypographyContextProps = Required>;
+
+export default Props;
diff --git a/packages/lib/src/utils/FocusLock.tsx b/packages/lib/src/utils/FocusLock.tsx
index 7a7ed2df4f..bd45bd976d 100644
--- a/packages/lib/src/utils/FocusLock.tsx
+++ b/packages/lib/src/utils/FocusLock.tsx
@@ -19,15 +19,15 @@ const focusableQuery = [
`[tabindex]${not.negTabIndex}${not.disabled}`,
].join(",");
-const getFocusableElements = (container: HTMLElement): HTMLElement[] =>
- Array.prototype.slice
- .call(container.querySelectorAll(focusableQuery))
- .filter(
- (element: HTMLElement) =>
- element.getAttribute("aria-hidden") !== "true" &&
- window.getComputedStyle(element).display !== "none" &&
- window.getComputedStyle(element).visibility !== "hidden"
- );
+const getFocusableElements = (container: HTMLElement): HTMLElement[] => {
+ const elements = Array.from(container.querySelectorAll(focusableQuery));
+ return elements.filter(
+ (element) =>
+ element.getAttribute("aria-hidden") !== "true" &&
+ window.getComputedStyle(element).display !== "none" &&
+ window.getComputedStyle(element).visibility !== "hidden"
+ );
+};
/**
* This function will try to focus the element and return true if it was able to receive the focus.
@@ -46,11 +46,12 @@ const attemptFocus = (element: HTMLElement): boolean => {
* @returns boolean: true if element is contained inside a Radix Portal, false otherwise.
*/
const radixPortalContains = (activeElement: Node): boolean => {
- const radixPortals = document.querySelectorAll("[data-radix-portal]");
- const radixPoppers = document.querySelectorAll("[data-radix-popper-content-wrapper]");
+ const radixPortals = Array.from(document.querySelectorAll("[data-radix-portal]"));
+ const radixPoppers = Array.from(document.querySelectorAll("[data-radix-popper-content-wrapper]"));
+
return (
- Array.prototype.slice.call(radixPortals).some((portal) => portal.contains(activeElement)) ||
- Array.prototype.slice.call(radixPoppers).some((popper) => popper.contains(activeElement))
+ radixPortals.some((portal) => portal.contains(activeElement)) ||
+ radixPoppers.some((popper) => popper.contains(activeElement))
);
};
@@ -67,7 +68,9 @@ const useFocusableElements = (ref: MutableRefObject): HTM
setFocusableElements(getFocusableElements(ref.current));
const observer = new MutationObserver(() => {
- if (ref.current != null) setFocusableElements(getFocusableElements(ref.current));
+ if (ref.current != null) {
+ setFocusableElements(getFocusableElements(ref.current));
+ }
});
observer.observe(ref.current, { childList: true, subtree: true });
return () => {
@@ -94,8 +97,11 @@ const FocusLock = ({ children }: { children: ReactNode }): JSX.Element => {
const focusFirst = useCallback(() => {
if (focusableElements != null) {
- if (focusableElements.length === 0) childrenContainerRef.current?.focus();
- else if (focusableElements.length > 0) focusableElements.some((element) => attemptFocus(element));
+ if (focusableElements.length === 0) {
+ childrenContainerRef.current?.focus();
+ } else if (focusableElements.length > 0) {
+ focusableElements.some((element) => attemptFocus(element));
+ }
}
}, [focusableElements]);
@@ -107,7 +113,9 @@ const FocusLock = ({ children }: { children: ReactNode }): JSX.Element => {
};
const focusLock = (event: KeyboardEvent) => {
- if (event.key === "Tab" && focusableElements?.length === 0) event.preventDefault();
+ if (event.key === "Tab" && focusableElements?.length === 0) {
+ event.preventDefault();
+ }
};
useEffect(() => {
@@ -130,8 +138,9 @@ const FocusLock = ({ children }: { children: ReactNode }): JSX.Element => {
container?.previousElementSibling?.contains(target) ||
radixPortalContains(target)
)
- )
+ ) {
focusFirst();
+ }
};
document.addEventListener("focusout", focusGuardHandler);
diff --git a/packages/lib/src/wizard/Wizard.stories.tsx b/packages/lib/src/wizard/Wizard.stories.tsx
index 5271e88c31..1cbbf1bb43 100644
--- a/packages/lib/src/wizard/Wizard.stories.tsx
+++ b/packages/lib/src/wizard/Wizard.stories.tsx
@@ -1,8 +1,7 @@
-import { userEvent, within } from "@storybook/test";
+import { Meta, StoryObj } from "@storybook/react";
import ExampleContainer from "../../.storybook/components/ExampleContainer";
import Title from "../../.storybook/components/Title";
import DxcWizard from "./Wizard";
-import { Meta, StoryObj } from "@storybook/react";
import DxcContainer from "../container/Container";
export default {
diff --git a/packages/lib/src/wizard/Wizard.test.tsx b/packages/lib/src/wizard/Wizard.test.tsx
index d64d101ef7..83d14e9777 100644
--- a/packages/lib/src/wizard/Wizard.test.tsx
+++ b/packages/lib/src/wizard/Wizard.test.tsx
@@ -114,7 +114,7 @@ describe("Wizard components tests", () => {
});
test("Controlled wizard function is called", () => {
- const onClick = jest.fn((i) => i);
+ const onClick = jest.fn((_i: number) => {});
const { getAllByRole } = render(
{
/>
);
const steps = getAllByRole("button");
- steps[1] && fireEvent.click(steps[1]);
- steps[0] && fireEvent.click(steps[0]);
+ if (steps[1]) {
+ fireEvent.click(steps[1]);
+ }
+ if (steps[0]) {
+ fireEvent.click(steps[0]);
+ }
expect(onClick).toHaveBeenCalledTimes(2);
expect(onClick).toHaveBeenNthCalledWith(1, 1);
expect(onClick).toHaveBeenNthCalledWith(2, 0);
diff --git a/packages/lib/src/wizard/Wizard.tsx b/packages/lib/src/wizard/Wizard.tsx
index 2b523492c8..aa15ebfb89 100644
--- a/packages/lib/src/wizard/Wizard.tsx
+++ b/packages/lib/src/wizard/Wizard.tsx
@@ -6,6 +6,7 @@ import DxcIcon from "../icon/Icon";
import WizardPropsType, { StepProps } from "./types";
import DxcFlex from "../flex/Flex";
import icons from "./Icons";
+import { css } from "@emotion/react";
const Wizard = styled.div<{
margin: WizardPropsType["margin"];
@@ -103,13 +104,14 @@ const Step = styled.button<{
}
${({ unvisited }) =>
unvisited &&
- `${IconContainer} {
- border-color: var(--border-color-neutral-strongest);
- }
- ${IconContainer}, ${Number}, ${Label}, ${Description} {
- color: var(--color-fg-neutral-stronger);
- }
- `}
+ css`
+ ${IconContainer} {
+ border-color: var(--border-color-neutral-strongest);
+ }
+ ${IconContainer}, ${Number}, ${Label}, ${Description} {
+ color: var(--color-fg-neutral-stronger);
+ }
+ `}
&:focus:enabled {
outline: var(--border-width-m) var(--border-style-default) var(--border-color-secondary-medium);
}
diff --git a/packages/lib/test/accessibility/axe-helper.ts b/packages/lib/test/accessibility/axe-helper.ts
index 77b9611cfe..c20cb357ee 100644
--- a/packages/lib/test/accessibility/axe-helper.ts
+++ b/packages/lib/test/accessibility/axe-helper.ts
@@ -1,5 +1,5 @@
import { configureAxe } from "jest-axe";
-import { disabledRules } from "./rules/common/disabledRules";
+import disabledRules from "./rules/common/disabledRules";
export const formatRules = (rules: string[]) =>
rules.reduce(
diff --git a/packages/lib/test/accessibility/rules/common/disabledRules.ts b/packages/lib/test/accessibility/rules/common/disabledRules.ts
index 88e53db2be..ff2741a990 100644
--- a/packages/lib/test/accessibility/rules/common/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/common/disabledRules.ts
@@ -1,7 +1,7 @@
/**
* Array of accessibility rule IDs to be disabled in both Jest and Storybook.
*/
-export const disabledRules = [
+const disabledRules = [
// Disable heading order rule to prevent errors from using h2 and h4 in the titles of the stories
"heading-order",
// Disable autocomplete valid rule to prevent errors from "nope" which is used on purpose as an invalid autocomplete value
@@ -13,3 +13,5 @@ export const disabledRules = [
// TODO: REMOVE
"color-contrast",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/breadcrumbs/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/breadcrumbs/disabledRules.ts
index c37ae007fa..774eb12f92 100644
--- a/packages/lib/test/accessibility/rules/specific/breadcrumbs/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/breadcrumbs/disabledRules.ts
@@ -2,7 +2,9 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the breadcrumbs component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// Disable landmark unique valid rule to prevent errors from having multiple nav in the same page (that can happen in testing environments)
"landmark-unique",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/data-grid/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/data-grid/disabledRules.ts
index 52c467b57e..69c21fd7e2 100644
--- a/packages/lib/test/accessibility/rules/specific/data-grid/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/data-grid/disabledRules.ts
@@ -2,7 +2,9 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the data grid component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// Disable scrollable region focusable rule to prevent errors from having an empty header for the expandable data grids
"empty-table-header",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/date-input/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/date-input/disabledRules.ts
index 4c9b3b9bbc..728ef130ba 100644
--- a/packages/lib/test/accessibility/rules/specific/date-input/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/date-input/disabledRules.ts
@@ -2,8 +2,10 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the date input component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// TODO: Remove when the false positive is fixed
// Disable aria allowed rule to prevent false positive from gridcell role not being allowed in buttons
"aria-allowed-role",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/footer/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/footer/disabledRules.ts
index 942b53bfa7..0143d0556e 100644
--- a/packages/lib/test/accessibility/rules/specific/footer/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/footer/disabledRules.ts
@@ -2,9 +2,11 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the footer component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// Disable landmark duplicate content info rule to prevent errors from having multiple footers in the same page (that can happen in testing environments)
"landmark-no-duplicate-contentinfo",
// Disable landmark unique valid rule to prevent errors from having multiple footers in the same page (that can happen in testing environments)
"landmark-unique",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/header/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/header/disabledRules.ts
index c608dd0620..c864764443 100644
--- a/packages/lib/test/accessibility/rules/specific/header/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/header/disabledRules.ts
@@ -2,9 +2,11 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the header component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// Disable landmark duplicate banner rule to prevent errors from having multiple headers in the same page (that can happen in testing environments)
"landmark-no-duplicate-banner",
// Disable landmark unique valid rule to prevent errors from having multiple headers in the same page (that can happen in testing environments)
"landmark-unique",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/resultset-table/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/resultset-table/disabledRules.ts
index 0c8413f962..c8b0b11468 100644
--- a/packages/lib/test/accessibility/rules/specific/resultset-table/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/resultset-table/disabledRules.ts
@@ -2,8 +2,10 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the resultset table component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// TODO: Find a better solution
- // Disable scrollable region focusable rule to prevent errors from having scrollable tables with no focusable elements
+ // Disable scrollable region focusable rule to prevent errors from having scrollable tables with no focusable elements
"scrollable-region-focusable",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/select/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/select/disabledRules.ts
index 25ad380084..42852ed8a3 100644
--- a/packages/lib/test/accessibility/rules/specific/select/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/select/disabledRules.ts
@@ -2,8 +2,10 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the header component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// TODO: Work on nested interaction with the DxcCheckbox component to prevent these issues
"nested-interactive",
- "scrollable-region-focusable"
+ "scrollable-region-focusable",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/switch/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/switch/disabledRules.ts
index c0f57cafad..49fb875b9b 100644
--- a/packages/lib/test/accessibility/rules/specific/switch/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/switch/disabledRules.ts
@@ -2,7 +2,9 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the switch component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// Disable aria toggle field name rule to prevent errors from having switches with no label on purpose
"aria-toggle-field-name",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/accessibility/rules/specific/table/disabledRules.ts b/packages/lib/test/accessibility/rules/specific/table/disabledRules.ts
index e028827cf7..899019e395 100644
--- a/packages/lib/test/accessibility/rules/specific/table/disabledRules.ts
+++ b/packages/lib/test/accessibility/rules/specific/table/disabledRules.ts
@@ -2,8 +2,10 @@
* Array of accessibility rule IDs to be disabled in both Jest and Storybook for the table component.
*
*/
-export const disabledRules = [
+const disabledRules = [
// TODO: Find a better solution
- // Disable scrollable region focusable rule to prevent errors from having scrollable tables with no focusable elements
+ // Disable scrollable region focusable rule to prevent errors from having scrollable tables with no focusable elements
"scrollable-region-focusable",
];
+
+export default disabledRules;
diff --git a/packages/lib/test/mocks/domRectMock.ts b/packages/lib/test/mocks/domRectMock.ts
new file mode 100644
index 0000000000..1a8f57782e
--- /dev/null
+++ b/packages/lib/test/mocks/domRectMock.ts
@@ -0,0 +1,31 @@
+class MockDOMRect implements DOMRect {
+ x = 0;
+ y = 0;
+ width = 0;
+ height = 0;
+ top = 0;
+ left = 0;
+ bottom = 0;
+ right = 0;
+
+ constructor(x?: number, y?: number, width?: number, height?: number) {
+ this.x = x ?? 0;
+ this.y = y ?? 0;
+ this.width = width ?? 0;
+ this.height = height ?? 0;
+ this.top = this.y;
+ this.left = this.x;
+ this.bottom = this.y + this.height;
+ this.right = this.x + this.width;
+ }
+
+ toJSON() {
+ return {};
+ }
+
+ static fromRect(rect?: DOMRectInit) {
+ return new MockDOMRect(rect?.x, rect?.y, rect?.width, rect?.height);
+ }
+}
+
+export default MockDOMRect;
diff --git a/packages/lib/test/mocks/pngMock.js b/packages/lib/test/mocks/pngMock.js
deleted file mode 100644
index cc6e23970a..0000000000
--- a/packages/lib/test/mocks/pngMock.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = 'ImageMock';
\ No newline at end of file
diff --git a/packages/lib/test/mocks/pngMock.ts b/packages/lib/test/mocks/pngMock.ts
new file mode 100644
index 0000000000..6682097e5d
--- /dev/null
+++ b/packages/lib/test/mocks/pngMock.ts
@@ -0,0 +1 @@
+export default "ImageMock";
diff --git a/packages/lib/test/mocks/svgMock.js b/packages/lib/test/mocks/svgMock.js
deleted file mode 100644
index 948c11557d..0000000000
--- a/packages/lib/test/mocks/svgMock.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = 'IconMock';
\ No newline at end of file
diff --git a/packages/lib/test/mocks/svgMock.ts b/packages/lib/test/mocks/svgMock.ts
new file mode 100644
index 0000000000..6aaf141eba
--- /dev/null
+++ b/packages/lib/test/mocks/svgMock.ts
@@ -0,0 +1 @@
+export default "IconMock";
diff --git a/packages/lib/tsconfig.json b/packages/lib/tsconfig.json
index ad4b33f44e..4a49272b0c 100644
--- a/packages/lib/tsconfig.json
+++ b/packages/lib/tsconfig.json
@@ -6,5 +6,5 @@
"forceConsistentCasingInFileNames": true
},
"include": ["src", "test", ".", ".storybook/**/*"],
- "exclude": ["node_modules", "dist", ".turbo"]
+ "exclude": ["node_modules", "dist", "coverage", ".turbo"]
}
diff --git a/packages/lib/tsconfig.lint.json b/packages/lib/tsconfig.lint.json
index 422c64dbc2..d3855d5bea 100644
--- a/packages/lib/tsconfig.lint.json
+++ b/packages/lib/tsconfig.lint.json
@@ -1,8 +1,4 @@
{
- "extends": "@dxc-technology/typescript-config/react-library.json",
- "compilerOptions": {
- "outDir": "dist"
- },
- "include": ["src", "turbo"],
- "exclude": ["node_modules", "dist"]
+ "extends": "./tsconfig.json",
+ "exclude": ["node_modules", "dist", ".turbo", "coverage"]
}
diff --git a/packages/typescript-config/base.json b/packages/typescript-config/base.json
index a6d77a89f5..d8161a2219 100644
--- a/packages/typescript-config/base.json
+++ b/packages/typescript-config/base.json
@@ -13,6 +13,6 @@
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
- "target": "ES2022",
+ "target": "ES2022"
}
}
diff --git a/scripts/copy-readme.js b/scripts/copy-readme.js
index 06baa71f0d..dfc1f60540 100644
--- a/scripts/copy-readme.js
+++ b/scripts/copy-readme.js
@@ -1,3 +1,3 @@
-const fs = require('fs');
+const fs = require("fs");
-fs.createReadStream('../../README.md').pipe(fs.createWriteStream('../lib/README.md'));
\ No newline at end of file
+fs.createReadStream("../../README.md").pipe(fs.createWriteStream("../lib/README.md"));
diff --git a/scripts/create-version.js b/scripts/create-version.js
index af3736ab48..c383ab1c01 100644
--- a/scripts/create-version.js
+++ b/scripts/create-version.js
@@ -7,8 +7,7 @@ const setVersion = () => {
};
const jsonData = JSON.stringify(object);
const versionDirectory = "./catalog/version/";
- if (!fs.existsSync(versionDirectory))
- fs.mkdirSync(versionDirectory, { recursive: true });
+ if (!fs.existsSync(versionDirectory)) fs.mkdirSync(versionDirectory, { recursive: true });
fs.writeFile(`${versionDirectory}version.json`, jsonData, (err) => {
if (err) throw err;
});
diff --git a/scripts/package-lock.json b/scripts/package-lock.json
deleted file mode 100644
index ae5e700ad4..0000000000
--- a/scripts/package-lock.json
+++ /dev/null
@@ -1,233 +0,0 @@
-{
- "name": "@dxc-technology/halstack-react",
- "version": "0.0.0",
- "lockfileVersion": 1,
- "requires": true,
- "dependencies": {
- "available-typed-arrays": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
- "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
- },
- "aws-sdk": {
- "version": "2.1369.0",
- "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1369.0.tgz",
- "integrity": "sha512-DdCQjlhQDi9w8J4moqECrrp9ARWCay0UI38adPSS0GG43gh3bl3OoMlgKJ8aZxi4jUvzE48K9yhFHz4y/mazZw==",
- "requires": {
- "buffer": "4.9.2",
- "events": "1.1.1",
- "ieee754": "1.1.13",
- "jmespath": "0.16.0",
- "querystring": "0.2.0",
- "sax": "1.2.1",
- "url": "0.10.3",
- "util": "^0.12.4",
- "uuid": "8.0.0",
- "xml2js": "0.5.0"
- }
- },
- "base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
- },
- "buffer": {
- "version": "4.9.2",
- "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
- "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
- "requires": {
- "base64-js": "^1.0.2",
- "ieee754": "^1.1.4",
- "isarray": "^1.0.0"
- }
- },
- "call-bind": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
- "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
- "requires": {
- "function-bind": "^1.1.1",
- "get-intrinsic": "^1.0.2"
- }
- },
- "events": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
- "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw=="
- },
- "for-each": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
- "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
- "requires": {
- "is-callable": "^1.1.3"
- }
- },
- "function-bind": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
- "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
- },
- "get-intrinsic": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
- "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
- "requires": {
- "function-bind": "^1.1.1",
- "has": "^1.0.3",
- "has-symbols": "^1.0.3"
- }
- },
- "gopd": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
- "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
- "requires": {
- "get-intrinsic": "^1.1.3"
- }
- },
- "has": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
- "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
- "requires": {
- "function-bind": "^1.1.1"
- }
- },
- "has-symbols": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
- "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
- },
- "has-tostringtag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
- "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
- "requires": {
- "has-symbols": "^1.0.2"
- }
- },
- "ieee754": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
- "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
- },
- "is-arguments": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
- "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
- "requires": {
- "call-bind": "^1.0.2",
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-callable": {
- "version": "1.2.7",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
- "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="
- },
- "is-generator-function": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
- "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
- "requires": {
- "has-tostringtag": "^1.0.0"
- }
- },
- "is-typed-array": {
- "version": "1.1.10",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz",
- "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==",
- "requires": {
- "available-typed-arrays": "^1.0.5",
- "call-bind": "^1.0.2",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "has-tostringtag": "^1.0.0"
- }
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
- },
- "jmespath": {
- "version": "0.16.0",
- "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
- "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw=="
- },
- "punycode": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
- "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="
- },
- "querystring": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
- "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g=="
- },
- "sax": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
- "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="
- },
- "url": {
- "version": "0.10.3",
- "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
- "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==",
- "requires": {
- "punycode": "1.3.2",
- "querystring": "0.2.0"
- }
- },
- "util": {
- "version": "0.12.5",
- "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
- "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
- "requires": {
- "inherits": "^2.0.3",
- "is-arguments": "^1.0.4",
- "is-generator-function": "^1.0.7",
- "is-typed-array": "^1.1.3",
- "which-typed-array": "^1.1.2"
- }
- },
- "uuid": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz",
- "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw=="
- },
- "which-typed-array": {
- "version": "1.1.9",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
- "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==",
- "requires": {
- "available-typed-arrays": "^1.0.5",
- "call-bind": "^1.0.2",
- "for-each": "^0.3.3",
- "gopd": "^1.0.1",
- "has-tostringtag": "^1.0.0",
- "is-typed-array": "^1.1.10"
- }
- },
- "xml2js": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
- "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
- "requires": {
- "sax": ">=0.6.0",
- "xmlbuilder": "~11.0.0"
- }
- },
- "xmlbuilder": {
- "version": "11.0.1",
- "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
- "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
- }
- }
-}