From 313de0434aa4005bfbce56385b7030c101340661 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Tue, 3 Dec 2024 17:06:39 +0100 Subject: [PATCH 01/41] Added required changes to types in order to enable strict mode --- apps/website/pages/_app.tsx | 13 +- .../screens/common/HalstackMarkdownParser.tsx | 4 +- apps/website/screens/common/MainContent.tsx | 4 +- apps/website/screens/common/StatusBadge.tsx | 2 +- .../screens/common/example/Example.tsx | 18 +- apps/website/screens/common/pagesList.ts | 4 +- .../data-grid/code/DataGridCodePage.tsx | 1 - .../screens/theme-generator/ImportDialog.tsx | 2 +- .../theme-generator/ThemeGenerator.tsx | 4 +- .../components/ThemeInputsConfig.tsx | 6 +- .../components/widgets/ImageConfig.tsx | 11 +- apps/website/screens/theme-generator/utils.ts | 11 +- apps/website/tsconfig.json | 2 +- package-lock.json | 26 + package.json | 1 + packages/lib/.storybook/main.ts | 1 + packages/lib/src/HalstackContext.tsx | 58 +- .../src/accordion-group/AccordionGroup.tsx | 4 +- .../AccordionGroupAccordion.tsx | 8 +- .../accordion-group/AccordionGroupContext.tsx | 6 +- packages/lib/src/accordion-group/types.ts | 17 +- packages/lib/src/accordion/Accordion.tsx | 38 +- packages/lib/src/alert/Alert.tsx | 26 +- packages/lib/src/badge/Badge.tsx | 30 +- packages/lib/src/bleed/Bleed.tsx | 2 +- packages/lib/src/breadcrumbs/Breadcrumbs.tsx | 20 +- packages/lib/src/breadcrumbs/Item.tsx | 10 +- .../lib/src/bulleted-list/BulletedList.tsx | 64 ++- packages/lib/src/button/Button.tsx | 58 +- packages/lib/src/checkbox/Checkbox.tsx | 51 +- packages/lib/src/chip/Chip.tsx | 2 +- packages/lib/src/common/utils.ts | 22 +- packages/lib/src/container/Container.tsx | 53 +- .../src/contextual-menu/ContextualMenu.tsx | 51 +- .../lib/src/contextual-menu/GroupItem.tsx | 20 +- .../lib/src/contextual-menu/SingleItem.tsx | 12 +- packages/lib/src/data-grid/DataGrid.tsx | 113 ++-- packages/lib/src/data-grid/utils.tsx | 509 +++++++++--------- packages/lib/src/date-input/Calendar.tsx | 57 +- packages/lib/src/date-input/DateInput.tsx | 179 +++--- packages/lib/src/dialog/Dialog.tsx | 8 +- packages/lib/src/dropdown/Dropdown.tsx | 64 ++- packages/lib/src/file-input/FileInput.tsx | 120 +++-- packages/lib/src/image/Image.tsx | 40 +- packages/lib/src/inset/Inset.tsx | 2 +- packages/lib/src/layout/ApplicationLayout.tsx | 25 +- packages/lib/src/nav-tabs/NavTabs.tsx | 74 +-- packages/lib/src/nav-tabs/NavTabsContext.tsx | 4 +- packages/lib/src/nav-tabs/Tab.tsx | 49 +- packages/lib/src/nav-tabs/types.ts | 8 +- packages/lib/src/number-input/NumberInput.tsx | 20 +- .../src/number-input/NumberInputContext.tsx | 4 +- packages/lib/src/paginator/Paginator.tsx | 21 +- packages/lib/src/paragraph/Paragraph.tsx | 11 +- .../lib/src/password-input/PasswordInput.tsx | 18 +- packages/lib/src/progress-bar/ProgressBar.tsx | 16 +- packages/lib/src/quick-nav/QuickNav.tsx | 4 +- packages/lib/src/radio-group/RadioGroup.tsx | 68 ++- .../src/resultset-table/ResultsetTable.tsx | 62 ++- packages/lib/src/select/Select.tsx | 264 +++++---- packages/lib/src/select/selectUtils.ts | 83 +-- packages/lib/src/slider/Slider.tsx | 84 +-- packages/lib/src/spinner/Spinner.tsx | 23 +- packages/lib/src/switch/Switch.tsx | 46 +- packages/lib/src/table/Table.tsx | 31 +- packages/lib/src/tabs/Tab.tsx | 52 +- packages/lib/src/tabs/Tabs.tsx | 76 +-- packages/lib/src/tabs/TabsContext.tsx | 4 +- packages/lib/src/tabs/types.ts | 23 +- packages/lib/src/tag/Tag.tsx | 41 +- packages/lib/src/text-input/TextInput.tsx | 424 +++++++++------ packages/lib/src/textarea/Textarea.tsx | 137 +++-- packages/lib/src/toast/ToastsQueue.tsx | 27 +- packages/lib/src/toast/useToast.tsx | 12 +- packages/lib/src/toggle-group/ToggleGroup.tsx | 34 +- packages/lib/src/tooltip/types.tsx | 6 +- packages/lib/src/utils/useTimeout.tsx | 3 +- packages/lib/src/utils/useWidth.tsx | 9 +- packages/lib/src/wizard/Wizard.tsx | 36 +- packages/lib/tsconfig.json | 9 +- packages/typescript-config/react-library.json | 2 +- 81 files changed, 2077 insertions(+), 1487 deletions(-) diff --git a/apps/website/pages/_app.tsx b/apps/website/pages/_app.tsx index eabbde53ae..63a1231e0f 100644 --- a/apps/website/pages/_app.tsx +++ b/apps/website/pages/_app.tsx @@ -12,14 +12,14 @@ import StatusBadge from "@/common/StatusBadge"; import "../global-styles.css"; type NextPageWithLayout = NextPage & { - getLayout?: (page: ReactElement) => ReactNode; + getLayout?: (_page: ReactElement) => ReactNode; }; type AppPropsWithLayout = AppProps & { Component: NextPageWithLayout; }; type ApplicationLayoutWrapperProps = { condition: boolean; - wrapper: (children: React.ReactNode) => JSX.Element; + wrapper: (_children: ReactNode) => JSX.Element; children: ReactNode; }; @@ -52,9 +52,12 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => { }; const matchPaths = (linkPath: string) => { - const pathToBeMatched = currentPath.split("#")[0].slice(0, -1); - const desiredPaths = [linkPath, `${linkPath}/specifications`, `${linkPath}/usage`]; - return desiredPaths.includes(pathToBeMatched); + const pathToBeMatched = currentPath?.split("#")[0]?.slice(0, -1); + if (pathToBeMatched) { + const desiredPaths = [linkPath, `${linkPath}/specifications`, `${linkPath}/usage`]; + return desiredPaths.includes(pathToBeMatched); + } + return false; }; return ( diff --git a/apps/website/screens/common/HalstackMarkdownParser.tsx b/apps/website/screens/common/HalstackMarkdownParser.tsx index d16f1c460d..9b16503f70 100644 --- a/apps/website/screens/common/HalstackMarkdownParser.tsx +++ b/apps/website/screens/common/HalstackMarkdownParser.tsx @@ -1,7 +1,7 @@ import { DxcBulletedList, DxcHeading, DxcLink } from "@dxc-technology/halstack-react"; import { ReactMarkdown } from "react-markdown/lib/react-markdown"; import Code from "./Code"; -import React, { Children } from "react"; +import { Children } from "react"; const HalstackMarkdownParser = ({ markdown }: { markdown: string }) => ( ( h3: ({ children }) => ( (child as string).replace(/[^\x00-\x7F]/g, "")).join("")} + text={Children?.map(children, (child) => (child as string).replace(/[^\x00-\x7F]/g, ""))?.join("") ?? ""} /> ), ul: ({ children }) => ( diff --git a/apps/website/screens/common/MainContent.tsx b/apps/website/screens/common/MainContent.tsx index 0eb6b6eb40..e6f71ed341 100644 --- a/apps/website/screens/common/MainContent.tsx +++ b/apps/website/screens/common/MainContent.tsx @@ -16,7 +16,9 @@ const MainContainer = styled.div` const pathVersion = process.env.NEXT_PUBLIC_SITE_VERSION === "next" || process.env.NODE_ENV === "development" ? 0 - : parseInt(process.env.NEXT_PUBLIC_SITE_VERSION?.split(".")[0], 10); + : process.env.NEXT_PUBLIC_SITE_VERSION != null + ? parseInt(process.env.NEXT_PUBLIC_SITE_VERSION.split(".")[0]!, 10) + : null; const MainContent = ({ children }: { children: ReactNode }) => { const toast = useToast(); diff --git a/apps/website/screens/common/StatusBadge.tsx b/apps/website/screens/common/StatusBadge.tsx index dc0dddd4ad..36eff8b262 100644 --- a/apps/website/screens/common/StatusBadge.tsx +++ b/apps/website/screens/common/StatusBadge.tsx @@ -42,7 +42,7 @@ const getBadgeTitle = (status: StatusBadgeProps["status"]) => { const StatusBadge = ({ hasTitle = false, status }: StatusBadgeProps) => ( { - navigator.clipboard - .writeText(example.code) - .then(() => { - toast.success({ message: "Code copied to the clipboard." }); - }) - .catch(() => { - toast.warning({ message: "Failed to copy the text to the clipboard." }); - }); + if (example.code) { + navigator.clipboard + .writeText(example.code) + .then(() => { + toast.success({ message: "Code copied to the clipboard." }); + }) + .catch(() => { + toast.warning({ message: "Failed to copy the text to the clipboard." }); + }); + } }; return ( diff --git a/apps/website/screens/common/pagesList.ts b/apps/website/screens/common/pagesList.ts index 8203fa62c0..7cb4b7ff57 100644 --- a/apps/website/screens/common/pagesList.ts +++ b/apps/website/screens/common/pagesList.ts @@ -76,7 +76,7 @@ export const getNavigationLinks = (currentPath: string): NavigationLinks => { const previousLinkIndex = currentLinkIndex - 1; const previousLinkExists = previousLinkIndex >= 0; return { - previousLink: previousLinkExists ? links[previousLinkIndex] : null, - nextLink: nextLinkExists ? links[nextLinkIndex] : null, + previousLink: previousLinkExists ? (links[previousLinkIndex] ?? null) : null, + nextLink: nextLinkExists ? (links[nextLinkIndex] ?? null) : null, }; }; diff --git a/apps/website/screens/components/data-grid/code/DataGridCodePage.tsx b/apps/website/screens/components/data-grid/code/DataGridCodePage.tsx index 095c40cd1f..3a6eed6ff9 100644 --- a/apps/website/screens/components/data-grid/code/DataGridCodePage.tsx +++ b/apps/website/screens/components/data-grid/code/DataGridCodePage.tsx @@ -278,7 +278,6 @@ const sections = [ ), }, - , { title: "DxcDataGrid.ActionsCell", content: ( diff --git a/apps/website/screens/theme-generator/ImportDialog.tsx b/apps/website/screens/theme-generator/ImportDialog.tsx index 3b117791f5..54085e0e77 100644 --- a/apps/website/screens/theme-generator/ImportDialog.tsx +++ b/apps/website/screens/theme-generator/ImportDialog.tsx @@ -19,7 +19,7 @@ const validateInputTheme = (json: string, customThemeSchema: IndexedTheme) => { inputComponentNames.forEach((componentName) => { const errorMessage = (!schemaComponentNames.includes(componentName) && "Invalid component name.") || - (!isArrayIncluded(inputTheme[componentName], customThemeSchema[componentName]) && + (!isArrayIncluded(inputTheme[componentName]!, customThemeSchema[componentName] ?? {}) && `Invalid theme input name in the ${componentName} component.`); if (errorMessage) throw new Error(errorMessage); diff --git a/apps/website/screens/theme-generator/ThemeGenerator.tsx b/apps/website/screens/theme-generator/ThemeGenerator.tsx index 5ac05d2c2d..70701cd2cf 100644 --- a/apps/website/screens/theme-generator/ThemeGenerator.tsx +++ b/apps/website/screens/theme-generator/ThemeGenerator.tsx @@ -125,8 +125,8 @@ const ThemeGenerator = () => { diff --git a/apps/website/screens/theme-generator/components/ThemeInputsConfig.tsx b/apps/website/screens/theme-generator/components/ThemeInputsConfig.tsx index 48bb431b12..6d22aa91c5 100644 --- a/apps/website/screens/theme-generator/components/ThemeInputsConfig.tsx +++ b/apps/website/screens/theme-generator/components/ThemeInputsConfig.tsx @@ -6,7 +6,7 @@ import ThemeInput from "./ThemeInput"; type ThemeInputsConfigProps = { componentInputs: IndexedThemeInput; componentInputsTypes: IndexedThemeInput; - onChangeCustomTheme: (propertyName: string, propertyValue: string) => void; + onChangeCustomTheme: (_propertyName: string, _propertyValue: string) => void; }; const ThemeInputsConfig = ({ @@ -22,9 +22,9 @@ const ThemeInputsConfig = ({ ))} diff --git a/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx b/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx index 15e03112e0..d61e9851de 100644 --- a/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx +++ b/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx @@ -11,10 +11,13 @@ const ImageConfig = ({ propertyName, propertyValue, onChangeCustomTheme }: Theme input.click(); }; const upload = (event: React.ChangeEvent) => { - const files = event.target.files as FileList; - const url = URL.createObjectURL(files[0]); - onChangeCustomTheme(propertyName, url); - setLogoImage(url); + const files = event.target.files; + if (files && files[0]) { + const url = URL.createObjectURL(files[0]); + onChangeCustomTheme(propertyName, url); + setLogoImage(url); + return () => URL.revokeObjectURL(url); + } }; useEffect(() => { diff --git a/apps/website/screens/theme-generator/utils.ts b/apps/website/screens/theme-generator/utils.ts index fb446e7d31..64d2eeeccf 100644 --- a/apps/website/screens/theme-generator/utils.ts +++ b/apps/website/screens/theme-generator/utils.ts @@ -1,3 +1,5 @@ +import type { IndexedTheme } from "./types"; + export const makeReadable = (token: string) => token.replace(/^[a-z]|[A-Z]/g, function (v, i) { return i === 0 ? v.toUpperCase() : " " + v.toLowerCase(); @@ -8,9 +10,9 @@ export const makeReadableSidenav = (token: string) => return i === 0 ? v.toUpperCase() : " " + v; }); -const isObject = (item) => item && typeof item === "object" && !Array.isArray(item); +const isObject = (item: unknown) => item && typeof item === "object" && !Array.isArray(item); -export const deepMerge = (target, ...sources) => { +export const deepMerge = (target: T, ...sources: Partial[]): T => { if (!sources.length) return target; const source = sources.shift(); @@ -18,7 +20,7 @@ export const deepMerge = (target, ...sources) => { for (const key in source) { if (isObject(source[key])) { if (!target[key]) Object.assign(target, { [key]: {} }); - deepMerge(target[key], source[key]); + deepMerge(target[key] as object, source[key] as object); } else { Object.assign(target, { [key]: source[key] }); } @@ -28,7 +30,7 @@ export const deepMerge = (target, ...sources) => { return deepMerge(target, ...sources); }; -export const downloadFile = (content) => { +export const downloadFile = (content: IndexedTheme) => { const data = new Blob([JSON.stringify(content, null, "\t")], { type: "application/json", }); @@ -38,4 +40,5 @@ export const downloadFile = (content) => { element.download = "theme.json"; document.body.appendChild(element); element.click(); + return () => URL.revokeObjectURL(element.href); }; diff --git a/apps/website/tsconfig.json b/apps/website/tsconfig.json index a46023771b..9e853b6beb 100644 --- a/apps/website/tsconfig.json +++ b/apps/website/tsconfig.json @@ -8,7 +8,7 @@ "paths": { "@/common/*": ["screens/common/*"] }, - "strict": false + "strict": true }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] diff --git a/package-lock.json b/package-lock.json index a7daf7e30a..bdbc870a67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "packages/*" ], "devDependencies": { + "@types/color": "^4.2.0", "prettier": "^3.2.5", "turbo": "^2.0.4", "typescript": "^5.6.3" @@ -37,6 +38,7 @@ "styled-components": "^5.3.3" }, "devDependencies": { + "@dxc-technology/typescript-config": "*", "@types/node": "^20", "@types/react": "^18", "@types/react-color": "^3.0.6", @@ -5778,6 +5780,30 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/color": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/color/-/color-4.2.0.tgz", + "integrity": "sha512-6+xrIRImMtGAL2X3qYkd02Mgs+gFGs+WsK0b7VVMaO4mYRISwyTjcqNrO0mNSmYEoq++rSLDB2F5HDNmqfOe+A==", + "dev": true, + "dependencies": { + "@types/color-convert": "*" + } + }, + "node_modules/@types/color-convert": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.4.tgz", + "integrity": "sha512-Ub1MmDdyZ7mX//g25uBAoH/mWGd9swVbt8BseymnaE18SU4po/PjmCrHxqIIRjBo3hV/vh1KGr0eMxUhp+t+dQ==", + "dev": true, + "dependencies": { + "@types/color-name": "^1.1.0" + } + }, + "node_modules/@types/color-name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.5.tgz", + "integrity": "sha512-j2K5UJqGTxeesj6oQuGpMgifpT5k9HprgQd8D1Y0lOFqKHl3PJu5GMeS4Y5EgjS55AE6OQxf8mPED9uaGbf4Cg==", + "dev": true + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", diff --git a/package.json b/package.json index c0efb274ed..a1a51dde0b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "test:watch": "turbo test:watch" }, "devDependencies": { + "@types/color": "^4.2.0", "prettier": "^3.2.5", "turbo": "^2.0.4", "typescript": "^5.6.3" diff --git a/packages/lib/.storybook/main.ts b/packages/lib/.storybook/main.ts index 46ef194f3d..f5853a2183 100644 --- a/packages/lib/.storybook/main.ts +++ b/packages/lib/.storybook/main.ts @@ -19,4 +19,5 @@ const config: StorybookConfig = { reactDocgen: "react-docgen-typescript", }, }; + export default config; diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index 36a5702ced..6cfa406495 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -1,6 +1,5 @@ -import { createContext, useMemo } from "react"; +import { createContext, ReactNode, useMemo } from "react"; import Color from "color"; -import styled from "styled-components"; import { AdvancedTheme, OpinionatedTheme, @@ -9,6 +8,14 @@ import { defaultTranslatedComponentLabels, } from "./common/variables"; +/** + * This type is used to allow partial themes and labels objects to be passed to the HalstackProvider. + * This is an extension of the already existing Partial type, which only allows one level of partiality. + */ +export type DeepPartial = { + [P in keyof T]?: Partial; +}; + const HalstackContext = createContext | null>(null); const HalstackLanguageContext = createContext | null>(null); @@ -20,6 +27,7 @@ const addLightness = (newLightness: number, hexColor?: string) => { const lightnessColor = hslColor.lightness(); return hslColor.lightness(lightnessColor + newLightness).hex(); } + return null; } catch (e) { return null; } @@ -33,19 +41,21 @@ const subLightness = (newLightness: number, hexColor?: string) => { const lightnessColor = hslColor.lightness(); return hslColor.lightness(lightnessColor - newLightness).hex(); } + return null; } catch (e) { return null; } }; const parseAdvancedTheme = (advancedTheme: DeepPartial): AdvancedTheme => { - const allTokensCopy = JSON.parse(JSON.stringify(componentTokens)); - - Object.keys(allTokensCopy).map((component) => { - if (advancedTheme[component]) { - Object.keys(advancedTheme[component]).map((objectKey) => { - if (advancedTheme[component][objectKey]) { - allTokensCopy[component][objectKey] = advancedTheme[component][objectKey]; + const allTokensCopy: AdvancedTheme = JSON.parse(JSON.stringify(componentTokens)); + + (Object.keys(allTokensCopy) as (keyof AdvancedTheme)[]).forEach((component) => { + const componentTheme = advancedTheme[component]; + if (componentTheme != null) { + (Object.keys(componentTheme) as (keyof typeof componentTheme)[]).forEach((objectKey) => { + if (componentTheme[objectKey]) { + allTokensCopy[component][objectKey] = componentTheme[objectKey]; } }); } @@ -112,7 +122,7 @@ const parseTheme = (theme: DeepPartial): AdvancedTheme => { chipTokens.hoverIconColor = subLightness(10, theme?.chip?.iconColor) ?? chipTokens.hoverIconColor; chipTokens.activeIconColor = subLightness(30, theme?.chip?.iconColor) ?? chipTokens.activeIconColor; - const contextualMenu = componentTokensCopy.contextualMenu; + const { contextualMenu } = componentTokensCopy; contextualMenu.selectedMenuItemBackgroundColor = theme?.contextualMenu?.accentColor ?? contextualMenu.selectedMenuItemBackgroundColor; contextualMenu.hoverSelectedMenuItemBackgroundColor = @@ -385,30 +395,26 @@ const parseTheme = (theme: DeepPartial): AdvancedTheme => { const parseLabels = (labels: DeepPartial): TranslatedLabels => { const parsedLabels = defaultTranslatedComponentLabels; - Object.keys(labels).map((component) => { + (Object.keys(labels) as (keyof TranslatedLabels)[]).forEach((component) => { if (parsedLabels[component]) { - Object.keys(parsedLabels[component]).map((label) => { - if (labels[component][label]) { - parsedLabels[component][label] = labels[component][label]; - } - }); + const componentLabels = labels[component]; + if (componentLabels != null) { + (Object.keys(parsedLabels[component]) as (keyof typeof componentLabels)[]).forEach((label) => { + if (componentLabels[label]) { + parsedLabels[component][label] = componentLabels[label]; + } + }); + } } }); return parsedLabels; }; -/** - * This type is used to allow partial themes and labels objects to be passed to the HalstackProvider. - * This is an extension of the already existing Partial type, which only allows one level of partiality. - */ -export type DeepPartial = { - [P in keyof T]?: Partial; -}; type HalstackProviderPropsType = { theme?: DeepPartial; advancedTheme?: DeepPartial; labels?: DeepPartial; - children: React.ReactNode; + children: ReactNode; }; const HalstackProvider = ({ theme, advancedTheme, labels, children }: HalstackProviderPropsType): JSX.Element => { const parsedTheme = useMemo( @@ -424,4 +430,6 @@ const HalstackProvider = ({ theme, advancedTheme, labels, children }: HalstackPr ); }; -export { HalstackContext as default, HalstackProvider, HalstackLanguageContext }; +export { HalstackProvider, HalstackLanguageContext }; + +export default HalstackContext; diff --git a/packages/lib/src/accordion-group/AccordionGroup.tsx b/packages/lib/src/accordion-group/AccordionGroup.tsx index 479e2ecd96..d412ea9dc2 100644 --- a/packages/lib/src/accordion-group/AccordionGroup.tsx +++ b/packages/lib/src/accordion-group/AccordionGroup.tsx @@ -1,11 +1,11 @@ import { Children, useCallback, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import { getMargin } from "../common/utils"; +import getMargin from "../common/utils"; import { spaces } from "../common/variables"; import useTheme from "../useTheme"; import AccordionGroupAccordion from "./AccordionGroupAccordion"; import AccordionGroupPropsType from "./types"; -import { AccordionGroupAccordionContext } from "./AccordionGroupContext"; +import AccordionGroupAccordionContext from "./AccordionGroupContext"; const DxcAccordionGroup = ({ defaultIndexActive, diff --git a/packages/lib/src/accordion-group/AccordionGroupAccordion.tsx b/packages/lib/src/accordion-group/AccordionGroupAccordion.tsx index 4e9ad252bb..8417f78be5 100644 --- a/packages/lib/src/accordion-group/AccordionGroupAccordion.tsx +++ b/packages/lib/src/accordion-group/AccordionGroupAccordion.tsx @@ -1,16 +1,18 @@ import { useContext } from "react"; import DxcAccordion from "../accordion/Accordion"; import { AccordionPropsType } from "./types"; -import { AccordionGroupAccordionContext } from "./AccordionGroupContext"; +import AccordionGroupAccordionContext from "./AccordionGroupContext"; const AccordionGroupAccordion = ({ ...childProps }: AccordionPropsType): JSX.Element => { - const { activeIndex, handlerActiveChange, disabled, index } = useContext(AccordionGroupAccordionContext); + const { activeIndex, handlerActiveChange, disabled, index } = useContext(AccordionGroupAccordionContext) ?? {}; return ( { - handlerActiveChange(index); + if (index != null) { + handlerActiveChange?.(index); + } }} disabled={disabled} {...childProps} diff --git a/packages/lib/src/accordion-group/AccordionGroupContext.tsx b/packages/lib/src/accordion-group/AccordionGroupContext.tsx index c70e3d0809..70f6812ec4 100644 --- a/packages/lib/src/accordion-group/AccordionGroupContext.tsx +++ b/packages/lib/src/accordion-group/AccordionGroupContext.tsx @@ -1,4 +1,6 @@ import { createContext } from "react"; -import { AccordionGroupAccordionContextProps } from "./types"; +import type { AccordionGroupAccordionContextProps } from "./types"; -export const AccordionGroupAccordionContext = createContext(null); +const AccordionGroupAccordionContext = createContext(null); + +export default AccordionGroupAccordionContext; diff --git a/packages/lib/src/accordion-group/types.ts b/packages/lib/src/accordion-group/types.ts index 405ba4f24d..35969901a4 100644 --- a/packages/lib/src/accordion-group/types.ts +++ b/packages/lib/src/accordion-group/types.ts @@ -1,11 +1,8 @@ -type Space = "xxsmall" | "xsmall" | "small" | "medium" | "large" | "xlarge" | "xxlarge"; -type Margin = { - top?: Space; - bottom?: Space; - left?: Space; - right?: Space; -}; -type SVG = React.ReactNode & React.SVGProps; +import { ReactElement, ReactNode, SVGProps } from "react"; + +import { Space, Margin } from "../common/utils"; + +type SVG = ReactNode & SVGProps; export type AccordionPropsType = { /** @@ -28,7 +25,7 @@ export type AccordionPropsType = { * The expanded panel of the accordion. This area can be used to render * custom content. */ - children: React.ReactNode; + children: ReactNode; }; type Props = { @@ -57,7 +54,7 @@ type Props = { /** * Customized accordion(s) that are allowed inside an Accordion Group. */ - children: React.ReactElement[] | React.ReactElement; + children: ReactElement[] | ReactElement; }; export type AccordionGroupAccordionContextProps = { diff --git a/packages/lib/src/accordion/Accordion.tsx b/packages/lib/src/accordion/Accordion.tsx index aa9fc18b0c..b998739cab 100644 --- a/packages/lib/src/accordion/Accordion.tsx +++ b/packages/lib/src/accordion/Accordion.tsx @@ -1,6 +1,6 @@ import { useId, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import { getMargin } from "../common/utils"; +import getMargin from "../common/utils"; import { spaces } from "../common/variables"; import useTheme from "../useTheme"; import AccordionPropsType from "./types"; @@ -24,12 +24,14 @@ const DxcAccordion = ({ const colorsTheme = useTheme(); const handleAccordionState = () => { - isExpanded ?? setInnerIsExpanded((innerIsExpanded) => !innerIsExpanded); + if (isExpanded == null) { + setInnerIsExpanded((innerIsCurrentlyExpanded) => !innerIsCurrentlyExpanded); + } onChange?.(isExpanded != null ? !isExpanded : !innerIsExpanded); }; return ( - + {label} @@ -68,14 +70,14 @@ const DxcAccordion = ({ {assistiveText} @@ -192,7 +194,9 @@ const AccordionAssistiveText = styled.span` padding-right: ${(props) => props.theme.assistiveTextPaddingRight}; `; -const CollapseIndicator = styled.span<{ disabled: AccordionPropsType["disabled"] }>` +const CollapseIndicator = styled.span<{ + disabled: AccordionPropsType["disabled"]; +}>` display: flex; flex-wrap: wrap; align-content: center; diff --git a/packages/lib/src/alert/Alert.tsx b/packages/lib/src/alert/Alert.tsx index 6f662893ce..9a957583f2 100644 --- a/packages/lib/src/alert/Alert.tsx +++ b/packages/lib/src/alert/Alert.tsx @@ -1,5 +1,5 @@ -import { useState, memo, useMemo, useId, useEffect } from "react"; import styled, { css, ThemeProvider } from "styled-components"; +import { useState, memo, useId, useEffect } from "react"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import AlertPropsType from "./types"; @@ -125,7 +125,7 @@ const getIcon = (semantic: AlertPropsType["semantic"]) => { } }; -export default function DxcAlert({ +const DxcAlert = ({ closable = true, message = [], mode = "inline", @@ -133,7 +133,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); @@ -149,11 +149,15 @@ export default function DxcAlert({ }; const handleOnClose = () => { messages[currentIndex]?.onClose?.(); - mode !== "modal" && setMessages((prevMessages) => prevMessages.filter((_, index) => index !== currentIndex)); + if (mode !== "modal") { + setMessages((prevMessages) => prevMessages.filter((_, index) => index !== currentIndex)); + } }; useEffect(() => { - if (currentIndex === messages.length) handlePrevOnClick(); + if (currentIndex === messages.length) { + handlePrevOnClick(); + } }, [currentIndex, messages]); return ( @@ -193,7 +197,7 @@ export default function DxcAlert({ @@ -202,7 +206,7 @@ export default function DxcAlert({ @@ -215,8 +219,8 @@ export default function DxcAlert({ icon="close" title={ messages.length > 1 - ? translatedLabels.alert.closeMessageActionTitle - : translatedLabels.alert.closeAlertActionTitle + ? (translatedLabels?.alert?.closeMessageActionTitle ?? "") + : (translatedLabels?.alert?.closeAlertActionTitle ?? "") } onClick={handleOnClose} /> @@ -243,4 +247,6 @@ export default function DxcAlert({ ); -} +}; + +export default DxcAlert; diff --git a/packages/lib/src/badge/Badge.tsx b/packages/lib/src/badge/Badge.tsx index 64895758f5..bf2e241804 100644 --- a/packages/lib/src/badge/Badge.tsx +++ b/packages/lib/src/badge/Badge.tsx @@ -96,20 +96,21 @@ const DxcBadge = ({ )} {label && ( )} ); -const getColor = (mode, color) => (mode === "contextual" ? contextualColorMap[color].text : CoreTokens.color_white); +const getColor = (mode: BadgePropsType["mode"], color: BadgePropsType["color"]) => + mode === "contextual" ? color && contextualColorMap[color].text : CoreTokens.color_white; -const getBackgroundColor = (mode, color) => - mode === "contextual" ? contextualColorMap[color].background : CoreTokens.color_red_700; +const getBackgroundColor = (mode: BadgePropsType["mode"], color: BadgePropsType["color"]) => + mode === "contextual" ? color && contextualColorMap[color].background : CoreTokens.color_red_700; -const getPadding = (mode, size) => - mode === "contextual" ? sizeMap[size].padding.contextual : sizeMap[size].padding.notification; +const getPadding = (mode: BadgePropsType["mode"], size: BadgePropsType["size"]) => + size && (mode === "contextual" ? sizeMap[size].padding.contextual : sizeMap[size].padding.notification); const BadgeContainer = styled.div<{ label: BadgePropsType["label"]; @@ -118,11 +119,12 @@ const BadgeContainer = styled.div<{ size: BadgePropsType["size"]; }>` box-sizing: border-box; - border-radius: ${(props) => sizeMap[props.size].borderRadius}; + border-radius: ${(props) => props.size && sizeMap[props.size].borderRadius}; padding: ${(props) => (props.label ? getPadding(props.mode, props.size) : "")}; - width: ${(props) => (!props.label && props.mode === "notification" ? sizeMap[props.size].width : "fit-content")}; - min-width: ${(props) => props.mode === "notification" && sizeMap[props.size].minWidth}; - height: ${(props) => sizeMap[props.size].height}; + width: ${(props) => + !props.label && props.mode === "notification" ? props.size && sizeMap[props.size].width : "fit-content"}; + min-width: ${(props) => props.mode === "notification" && props.size && sizeMap[props.size].minWidth}; + height: ${(props) => props.size && sizeMap[props.size].height}; display: flex; align-items: center; gap: ${CoreTokens.spacing_2}; @@ -133,17 +135,17 @@ const BadgeContainer = styled.div<{ const IconContainer = styled.div<{ size: BadgePropsType["size"] }>` display: flex; - font-size: ${(props) => sizeMap[props.size].iconSize}; + font-size: ${(props) => props.size && sizeMap[props.size].iconSize}; svg { - height: ${(props) => sizeMap[props.size].iconSize}; - width: ${(props) => sizeMap[props.size].iconSize}; + height: ${(props) => props.size && sizeMap[props.size].iconSize}; + width: ${(props) => props.size && sizeMap[props.size].iconSize}; } `; const Label = styled.span<{ size: BadgePropsType["size"] }>` font-family: ${CoreTokens.type_sans}; - font-size: ${(props) => sizeMap[props.size].fontSize}; + font-size: ${(props) => props.size && sizeMap[props.size].fontSize}; font-weight: ${CoreTokens.type_semibold}; white-space: nowrap; `; diff --git a/packages/lib/src/bleed/Bleed.tsx b/packages/lib/src/bleed/Bleed.tsx index 6a7fa009bc..7d003f52dd 100644 --- a/packages/lib/src/bleed/Bleed.tsx +++ b/packages/lib/src/bleed/Bleed.tsx @@ -2,7 +2,7 @@ import styled from "styled-components"; import BleedPropsType from "./types"; import { CoreSpacingTokensType } from "../common/coreTokens"; -const getSpacingValue = (spacingName: CoreSpacingTokensType) => (spacingName ? spacingName : "0rem"); +const getSpacingValue = (spacingName?: CoreSpacingTokensType) => (spacingName ?? "0rem"); const StyledBleed = styled.div` ${({ space, horizontal, vertical, top, right, bottom, left }) => ` diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.tsx index a6888e0303..c4b5c09389 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -8,6 +8,7 @@ import CoreTokens from "../common/coreTokens"; import DxcIcon from "../icon/Icon"; import Item from "./Item"; import DxcFlex from "../flex/Flex"; +import { Option } from "../dropdown/types"; const DxcBreadcrumbs = ({ ariaLabel = "Breadcrumbs", @@ -18,8 +19,11 @@ const DxcBreadcrumbs = ({ }: BreadcrumbsProps) => { const handleOnSelectOption = useCallback( (href: string) => { - if (onItemClick) onItemClick(href); - else window.location.href = href; + if (onItemClick) { + onItemClick(href); + } else { + window.location.href = href; + } }, [items] ); @@ -29,19 +33,21 @@ const DxcBreadcrumbs = ({ {items && items.length > Math.max(itemsBeforeCollapse, 2) ? ( <> - {showRoot && } - + {showRoot && } + } - margin={showRoot && { left: "small" }} + margin={showRoot ? { left: "small" } : undefined} onSelectOption={handleOnSelectOption} - options={items.slice(showRoot ? 1 : 0, -1).map(({ label, href }) => ({ label, value: href }))} + options={items + .slice(showRoot ? 1 : 0, -1) + .map(({ label, href }) => ({ label, value: href }) as Option)} /> - + ) : ( items.map((item, index, { length }) => ( diff --git a/packages/lib/src/breadcrumbs/Item.tsx b/packages/lib/src/breadcrumbs/Item.tsx index fc8cb4f352..ae99c2bb82 100644 --- a/packages/lib/src/breadcrumbs/Item.tsx +++ b/packages/lib/src/breadcrumbs/Item.tsx @@ -1,21 +1,21 @@ -import { MouseEvent } from "react"; +import { useRef, MouseEvent } from "react"; import styled from "styled-components"; import CoreTokens from "../common/coreTokens"; import { ItemPropsType } from "./types"; -import { useRef } from "react"; const Item = ({ isCurrentPage = false, href, label, onClick }: ItemPropsType) => { - const currentItemRef = useRef(null); + const currentItemRef = useRef(null); const handleOnMouseEnter = (event: MouseEvent) => { const labelContainer = event.currentTarget; const optionElement = currentItemRef?.current; - if (optionElement.title === "" && labelContainer.scrollWidth > labelContainer.clientWidth) + if (optionElement?.title === "" && labelContainer.scrollWidth > labelContainer.clientWidth) { optionElement.title = label; + } }; const handleOnClick = (event: MouseEvent) => { - if (onClick) { + if (onClick && href) { event.preventDefault(); onClick(href); } diff --git a/packages/lib/src/bulleted-list/BulletedList.tsx b/packages/lib/src/bulleted-list/BulletedList.tsx index 3a97f2bcd2..e489552c99 100644 --- a/packages/lib/src/bulleted-list/BulletedList.tsx +++ b/packages/lib/src/bulleted-list/BulletedList.tsx @@ -6,47 +6,43 @@ import BulletedListPropsType, { BulletedListItemPropsType } from "./types"; import useTheme from "../useTheme"; import DxcIcon from "../icon/Icon"; -const BulletedListItem = ({ children }: BulletedListItemPropsType): JSX.Element => { - return <>{children}; -}; +const BulletedListItem = ({ children }: BulletedListItemPropsType): JSX.Element => <>{children}; const DxcBulletedList = ({ children, type = "disc", icon = "" }: BulletedListPropsType): JSX.Element => { const colorsTheme = useTheme(); return ( - + - {Children.map(children, (child, index) => { - return ( - - - {type === "number" ? ( - - {index + 1}. - - ) : type === "square" ? ( - - - - ) : type === "circle" ? ( - - - - ) : type === "icon" ? ( - - {typeof icon === "string" ? : icon} - - ) : ( - - - - )} - {child} - - - ); - })} + {Children.map(children, (child, index) => ( + + + {type === "number" ? ( + + {index + 1}. + + ) : type === "square" ? ( + + + + ) : type === "circle" ? ( + + + + ) : type === "icon" ? ( + + {typeof icon === "string" ? : icon} + + ) : ( + + + + )} + {child} + + + ))} diff --git a/packages/lib/src/button/Button.tsx b/packages/lib/src/button/Button.tsx index 54a51db86c..b78f463eb1 100644 --- a/packages/lib/src/button/Button.tsx +++ b/packages/lib/src/button/Button.tsx @@ -1,6 +1,6 @@ import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; -import { getMargin } from "../common/utils"; +import getMargin from "../common/utils"; import useTheme from "../useTheme"; import ButtonPropsType from "./types"; import DxcIcon from "../icon/Icon"; @@ -34,8 +34,8 @@ const DxcButton = ({ tabIndex={disabled ? -1 : tabIndex} type={type} $mode={mode === "primary" || mode === "secondary" || mode === "tertiary" ? mode : "primary"} - hasLabel={label ? true : false} - hasIcon={icon ? true : false} + hasLabel={!!label} + hasIcon={!!icon} iconPosition={iconPosition} size={size} margin={margin} @@ -62,9 +62,9 @@ const widths = { const calculateWidth = (margin: ButtonPropsType["margin"], size: ButtonPropsType["size"]) => size?.width === "fillParent" ? `calc(${widths[size?.width]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})` - : widths[size?.width]; + : size?.width && widths[size?.width]; -const getHeight = (height) => { +const getHeight = (height?: string) => { switch (height) { case "small": return 1.5; @@ -89,7 +89,7 @@ const getButtonStyles = ( let focus = ""; let disabled = ""; - let commonPrimaryStyles = ` + const commonPrimaryStyles = ` font-weight: ${theme.primaryFontWeight}; font-size: ${size?.height === "small" ? theme.primarySmallFontSize : size?.height === "medium" ? theme.primaryMediumFontSize : theme.primaryLargeFontSize}; font-family: ${theme.primaryFontFamily}; @@ -97,7 +97,7 @@ const getButtonStyles = ( border-width ${theme.primaryBorderThickness}; border-style: ${theme.primaryBorderStyle};`; - let commonSecondaryStyles = ` + const commonSecondaryStyles = ` font-weight: ${theme.secondaryFontWeight}; font-size: ${size?.height === "small" ? theme.secondarySmallFontSize : size?.height === "medium" ? theme.secondaryMediumFontSize : theme.secondaryLargeFontSize}; font-family: ${theme.secondaryFontFamily}; @@ -105,7 +105,7 @@ const getButtonStyles = ( border-width ${theme.secondaryBorderThickness}; border-style: ${theme.secondaryBorderStyle};`; - let commonTertiaryStyles = ` + const commonTertiaryStyles = ` font-weight: ${theme.tertiaryFontWeight}; font-size: ${size?.height === "small" ? theme.tertiarySmallFontSize : size?.height === "medium" ? theme.tertiaryMediumFontSize : theme.tertiaryLargeFontSize}; font-family: ${theme.tertiaryFontFamily}; @@ -385,6 +385,8 @@ const getButtonStyles = ( &:disabled { ${disabled} }`; + default: + return undefined; } }; @@ -403,7 +405,7 @@ const Button = styled.button<{ gap: 0.5rem; align-items: center; justify-content: center; - height: ${(props) => getHeight(props.size?.height && props.size?.height) + "rem"}; + height: ${(props) => `${getHeight(props.size?.height && props.size?.height)}rem`}; width: ${(props) => calculateWidth(props.margin, props.size)}; margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; margin-top: ${(props) => @@ -416,50 +418,50 @@ const Button = styled.button<{ props.margin && typeof props.margin === "object" && props.margin.left ? spaces[props.margin.left] : ""}; padding-top: ${(props) => props.hasIcon && !props.hasLabel - ? props.size.height === "small" + ? props?.size?.height === "small" ? props.theme.paddingSmallOnlyIconTop - : props.size.height === "medium" + : props?.size?.height === "medium" ? props.theme.paddingMediumOnlyIconTop : props.theme.paddingLargeOnlyIconTop - : props.size.height === "small" + : props?.size?.height === "small" ? props.theme.paddingSmallTop - : props.size.height === "medium" + : props?.size?.height === "medium" ? props.theme.paddingMediumTop : props.theme.paddingLargeTop}; padding-bottom: ${(props) => props.hasIcon && !props.hasLabel - ? props.size.height === "small" + ? props?.size?.height === "small" ? props.theme.paddingSmallOnlyIconBottom - : props.size.height === "medium" + : props?.size?.height === "medium" ? props.theme.paddingMediumOnlyIconBottom : props.theme.paddingLargeOnlyIconBottom - : props.size.height === "small" + : props?.size?.height === "small" ? props.theme.paddingSmallBottom - : props.size.height === "medium" + : props?.size?.height === "medium" ? props.theme.paddingMediumBottom : props.theme.paddingLargeBottom}; padding-left: ${(props) => props.hasIcon && !props.hasLabel - ? props.size.height === "small" + ? props?.size?.height === "small" ? props.theme.paddingSmallOnlyIconLeft - : props.size.height === "medium" + : props?.size?.height === "medium" ? props.theme.paddingMediumOnlyIconLeft : props.theme.paddingLargeOnlyIconLeft - : props.size.height === "small" + : props?.size?.height === "small" ? props.theme.paddingSmallLeft - : props.size.height === "medium" + : props?.size?.height === "medium" ? props.theme.paddingMediumLeft : props.theme.paddingLargeLeft}; padding-right: ${(props) => props.hasIcon && !props.hasLabel - ? props.size.height === "small" + ? props?.size?.height === "small" ? props.theme.paddingSmallOnlyIconRight - : props.size.height === "medium" + : props?.size?.height === "medium" ? props.theme.paddingMediumOnlyIconRight : props.theme.paddingLargeOnlyIconRight - : props.size.height === "small" + : props?.size?.height === "small" ? props.theme.paddingSmallRight - : props.size.height === "medium" + : props?.size?.height === "medium" ? props.theme.paddingMediumRight : props.theme.paddingLargeRight}; @@ -491,10 +493,10 @@ const IconContainer = styled.div<{ size: ButtonPropsType["size"]; }>` display: flex; - font-size: ${(props) => (props.size.height === "small" ? "16" : props.size.height === "medium" ? "16" : "24")}px; + font-size: ${(props) => (props?.size?.height === "small" ? "16" : props?.size?.height === "medium" ? "16" : "24")}px; svg { - height: ${(props) => (props.size.height === "small" ? "16" : props.size.height === "medium" ? "16" : "24")}px; - width: ${(props) => (props.size.height === "small" ? "16" : props.size.height === "medium" ? "16" : "24")}px; + height: ${(props) => (props?.size?.height === "small" ? "16" : props?.size?.height === "medium" ? "16" : "24")}px; + width: ${(props) => (props?.size?.height === "small" ? "16" : props?.size?.height === "medium" ? "16" : "24")}px; } `; diff --git a/packages/lib/src/checkbox/Checkbox.tsx b/packages/lib/src/checkbox/Checkbox.tsx index f138399c87..02e252b40c 100644 --- a/packages/lib/src/checkbox/Checkbox.tsx +++ b/packages/lib/src/checkbox/Checkbox.tsx @@ -1,7 +1,7 @@ -import { useState, useRef, useId, forwardRef } from "react"; +import { useState, useRef, useId, forwardRef, KeyboardEvent } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; -import { getMargin } from "../common/utils"; +import getMargin from "../common/utils"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import CheckboxPropsType, { RefType } from "./types"; @@ -33,29 +33,36 @@ const DxcCheckbox = forwardRef( ): JSX.Element => { const labelId = `label-checkbox-${useId()}`; const [innerChecked, setInnerChecked] = useState(defaultChecked); - const checkboxRef = useRef(null); + const checkboxRef = useRef(null); const colorsTheme = useTheme(); const translatedLabels = useTranslatedLabels(); const handleCheckboxChange = () => { if (!disabled && !readOnly) { - document.activeElement !== checkboxRef?.current && checkboxRef?.current?.focus(); + if (document.activeElement !== checkboxRef?.current) { + checkboxRef?.current?.focus(); + } const newChecked = checked ?? innerChecked; - checked ?? setInnerChecked((innerChecked) => !innerChecked); + if (checked == null) { + setInnerChecked((innerCurrentlyChecked) => !innerCurrentlyChecked); + } onChange?.(!newChecked); } }; - const handleKeyboard = (event) => { + const handleKeyboard = (event: KeyboardEvent) => { switch (event.key) { case " ": event.preventDefault(); handleCheckboxChange(); + break; + default: + break; } }; return ( - + ( {label && ( {label} - {optional && ` ${translatedLabels.formFields.optionalLabel}`} + {optional && ` ${translatedLabels?.formFields?.optionalLabel}`} )} size === "fillParent" ? `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})` - : sizes[size]; + : size && sizes[size]; const getDisabledColor = (theme: AdvancedTheme["checkbox"], element: string) => { switch (element) { @@ -127,6 +134,8 @@ const getDisabledColor = (theme: AdvancedTheme["checkbox"], element: string) => return theme.disabledBorderColor; case "label": return theme.disabledFontColor; + default: + return undefined; } }; @@ -142,6 +151,8 @@ const getReadOnlyColor = (theme: AdvancedTheme["checkbox"], element: string) => return theme.readOnlyBorderColor; case "hoverBorder": return theme.hoverReadOnlyBorderColor; + default: + return undefined; } }; @@ -159,6 +170,8 @@ const getEnabledColor = (theme: AdvancedTheme["checkbox"], element: string) => { return theme.hoverBorderColor; case "label": return theme.fontColor; + default: + return undefined; } }; @@ -257,18 +270,14 @@ const MainContainer = styled.div<{ &:hover ${Checkbox} { border: 2px solid - ${(props) => { - if (!props.disabled) - return props.readOnly - ? getReadOnlyColor(props.theme, "hoverBorder") - : getEnabledColor(props.theme, "hoverBorder"); - }}; - color: ${(props) => { - if (!props.disabled) - return props.readOnly - ? getReadOnlyColor(props.theme, "hoverBackground") - : getEnabledColor(props.theme, "hoverBackground"); - }}; + ${(props) => + !props.disabled && + (props.readOnly ? getReadOnlyColor(props.theme, "hoverBorder") : getEnabledColor(props.theme, "hoverBorder"))}; + color: ${(props) => + !props.disabled && + (props.readOnly + ? getReadOnlyColor(props.theme, "hoverBackground") + : getEnabledColor(props.theme, "hoverBackground"))}; } `; diff --git a/packages/lib/src/chip/Chip.tsx b/packages/lib/src/chip/Chip.tsx index 7681b56a45..5981acb045 100644 --- a/packages/lib/src/chip/Chip.tsx +++ b/packages/lib/src/chip/Chip.tsx @@ -1,5 +1,5 @@ import styled, { ThemeProvider } from "styled-components"; -import { getMargin } from "../common/utils"; +import getMargin from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; import useTheme from "../useTheme"; diff --git a/packages/lib/src/common/utils.ts b/packages/lib/src/common/utils.ts index 082a3f6a85..470b957fc4 100644 --- a/packages/lib/src/common/utils.ts +++ b/packages/lib/src/common/utils.ts @@ -1,9 +1,19 @@ import { spaces } from "./variables"; -export const getMargin = (marginProp: string | object, side: string) => { - if (marginProp && typeof marginProp === "object") { - return (marginProp[side] && spaces[marginProp[side]]) || "0px"; - } else if (marginProp && typeof marginProp === "string") { - return spaces[marginProp]; - } else return "0px"; +export type Space = "xxsmall" | "xsmall" | "small" | "medium" | "large" | "xlarge" | "xxlarge"; +export type Margin = { + top?: Space; + bottom?: Space; + left?: Space; + right?: Space; }; +export type Side = keyof Margin; + +const getMargin = (marginProp: Space | Margin | undefined, side: Side) => + marginProp && typeof marginProp === "object" + ? (marginProp[side] && spaces[marginProp[side]]) || "0px" + : marginProp && typeof marginProp === "string" + ? spaces[marginProp] + : "0px"; + +export default getMargin; diff --git a/packages/lib/src/container/Container.tsx b/packages/lib/src/container/Container.tsx index a32c845c72..b968f2f32c 100644 --- a/packages/lib/src/container/Container.tsx +++ b/packages/lib/src/container/Container.tsx @@ -21,9 +21,9 @@ const DxcContainer = ({ display, width, height, overflow, ...props }: ContainerP ); const getBorderStyles = (direction: "top" | "bottom" | "left" | "right", borderProperties: BorderProperties) => - `border-${direction}: ${borderProperties?.width ?? ""} ${borderProperties?.style ?? ""} ${ - getCoreColorToken(borderProperties?.color) ?? "" - };`; + `border-${direction}-width: ${borderProperties?.width ?? ""}; + border-${direction}-style: ${borderProperties?.style ?? ""}; + border-${direction}-color: ${borderProperties?.color ? getCoreColorToken(borderProperties?.color) : ""};`; const Container = styled.div` box-sizing: ${({ boxSizing }) => boxSizing}; @@ -45,7 +45,7 @@ const Container = styled.div` background-attachment: ${({ background }) => background?.attachment}; background-clip: ${({ background }) => background?.clip}; - background-color: ${({ background }) => getCoreColorToken(background?.color)}; + background-color: ${({ background }) => (background?.color ? getCoreColorToken(background?.color) : "")}; background-image: ${({ background }) => background?.image}; background-origin: ${({ background }) => background?.origin}; background-position: ${({ background }) => background?.position}; @@ -55,33 +55,38 @@ const Container = styled.div` border-radius: ${({ borderRadius }) => borderRadius}; border-width: ${({ border }) => (border && "width" in border ? `${border?.width}` : "")}; border-style: ${({ border }) => (border && "style" in border ? `${border?.style}` : "")}; - border-color: ${({ border }) => (border && "color" in border ? `${getCoreColorToken(border?.color)}` : "")}; + border-color: ${({ border }) => + border && "color" in border && border?.color ? `${getCoreColorToken(border?.color)}` : ""}; ${({ border }) => { if (border != null) { let styles = ""; - switch (true) { - case "top" in border: - styles += getBorderStyles("top", border.top); - case "right" in border: - styles += getBorderStyles("right", border.right); - case "left" in border: - styles += getBorderStyles("left", border.left); - case "bottom" in border: - styles += 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; } + return undefined; }}; margin: ${({ margin }) => (typeof margin === "string" ? spaces[margin] : "")}; - margin-top: ${({ margin }) => (typeof margin === "object" ? spaces[margin.top] : "")}; - margin-right: ${({ margin }) => (typeof margin === "object" ? spaces[margin.right] : "")}; - margin-bottom: ${({ margin }) => (typeof margin === "object" ? spaces[margin.bottom] : "")}; - margin-left: ${({ margin }) => (typeof margin === "object" ? spaces[margin.left] : "")}; + margin-top: ${({ margin }) => (typeof margin === "object" && margin.top ? spaces[margin.top] : "")}; + margin-right: ${({ margin }) => (typeof margin === "object" && margin.right ? spaces[margin.right] : "")}; + margin-bottom: ${({ margin }) => (typeof margin === "object" && margin.bottom ? spaces[margin.bottom] : "")}; + margin-left: ${({ margin }) => (typeof margin === "object" && margin.left ? spaces[margin.left] : "")}; - outline: ${({ outline }) => - `${outline?.width ?? ""} ${outline?.style ?? ""} ${getCoreColorToken(outline?.color) ?? ""}`}; + outline-width: ${({ outline }) => `${outline?.width ?? ""}`}; + outline-style: ${({ outline }) => `${outline?.style ?? ""}`}; + outline-color: ${({ outline }) => (outline?.color ? `${getCoreColorToken(outline?.color)}` : "")}; outline-offset: ${({ outline }) => outline?.offset}; overflow: ${({ $overflow }) => (typeof $overflow === "string" ? $overflow : "")}; @@ -89,10 +94,10 @@ const Container = styled.div` overflow-y: ${({ $overflow }) => (typeof $overflow === "object" ? `${$overflow?.y}` : "")}; padding: ${({ padding }) => (typeof padding === "string" ? spaces[padding] : "")}; - padding-top: ${({ padding }) => (typeof padding === "object" ? spaces[padding.top] : "")}; - padding-right: ${({ padding }) => (typeof padding === "object" ? spaces[padding.right] : "")}; - padding-bottom: ${({ padding }) => (typeof padding === "object" ? spaces[padding.bottom] : "")}; - padding-left: ${({ padding }) => (typeof padding === "object" ? spaces[padding.left] : "")}; + padding-top: ${({ padding }) => (typeof padding === "object" && padding.top ? spaces[padding.top] : "")}; + padding-right: ${({ padding }) => (typeof padding === "object" && padding.right ? spaces[padding.right] : "")}; + padding-bottom: ${({ padding }) => (typeof padding === "object" && padding.bottom ? spaces[padding.bottom] : "")}; + padding-left: ${({ padding }) => (typeof padding === "object" && padding.left ? spaces[padding.left] : "")}; `; export default DxcContainer; diff --git a/packages/lib/src/contextual-menu/ContextualMenu.tsx b/packages/lib/src/contextual-menu/ContextualMenu.tsx index 15fd2302df..b8cfe580b5 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.tsx @@ -16,19 +16,33 @@ import ContextualMenuPropsType, { import Section from "./Section"; const isGroupItem = (item: Item | GroupItem): item is GroupItem => "items" in item; -const isSection = (item?: SectionType | Item | GroupItem): item is SectionType => - item && "items" in item && !("label" in item); -const addIdToItems = (items: ContextualMenuPropsType["items"]): (ItemWithId | GroupItemWithId)[] | SectionWithId[] => { +const isSection = (item: SectionType | Item | GroupItem): item is SectionType => "items" in item && !("label" in item); + +const addIdToItems = (items: ContextualMenuPropsType["items"]): (ItemWithId | GroupItemWithId | SectionWithId)[] => { let accId = 0; - const innerAddIdToItems = (items: ContextualMenuPropsType["items"]) => { - return items.map((item: Item | GroupItem | SectionType) => - isSection(item) - ? { ...item, items: innerAddIdToItems(item.items) } - : isGroupItem(item) - ? { ...item, items: innerAddIdToItems(item.items) } - : { ...item, id: accId++ } - ); - }; + + const innerAddIdToItems = ( + innerItems: ContextualMenuPropsType["items"] + ): (ItemWithId | GroupItemWithId | SectionWithId)[] => + innerItems.map((item: Item | GroupItem | SectionType) => { + let newItem; + if (isSection(item)) { + newItem = { + ...item, + id: (accId += 1), + items: innerAddIdToItems(item.items), + } as SectionWithId; + } else if (isGroupItem(item)) { + newItem = { + ...item, + id: (accId += 1), + items: innerAddIdToItems(item.items), + } as GroupItemWithId; + } else { + newItem = { ...item, id: (accId += 1) } as ItemWithId; + } + return newItem; + }); return innerAddIdToItems(items); }; @@ -42,16 +56,19 @@ export const ContextualMenuContext = createContext { const [selectedItemId, setSelectedItemId] = useState(-1); - const contextualMenuRef = useRef(null); + const contextualMenuRef = useRef(null); const itemsWithId = useMemo(() => addIdToItems(items), [items]); + const contextValue = useMemo(() => ({ selectedItemId, setSelectedItemId }), [selectedItemId, setSelectedItemId]); const colorsTheme = useTheme(); const [firstUpdate, setFirstUpdate] = useState(true); useLayoutEffect(() => { if (selectedItemId !== -1 && firstUpdate) { const contextualMenuEl = contextualMenuRef?.current; - const selectedItemEl = contextualMenuEl?.querySelector("[aria-pressed='true']"); - contextualMenuEl?.scrollTo?.({ top: selectedItemEl?.offsetTop - contextualMenuEl?.clientHeight / 2 }); + const selectedItemEl = contextualMenuEl?.querySelector("[aria-pressed='true']") as HTMLButtonElement; + contextualMenuEl?.scrollTo?.({ + top: (selectedItemEl?.offsetTop || 0) - (contextualMenuEl?.clientHeight || 0) / 2, + }); setFirstUpdate(false); } }, [firstUpdate, selectedItemId]); @@ -59,8 +76,8 @@ const DxcContextualMenu = ({ items }: ContextualMenuPropsType) => { return ( - - {isSection(itemsWithId[0]) ? ( + + {isSection(itemsWithId[0] as SectionWithId) ? ( (itemsWithId as SectionWithId[]).map((item, index) => (
)) diff --git a/packages/lib/src/contextual-menu/GroupItem.tsx b/packages/lib/src/contextual-menu/GroupItem.tsx index 4be9b64433..9d1c7e4e4a 100644 --- a/packages/lib/src/contextual-menu/GroupItem.tsx +++ b/packages/lib/src/contextual-menu/GroupItem.tsx @@ -5,18 +5,20 @@ import ItemAction from "./ItemAction"; import MenuItem from "./MenuItem"; import { GroupItemProps, ItemWithId } from "./types"; -const isGroupSelected = (items: GroupItemProps["items"], selectedItemId: number): boolean => - 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; - }); +const isGroupSelected = (items: GroupItemProps["items"], selectedItemId: number | undefined): boolean => + items.some((item) => + "items" in item + ? isGroupSelected(item.items, selectedItemId) + : selectedItemId !== -1 + ? item.id === selectedItemId + : (item as ItemWithId).selectedByDefault + ); const GroupItem = ({ items, ...props }: GroupItemProps) => { const groupMenuId = `group-menu-${useId()}`; - const { selectedItemId } = useContext(ContextualMenuContext); + const { selectedItemId } = useContext(ContextualMenuContext) ?? {}; const groupSelected = useMemo(() => isGroupSelected(items, selectedItemId), [items, selectedItemId]); - const [isOpen, setIsOpen] = useState(groupSelected && selectedItemId === -1 ? true : false); + const [isOpen, setIsOpen] = useState(groupSelected && selectedItemId === -1); return ( <> @@ -26,7 +28,7 @@ const GroupItem = ({ items, ...props }: GroupItemProps) => { aria-pressed={groupSelected && !isOpen} collapseIcon={isOpen ? : } onClick={() => { - setIsOpen((isOpen) => !isOpen); + setIsOpen((isCurrentlyOpen) => !isCurrentlyOpen); }} selected={groupSelected && !isOpen} {...props} diff --git a/packages/lib/src/contextual-menu/SingleItem.tsx b/packages/lib/src/contextual-menu/SingleItem.tsx index b2559d3a52..10900a0971 100644 --- a/packages/lib/src/contextual-menu/SingleItem.tsx +++ b/packages/lib/src/contextual-menu/SingleItem.tsx @@ -4,22 +4,26 @@ import ItemAction from "./ItemAction"; import { SingleItemProps } from "./types"; const SingleItem = ({ id, onSelect, selectedByDefault, ...props }: SingleItemProps) => { - const { selectedItemId, setSelectedItemId } = useContext(ContextualMenuContext); + const { selectedItemId, setSelectedItemId } = useContext(ContextualMenuContext) ?? {}; const handleClick = () => { - setSelectedItemId(id); + setSelectedItemId?.(id); onSelect?.(); }; useEffect(() => { - if (selectedItemId === -1 && selectedByDefault) setSelectedItemId(id); + if (selectedItemId === -1 && selectedByDefault) { + setSelectedItemId?.(id); + } }, [selectedItemId, selectedByDefault, id]); return ( ); diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index 46100cbda7..f34c91106c 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -1,6 +1,7 @@ import { useEffect, useMemo, useState } from "react"; -import DataGridPropsType, { HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; import DataGrid, { SortColumn } from "react-data-grid"; +import styled, { ThemeProvider } from "styled-components"; +import DataGridPropsType, { HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; import "react-data-grid/lib/styles.css"; import { @@ -19,7 +20,6 @@ import { getMinItemsPerPageIndex, getMaxItemsPerPageIndex, } from "./utils"; -import styled, { ThemeProvider } from "styled-components"; import useTheme from "../useTheme"; import DxcPaginator from "../paginator/Paginator"; import { DxcActionsCell } from "../table/Table"; @@ -68,9 +68,7 @@ const DxcDataGrid = ({ // Proccess columns prop into usable columns based on other props const columnsToRender = useMemo(() => { - let expectedColumns = columns.map((column) => { - return convertToRDGColumns(column, summaryRow); - }); + let expectedColumns = columns.map((column) => convertToRDGColumns(column, summaryRow)); if (expandable) { expectedColumns = [ { @@ -85,7 +83,7 @@ const DxcDataGrid = ({ renderCell({ row }) { if (row.isExpandedChildContent) { // if it is expanded content - return row.expandedChildContent || <>; + return row.expandedChildContent || null; } // if row has expandable content return ( @@ -100,24 +98,26 @@ const DxcDataGrid = ({ } if (!expandable && rows.some((row) => Array.isArray(row.childRows) && row.childRows.length > 0) && uniqueRowId) { // only the first column will be clickable and will expand the rows - const firstColumnKey = expectedColumns[0].key; - expectedColumns[0] = { - ...expectedColumns[0], - renderCell({ row }) { - if ((row as HierarchyGridRow).childRows?.length) { + const firstColumnKey = expectedColumns[0]?.key; + if (firstColumnKey) { + expectedColumns[0] = { + ...expectedColumns[0]!, + renderCell({ row }) { + if ((row as HierarchyGridRow).childRows?.length) { + return ( + + {renderHierarchyTrigger(rowsToRender, row, uniqueRowId, firstColumnKey, setRowsToRender)} + + ); + } return ( - - {renderHierarchyTrigger(rowsToRender, row, uniqueRowId, firstColumnKey, setRowsToRender)} + + {row[firstColumnKey]} ); - } - return ( - - {row[firstColumnKey]} - - ); - }, - }; + }, + }; + } } if (selectable) { expectedColumns = [ @@ -135,14 +135,13 @@ const DxcDataGrid = ({ ); } + return null; }, - renderHeaderCell: () => { - return ( - - {renderHeaderCheckbox(rows, uniqueRowId, selectedRows, colorsTheme, onSelectRows)} - - ); - }, + renderHeaderCell: () => ( + + {renderHeaderCheckbox(rows, uniqueRowId, selectedRows, colorsTheme, onSelectRows)} + + ), }, ...expectedColumns, ]; @@ -161,32 +160,46 @@ const DxcDataGrid = ({ setRowsToRender(rows); }, [rows]); - const reorderedColumns = useMemo(() => { - // Array ordered by columnsOrder - return columnsOrder.map((index) => columnsToRender[index]); - }, [columnsOrder, columnsToRender]); + const reorderedColumns = useMemo( + () => + // Array ordered by columnsOrder + columnsOrder.map((index) => columnsToRender[index]!), + [columnsOrder, columnsToRender] + ); const onColumnsReorder = (sourceKey: string, targetKey: string) => { - setColumnsOrder((columnsOrder) => { - const sourceColumnOrderIndex = columnsOrder.findIndex((index) => columnsToRender[index].key === sourceKey); - const targetColumnOrderIndex = columnsOrder.findIndex((index) => columnsToRender[index].key === targetKey); - const newColumnsOrder = columnsOrder.slice(); - newColumnsOrder.splice(sourceColumnOrderIndex, 1); - newColumnsOrder.splice(targetColumnOrderIndex, 0, columnsOrder[sourceColumnOrderIndex]); + setColumnsOrder((currentColumnsOrder) => { + const sourceColumnOrderIndex = currentColumnsOrder.findIndex( + (index) => columnsToRender[index]?.key === sourceKey + ); + const targetColumnOrderIndex = currentColumnsOrder.findIndex( + (index) => columnsToRender[index]?.key === targetKey + ); + const newColumnsOrder = currentColumnsOrder.slice(); + if (sourceColumnOrderIndex === -1 || targetColumnOrderIndex === -1) { + return currentColumnsOrder; // Return the current order if an error is found + } + const itemToMove = currentColumnsOrder[sourceColumnOrderIndex]; + if (itemToMove != null) { + newColumnsOrder.splice(sourceColumnOrderIndex, 1); + newColumnsOrder.splice(targetColumnOrderIndex, 0, itemToMove); + } return newColumnsOrder; }); }; const onRowsChange = (newRows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]) => { // call function to change rows, like when they have been edited - if (typeof onGridRowsChange === "function") onGridRowsChange(newRows); + if (typeof onGridRowsChange === "function") { + onGridRowsChange(newRows); + } }; const sortedRows = useMemo((): readonly GridRow[] | HierarchyGridRow[] | ExpandableGridRow[] => { const sortFunctions = getCustomSortFn(columns); if (!onSort) { if (expandable && sortColumns.length > 0) { - const sortedRows = sortRows( + const innerSortedRows = sortRows( rowsToRender.filter((row) => !row.isExpandedChildContent), sortColumns, sortFunctions @@ -195,14 +208,16 @@ const DxcDataGrid = ({ .filter((row) => row.isExpandedChildContent) .map((expandedRow) => addRow( - sortedRows, - sortedRows.findIndex((trigger) => rowKeyGetter(trigger, uniqueRowId) === expandedRow.triggerRowKey) + 1, + innerSortedRows, + innerSortedRows.findIndex((trigger) => rowKeyGetter(trigger, uniqueRowId) === expandedRow.triggerRowKey) + + 1, expandedRow ) ); - return sortedRows; - } else if (!expandable && sortColumns.length > 0) { - if (uniqueRowId) return sortHierarchyRows(rowsToRender, sortColumns, sortFunctions, uniqueRowId); + return innerSortedRows; + } + if (!expandable && sortColumns.length > 0 && uniqueRowId) { + return sortHierarchyRows(rowsToRender, sortColumns, sortFunctions, uniqueRowId); } } return rowsToRender; @@ -234,7 +249,7 @@ const DxcDataGrid = ({ renderers={{ renderSortStatus }} sortColumns={sortColumns} onSortColumnsChange={handleSortChange} - rowKeyGetter={(row) => uniqueRowId && rowKeyGetter(row, uniqueRowId)} + rowKeyGetter={(row) => (uniqueRowId ? rowKeyGetter(row, uniqueRowId) : "")} rowHeight={(row) => { if ( row.isExpandedChildContent && @@ -243,12 +258,12 @@ const DxcDataGrid = ({ ) { return row.expandedContentHeight; } - return colorsTheme.dataGrid.dataRowHeight; + return colorsTheme?.dataGrid?.dataRowHeight ?? 0; }} selectedRows={selectedRows} bottomSummaryRows={summaryRow ? [summaryRow] : undefined} - headerRowHeight={colorsTheme.dataGrid.headerRowHeight} - summaryRowHeight={colorsTheme.dataGrid.summaryRowHeight} + headerRowHeight={colorsTheme?.dataGrid?.headerRowHeight} + summaryRowHeight={colorsTheme?.dataGrid?.summaryRowHeight} className="fill-grid" /> {showPaginator && ( diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index b6bfa637b5..6fe3619eaf 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -1,11 +1,14 @@ +// TODO: Remove eslint disable +/* eslint-disable no-param-reassign */ + 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"; import { AdvancedTheme } from "../common/variables"; import { DeepPartial, HalstackProvider } from "../HalstackContext"; import DxcIcon from "../icon/Icon"; import { GridColumn, HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; -import { Column, RenderSortStatusProps, SortColumn, textEditor } from "react-data-grid"; /** * Function to overwrite the checkbox theme based on a passed theme object. @@ -15,12 +18,12 @@ import { Column, RenderSortStatusProps, SortColumn, textEditor } from "react-dat const overwriteTheme = (theme: DeepPartial) => { const newTheme = { checkbox: { - backgroundColorChecked: theme.dataGrid.headerCheckboxBackgroundColorChecked, - hoverBackgroundColorChecked: theme.dataGrid.headerCheckboxHoverBackgroundColorChecked, - borderColor: theme.dataGrid.headerCheckboxBorderColor, - hoverBorderColor: theme.dataGrid.headerCheckboxHoverBorderColor, - checkColor: theme.dataGrid.headerCheckboxCheckColor, - focusColor: theme.dataGrid.focusColor, + backgroundColorChecked: theme?.dataGrid?.headerCheckboxBackgroundColorChecked, + hoverBackgroundColorChecked: theme?.dataGrid?.headerCheckboxHoverBackgroundColorChecked, + borderColor: theme?.dataGrid?.headerCheckboxBorderColor, + hoverBorderColor: theme?.dataGrid?.headerCheckboxHoverBorderColor, + checkColor: theme?.dataGrid?.headerCheckboxCheckColor, + focusColor: theme?.dataGrid?.focusColor, }, }; @@ -36,53 +39,45 @@ const overwriteTheme = (theme: DeepPartial) => { export const convertToRDGColumns = ( gridColumn: GridColumn, summaryRow?: GridRow -): Column => { - return { - key: gridColumn.key, - name: gridColumn.label, - resizable: gridColumn.resizable, - sortable: gridColumn.sortable, - draggable: gridColumn.draggable, - editable: gridColumn.textEditable, - headerCellClass: gridColumn.alignment ? `header-align-${gridColumn.alignment}` : `header-align-left`, - renderEditCell: gridColumn.textEditable ? textEditor : undefined, - renderCell: ({ row }) => { - return ( -
- {row[gridColumn.key]} -
- ); - }, - renderSummaryCell: () => { - return gridColumn.summaryKey ? ( -
- {summaryRow?.[gridColumn.summaryKey]} -
- ) : undefined; - }, - }; -}; +): Column => ({ + key: gridColumn.key, + name: gridColumn.label, + resizable: gridColumn.resizable, + sortable: gridColumn.sortable, + draggable: gridColumn.draggable, + editable: gridColumn.textEditable, + headerCellClass: gridColumn.alignment ? `header-align-${gridColumn.alignment}` : `header-align-left`, + renderEditCell: gridColumn.textEditable ? textEditor : undefined, + renderCell: ({ row }) => ( +
+ {row[gridColumn.key]} +
+ ), + renderSummaryCell: () => gridColumn.summaryKey ? ( +
+ {summaryRow?.[gridColumn.summaryKey]} +
+ ) : undefined, +}); /** * Renders sort status icon for a column based on the sort direction. * @param {RenderSortStatusProps} props - Properties including sortDirection to render the appropriate sort icon. * @returns {JSX.Element} Icon indicating the current sort status. */ -export const renderSortStatus = ({ sortDirection }: RenderSortStatusProps) => { - return ( -
- {sortDirection !== undefined ? ( - sortDirection === "ASC" ? ( - - ) : ( - - ) +export const renderSortStatus = ({ sortDirection }: RenderSortStatusProps) => ( +
+ {sortDirection !== undefined ? ( + sortDirection === "ASC" ? ( + ) : ( - - )} -
- ); -}; + + ) + ) : ( + + )} +
+); /** * Renders an expandable trigger icon that toggles row expansion. @@ -97,39 +92,37 @@ export const renderExpandableTrigger = ( rows: ExpandableGridRow[], uniqueRowId: string, setRowsToRender: (_value: SetStateAction) => void -) => { - return ( - { - row.contentIsExpanded = !row.contentIsExpanded; - if (row.contentIsExpanded) { - const rowIndex = rows.findIndex((rowToRender) => row === rowToRender); - setRowsToRender((rows) => { - const newRows = [...rows]; - addRow(newRows, rowIndex + 1, { - isExpandedChildContent: row.contentIsExpanded, - [uniqueRowId]: rowKeyGetter(row, uniqueRowId) + "_expanded", - expandedChildContent: row.expandedContent, - triggerRowKey: rowKeyGetter(row, uniqueRowId), - expandedContentHeight: row.expandedContentHeight, - }); - return newRows; +) => ( + { + row.contentIsExpanded = !row.contentIsExpanded; + if (row.contentIsExpanded) { + const rowIndex = rows.findIndex((rowToRender) => row === rowToRender); + setRowsToRender((currentRows) => { + const newRows = [...currentRows]; + addRow(newRows, rowIndex + 1, { + isExpandedChildContent: row.contentIsExpanded, + [uniqueRowId]: `${rowKeyGetter(row, uniqueRowId)}_expanded`, + expandedChildContent: row.expandedContent, + triggerRowKey: rowKeyGetter(row, uniqueRowId), + expandedContentHeight: row.expandedContentHeight, }); - } else { - const rowIndex = rows.findIndex((rowToRender) => row === rowToRender); - setRowsToRender((rows) => { - const newRows = [...rows]; - deleteRow(newRows, rowIndex + 1); - return newRows; - }); - } - }} - /> - ); -}; + return newRows; + }); + } else { + const rowIndex = rows.findIndex((rowToRender) => row === rowToRender); + setRowsToRender((currentRows) => { + const newRows = [...currentRows]; + deleteRow(newRows, rowIndex + 1); + return newRows; + }); + } + }} + /> +); /** * Renders a trigger for hierarchical row expansion in the grid. @@ -146,63 +139,62 @@ export const renderHierarchyTrigger = ( uniqueRowId: string, columnKey: string, setRowsToRender: (_value: SetStateAction) => void -) => { - return ( - - ); -}; + newRowsToRender = rows.filter( + (row) => + !rowsToRemove + .map((rowToRemove) => { + if (rowToRemove.visibleChildren) { + rowToRemove.visibleChildren = false; + } + return rowKeyGetter(rowToRemove, uniqueRowId); + }) + .includes(rowKeyGetter(row, uniqueRowId)) + ); + } + triggerRow.visibleChildren = !triggerRow.visibleChildren; + setRowsToRender(newRowsToRender); + }} + > + + {triggerRow[columnKey]} + +); /** * Renders a checkbox for row selection. * @param {GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]} rows - Array of rows that are currently displayed. * @param {GridRow | HierarchyGridRow | ExpandableGridRow} row - Row object to render the checkbox for. * @param {string} uniqueRowId - The key used to uniquely identify each row. - * @param {Set} selectedRows - Set containing the IDs of selected rows. + * @param {Set} selectedRows - Set containing the IDs of selected rows. * @param {Function} onSelectRows - Callback function that triggers when rows are selected/deselected. * @returns {JSX.Element} Checkbox for selecting the row. */ @@ -210,32 +202,34 @@ export const renderCheckbox = ( rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], row: GridRow | HierarchyGridRow | ExpandableGridRow, uniqueRowId: string, - selectedRows: Set, - onSelectRows: (_selected: Set) => void -) => { - return ( - { - const selected = new Set(selectedRows); - checked ? selected.add(rowKeyGetter(row, uniqueRowId)) : selected.delete(rowKeyGetter(row, uniqueRowId)); - if (row.childRows && Array.isArray(row.childRows)) { - getChildrenSelection(row.childRows, uniqueRowId, selected, checked); - } - if (row.parentKey) { - getParentSelectedState(rows, row.parentKey, uniqueRowId, selected, checked); - } - onSelectRows(selected); - }} - /> - ); -}; + selectedRows: Set, + onSelectRows: (_selected: Set) => void +) => ( + { + const selected = new Set(selectedRows); + if (checked) { + selected.add(rowKeyGetter(row, uniqueRowId)); + } else { + selected.delete(rowKeyGetter(row, uniqueRowId)); + } + if (row.childRows && Array.isArray(row.childRows)) { + getChildrenSelection(row.childRows, uniqueRowId, selected, checked); + } + if (row.parentKey) { + getParentSelectedState(rows, row.parentKey, uniqueRowId, selected, checked); + } + onSelectRows(selected); + }} + /> +); /** * Renders a header checkbox that controls the selection of all rows. * @param {GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]} rows - Array of rows that are currently displayed. * @param {string} uniqueRowId - The key used to uniquely identify each row. - * @param {Set} selectedRows - Set containing the IDs of selected rows. + * @param {Set} selectedRows - Set containing the IDs of selected rows. * @param {DeepPartial} colorsTheme - Custom theme colors for the checkbox. * @param {Function} onSelectRows - Callback function that triggers when rows are selected/deselected. * @returns {JSX.Element} Checkbox for the header checkbox. @@ -243,37 +237,37 @@ export const renderCheckbox = ( export const renderHeaderCheckbox = ( rows: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], uniqueRowId: string, - selectedRows: Set, + selectedRows: Set, colorsTheme: DeepPartial, - onSelectRows: (_selected: Set) => void -) => { - return ( - - !selectedRows.has(rowKeyGetter(row, uniqueRowId)))} - onChange={(checked) => { - const updatedSelection = new Set(selectedRows); - - if (checked) { - rows.forEach((row) => { - updatedSelection.add(rowKeyGetter(row, uniqueRowId)); - if (row.childRows && Array.isArray(row.childRows)) - getChildrenSelection(row.childRows, uniqueRowId, updatedSelection, checked); - }); - } else { - rows.forEach((row) => { - updatedSelection.delete(rowKeyGetter(row, uniqueRowId)); - if (row.childRows && Array.isArray(row.childRows)) - getChildrenSelection(row.childRows, uniqueRowId, updatedSelection, checked); - }); - } + onSelectRows: (_selected: Set) => void +) => ( + + !selectedRows.has(rowKeyGetter(row, uniqueRowId)))} + onChange={(checked) => { + const updatedSelection = new Set(selectedRows); - onSelectRows(updatedSelection); - }} - /> - - ); -}; + if (checked) { + rows.forEach((row) => { + updatedSelection.add(rowKeyGetter(row, uniqueRowId)); + if (row.childRows && Array.isArray(row.childRows)) { + getChildrenSelection(row.childRows, uniqueRowId, updatedSelection, checked); + } + }); + } else { + rows.forEach((row) => { + updatedSelection.delete(rowKeyGetter(row, uniqueRowId)); + if (row.childRows && Array.isArray(row.childRows)) { + getChildrenSelection(row.childRows, uniqueRowId, updatedSelection, checked); + } + }); + } + + onSelectRows(updatedSelection); + }} + /> + +); /** * Retrieves the unique key for a row based on the given uniqueRowId. @@ -283,7 +277,7 @@ export const renderHeaderCheckbox = ( */ export const rowKeyGetter = (row: GridRow | HierarchyGridRow | ExpandableGridRow, uniqueRowId: string) => { const keyValue = row[uniqueRowId]; - return typeof keyValue === "string" || typeof keyValue === "number" ? keyValue : null; + return typeof keyValue === "string" || typeof keyValue === "number" ? keyValue : ""; }; /** @@ -294,7 +288,7 @@ export const rowKeyGetter = (row: GridRow | HierarchyGridRow | ExpandableGridRow export const getCustomSortFn = (columns: GridColumn[]) => { const customSortFunctions = [...columns] .filter((column) => column.sortFn) - .map((column) => ({ column: column.key, sortFn: column.sortFn })); + .map((column) => ({ column: column.key, sortFn: column.sortFn! })); return customSortFunctions; }; @@ -307,10 +301,13 @@ export const getCustomSortFn = (columns: GridColumn[]) => { * @returns {number} -1 if rowA < rowB, 1 if rowA > rowB, or 0 if they are equal. */ export const compareRows = (rowA: ReactNode, rowB: ReactNode, sortFn?: (_a: ReactNode, _b: ReactNode) => number) => { - if (!sortFn) { - return rowA > rowB ? 1 : rowA < rowB ? -1 : 0; + if (rowA != null && rowB != null) { + if (!sortFn) { + return rowA > rowB ? 1 : rowA < rowB ? -1 : 0; + } + return sortFn(rowA, rowB); } - return sortFn(rowA, rowB); + return 0; }; /** @@ -324,33 +321,36 @@ export const compareRows = (rowA: ReactNode, rowB: ReactNode, sortFn?: (_a: Reac export const sortRows = ( rows: GridRow[] | ExpandableGridRow[] | HierarchyGridRow[], sortColumns: readonly SortColumn[], - sortFunctions: { column: string; sortFn: (_a: ReactNode, _b: ReactNode) => number }[], + sortFunctions?: { column: string; sortFn: (_a: ReactNode, _b: ReactNode) => number }[], reversed?: boolean -) => { - return [...rows].sort((a, b) => { - for (const sort of sortColumns) { +) => + [...rows].sort((a, b) => + sortColumns.reduce((compResult, sort) => { + if (compResult !== 0) { + return compResult; + } + const sortValueA = a[sort.columnKey]; const sortValueB = b[sort.columnKey]; - let compResult = 0; + let newCompResult = compResult; if (sortValueA && sortValueB) { - compResult = compareRows( - sortValueA, - sortValueB, - sortFunctions?.find(({ column }) => { - return column === sort.columnKey; - })?.sortFn - ); + const sortFn = sortFunctions?.find(({ column }) => column === sort.columnKey)?.sortFn; + newCompResult = compareRows(sortValueA, sortValueB, sortFn); } - if (compResult !== 0) { - if (reversed) return sort.direction === "ASC" ? -compResult : compResult; - return sort.direction === "ASC" ? compResult : -compResult; + if (newCompResult !== 0) { + return reversed + ? sort.direction === "ASC" + ? -newCompResult + : newCompResult + : sort.direction === "ASC" + ? newCompResult + : -newCompResult; } - } - return 0; - }); -}; + return newCompResult; + }, 0) + ); /** * Sorts an array of rows, ensuring child rows are placed under their parents. @@ -372,38 +372,38 @@ export const sortHierarchyRows = ( sortFunctions ); // check if there are child rows - if (rows.length === parentsSorted.length) return parentsSorted; - else { - let sortedChildren = sortRows( - rows.filter((row) => row.parentKey), - sortColumns, - sortFunctions, - true - ); - // add children directly under the parent if it is available - while (sortedChildren.length) { - if (uniqueRowId) { - sortedChildren = sortedChildren.reduce( - ( - remainingChilds: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], - child: GridRow | HierarchyGridRow | ExpandableGridRow - ) => { - const parentIndex = parentsSorted.findIndex( - (parent) => rowKeyGetter(parent, uniqueRowId) === child.parentKey - ); - if (parentIndex >= 0) { - parentsSorted.splice(parentIndex + 1, 0, child); - } else { - remainingChilds.push(child); - } - return remainingChilds; - }, - [] - ); - } - } + if (rows.length === parentsSorted.length) { return parentsSorted; } + let sortedChildren = sortRows( + rows.filter((row) => row.parentKey), + sortColumns, + sortFunctions, + true + ); + // add children directly under the parent if it is available + while (sortedChildren.length) { + if (uniqueRowId) { + sortedChildren = sortedChildren.reduce( + ( + remainingChilds: GridRow[] | HierarchyGridRow[] | ExpandableGridRow[], + child: GridRow | HierarchyGridRow | ExpandableGridRow + ) => { + const parentIndex = parentsSorted.findIndex( + (parent) => rowKeyGetter(parent, uniqueRowId) === child.parentKey + ); + if (parentIndex >= 0) { + parentsSorted.splice(parentIndex + 1, 0, child); + } else { + remainingChilds.push(child); + } + return remainingChilds; + }, + [] + ); + } + } + return parentsSorted; }; /** @@ -440,7 +440,7 @@ export const rowFinderBasedOnId = ( uniqueRowId: string, uniqueRowIdValue: ReactNode ): GridRow | HierarchyGridRow | ExpandableGridRow | undefined => { - let foundRow: GridRow | HierarchyGridRow | ExpandableGridRow | undefined = undefined; + let foundRow: GridRow | HierarchyGridRow | ExpandableGridRow | undefined; rowList.forEach((row) => { if (rowKeyGetter(row, uniqueRowId) === uniqueRowIdValue) { foundRow = { ...row }; @@ -449,7 +449,10 @@ export const rowFinderBasedOnId = ( foundRow = rowFinderBasedOnId(row.childRows, uniqueRowId, uniqueRowIdValue); } }); - if (foundRow) return foundRow; + if (foundRow) { + return foundRow; + } + return undefined; }; /** @@ -470,8 +473,9 @@ export const getChildrenSelection = ( // Recursively select/deselect child rows getChildrenSelection(row.childRows, uniqueRowId, selectedRows, checked); } - if (checked) selectedRows.add(rowKeyGetter(row, uniqueRowId)); - else { + if (checked) { + selectedRows.add(rowKeyGetter(row, uniqueRowId)); + } else { selectedRows.delete(rowKeyGetter(row, uniqueRowId)); } }); @@ -493,25 +497,23 @@ export const getParentSelectedState = ( selectedRows: Set, checkedStateToMatch: boolean ) => { - const parentRow: HierarchyGridRow = rowFinderBasedOnId(rowList, uniqueRowId, parentKeyValue); + const parentRow = rowFinderBasedOnId(rowList, uniqueRowId, parentKeyValue) as HierarchyGridRow; - if (!parentRow) return; + if (!parentRow) { + return; + } // Check if we are unselecting or any of the other direct childRows is unselected - const isAnyChildUnselected = parentRow.childRows?.some((row) => { - return !selectedRows.has(rowKeyGetter(row, uniqueRowId)); - }); + const isAnyChildUnselected = parentRow.childRows?.some((row) => !selectedRows.has(rowKeyGetter(row, uniqueRowId))); if (!checkedStateToMatch || isAnyChildUnselected) { // If the parent is selected but should not be, deselect it if (selectedRows.has(rowKeyGetter(parentRow, uniqueRowId))) { selectedRows.delete(rowKeyGetter(parentRow, uniqueRowId)); } - } else { + } else if (parentRow.childRows && !isAnyChildUnselected) { // If all child rows are selected, we select the parent - if (parentRow.childRows && !isAnyChildUnselected) { - selectedRows.add(rowKeyGetter(parentRow, uniqueRowId)); - } + selectedRows.add(rowKeyGetter(parentRow, uniqueRowId)); } // Recursively check the parent's parent if necessary @@ -555,10 +557,13 @@ export const getMaxItemsPerPageIndex = ( */ export const getPaginatedNodes = ( rows: readonly GridRow[] | ExpandableGridRow[] | HierarchyGridRow[], - uniqueRowId: string, + uniqueRowId?: string, start?: number, end?: number ): readonly GridRow[] | ExpandableGridRow[] | HierarchyGridRow[] => { + if (!uniqueRowId) { + return rows.slice(start, end); + } const rowsToPaginate: HierarchyGridRow[] = start != null && end != null ? rows @@ -571,18 +576,18 @@ export const getPaginatedNodes = ( if (row[uniqueRowId] === targetId) { return true; } - return row.childRows?.some((child) => isRowInHierarchy(child, targetId)); + return !!row?.childRows?.some((child) => isRowInHierarchy(child, targetId)); }; // Filter rows to include only those that are within the pagination range or are child rows - return rows.filter((row) => { - return rowsToPaginate.some( + return rows.filter((row) => + rowsToPaginate.some( (rowToPaginate) => rowToPaginate[uniqueRowId] === row[uniqueRowId] || (rowToPaginate.contentIsExpanded && row?.triggerRowKey === rowToPaginate[uniqueRowId] && row?.isExpandedChildContent) || rowToPaginate?.childRows?.some((child) => isRowInHierarchy(child, row[uniqueRowId])) - ); - }); + ) + ); }; diff --git a/packages/lib/src/date-input/Calendar.tsx b/packages/lib/src/date-input/Calendar.tsx index 682831c5f8..db620d80a5 100644 --- a/packages/lib/src/date-input/Calendar.tsx +++ b/packages/lib/src/date-input/Calendar.tsx @@ -1,11 +1,13 @@ import { Dayjs } from "dayjs"; -import { useState, useMemo, useEffect, useId, memo } from "react"; +import { useState, useMemo, useEffect, useId, memo, KeyboardEvent, FocusEvent } from "react"; import styled from "styled-components"; import { CalendarPropsType } from "./types"; import useTranslatedLabels from "../useTranslatedLabels"; +type Date = { day: number; month: number; year: number }; + const getDays = (innerDate: Dayjs) => { - const monthDayCells = []; + const monthDayCells: Date[] = []; const lastMonthNumberOfDays = innerDate.set("month", innerDate.get("month") - 1).endOf("month"); const firstDayOfMonth = innerDate.startOf("month").day() === 0 ? 6 : innerDate.startOf("month").day() - 1; const daysInMonth = firstDayOfMonth + innerDate.daysInMonth(); @@ -34,24 +36,22 @@ const getDays = (innerDate: Dayjs) => { return monthDayCells; }; -const getDateToFocus = (selectedDate, innerDate, today) => { - return selectedDate?.get("month") === innerDate.get("month") && selectedDate?.get("year") === innerDate.get("year") +const getDateToFocus = (selectedDate: Dayjs, innerDate: Dayjs, today: Dayjs) => + selectedDate?.get("month") === innerDate.get("month") && selectedDate?.get("year") === innerDate.get("year") ? selectedDate : today.get("month") === innerDate.get("month") && today.get("year") === innerDate.get("year") ? today : innerDate.set("date", 1); -}; -const isDaySelected = (date: { day: number; month: number; year: number }, selectedDate) => +const isDaySelected = (date: Date, selectedDate: Dayjs) => selectedDate?.get("month") === date.month && selectedDate?.get("year") === date.year && selectedDate?.get("date") === date.day; -const divideDaysIntoWeeks = (data: any[], weekSize: number) => { - return Array.from({ length: Math.ceil(data.length / weekSize) }, (_, rowIndex) => +const divideDaysIntoWeeks = (data: Date[], weekSize: number) => + Array.from({ length: Math.ceil(data.length / weekSize) }, (_, rowIndex) => data.slice(rowIndex * weekSize, (rowIndex + 1) * weekSize) ); -}; const Calendar = ({ selectedDate, @@ -65,15 +65,15 @@ const Calendar = ({ const [isFocusable, setIsFocusable] = useState(false); const dayCells = useMemo(() => getDays(innerDate), [innerDate]); const translatedLabels = useTranslatedLabels(); - const weekDays = translatedLabels.calendar.daysShort; + const weekDays = translatedLabels?.calendar?.daysShort ?? []; - const onDateClickHandler = (date: { day: number; month: number; year: number }) => { + const onDateClickHandler = (date: Date) => { const newDate = innerDate.set("month", date.month).set("date", date.day); onDaySelect(newDate); setDateToFocus(newDate); }; - const handleOnBlur = (event) => { + const handleOnBlur = (event: FocusEvent) => { if (!event?.currentTarget.contains(event.relatedTarget)) { setDateToFocus(getDateToFocus(selectedDate, innerDate, today)); } @@ -100,7 +100,7 @@ const Calendar = ({ } }, [innerDate, dateToFocus, selectedDate, today]); - const handleDayKeyboardEvent = (event, date) => { + const handleDayKeyboardEvent = (event: KeyboardEvent, date: Date) => { let dateToFocusTemp = date.month === innerDate.get("month") ? innerDate.set("date", date.day) @@ -109,16 +109,20 @@ const Calendar = ({ switch (event.key) { case "PageUp": event.preventDefault(); - event.shiftKey - ? (dateToFocusTemp = dateToFocusTemp.set("year", dateToFocusTemp.get("year") - 1)) - : (dateToFocusTemp = dateToFocusTemp.set("month", dateToFocusTemp.get("month") - 1)); + if (event.shiftKey) { + dateToFocusTemp = dateToFocusTemp.set("year", dateToFocusTemp.get("year") - 1); + } else { + dateToFocusTemp = dateToFocusTemp.set("month", dateToFocusTemp.get("month") - 1); + } focusDate(dateToFocusTemp); break; case "PageDown": event.preventDefault(); - event.shiftKey - ? (dateToFocusTemp = dateToFocusTemp.set("year", dateToFocusTemp.get("year") + 1)) - : (dateToFocusTemp = dateToFocusTemp.set("month", dateToFocusTemp.get("month") + 1)); + if (event.shiftKey) { + dateToFocusTemp = dateToFocusTemp.set("year", dateToFocusTemp.get("year") + 1); + } else { + dateToFocusTemp = dateToFocusTemp.set("month", dateToFocusTemp.get("month") + 1); + } focusDate(dateToFocusTemp); break; case "ArrowLeft": @@ -143,20 +147,26 @@ const Calendar = ({ break; case "Home": event.preventDefault(); - dateToFocus.get("day") !== 0 - ? (dateToFocusTemp = dateToFocusTemp.day(1)) - : (dateToFocusTemp = innerDate.date(date.day - 1).day(1)); + if (dateToFocus.get("day") !== 0) { + dateToFocusTemp = dateToFocusTemp.day(1); + } else { + dateToFocusTemp = innerDate.date(date.day - 1).day(1); + } focusDate(dateToFocusTemp); break; case "End": event.preventDefault(); - dateToFocusTemp.get("day") !== 0 && (dateToFocusTemp = dateToFocusTemp.day(7)); + if (dateToFocusTemp.get("day") !== 0) { + dateToFocusTemp = dateToFocusTemp.day(7); + } focusDate(dateToFocusTemp); break; case " ": event.preventDefault(); onDaySelect(dateToFocusTemp); break; + default: + break; } }; return ( @@ -174,6 +184,7 @@ const Calendar = ({ {week.map((date) => ( handleDayKeyboardEvent(event, date)} diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index 4f6db7360e..b89a14b411 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -1,13 +1,24 @@ -import { useState, useRef, useEffect, useId, useCallback, forwardRef } from "react"; -import dayjs from "dayjs"; +import { + useState, + useRef, + useEffect, + useId, + useCallback, + forwardRef, + Dispatch, + SetStateAction, + FocusEvent, + KeyboardEvent, +} from "react"; +import dayjs, { Dayjs } from "dayjs"; import styled, { ThemeProvider } from "styled-components"; +import * as Popover from "@radix-ui/react-popover"; +import customParseFormat from "dayjs/plugin/customParseFormat"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import DateInputPropsType, { RefType } from "./types"; import DatePicker from "./DatePicker"; -import * as Popover from "@radix-ui/react-popover"; -import customParseFormat from "dayjs/plugin/customParseFormat"; -import { getMargin } from "../common/utils"; +import getMargin from "../common/utils"; import { spaces } from "../common/variables"; import DxcTextInput from "../text-input/TextInput"; @@ -15,23 +26,30 @@ dayjs.extend(customParseFormat); const SIDEOFFSET = 4; -const getValueForPicker = (value, format) => dayjs(value, format.toUpperCase(), true); +const getValueForPicker = (value: string, format: string) => dayjs(value, format.toUpperCase(), true); -const getDate = (value, format, lastValidYear, setLastValidYear) => { - if ((value || value === "") && format.toUpperCase().includes("YYYY")) return getValueForPicker(value, format); - else { - let newDate = getValueForPicker(value, format); - if (!lastValidYear) { - if (+newDate.format("YY") < 68) { - setLastValidYear(2000 + +newDate.format("YY")); - newDate = newDate.set("year", 2000 + +newDate.format("YY")); - } else { - setLastValidYear(1900 + +newDate.format("YY")); - newDate = newDate.set("year", 1900 + +newDate.format("YY")); - } - } else newDate = newDate.set("year", (lastValidYear <= 1999 ? 1900 : 2000) + +newDate.format("YY")); - return newDate; +const getDate = ( + value: string, + format: string, + lastValidYear: number | undefined, + setLastValidYear: Dispatch> +) => { + if ((value || value === "") && format.toUpperCase().includes("YYYY")) { + return getValueForPicker(value, format); + } + let newDate = getValueForPicker(value, format); + if (lastValidYear == null) { + if (+newDate.format("YY") < 68) { + setLastValidYear(2000 + +newDate.format("YY")); + newDate = newDate.set("year", 2000 + +newDate.format("YY")); + } else { + setLastValidYear(1900 + +newDate.format("YY")); + newDate = newDate.set("year", 1900 + +newDate.format("YY")); + } + } else { + newDate = newDate.set("year", (lastValidYear <= 1999 ? 1900 : 2000) + +newDate.format("YY")); } + return newDate; }; const DxcDateInput = forwardRef( @@ -62,7 +80,7 @@ const DxcDateInput = forwardRef( const [isOpen, setIsOpen] = useState(false); const calendarId = `date-picker-${useId()}`; const [dayjsDate, setDayjsDate] = useState(getValueForPicker(value ?? defaultValue ?? "", format)); - const [lastValidYear, setLastValidYear] = useState( + const [lastValidYear, setLastValidYear] = useState( innerValue || value ? !format.toUpperCase().includes("YYYY") && +getValueForPicker(value ?? innerValue, format).format("YY") < 68 ? 2000 @@ -72,35 +90,39 @@ const DxcDateInput = forwardRef( const [sideOffset, setSideOffset] = useState(SIDEOFFSET); const colorsTheme = useTheme(); const translatedLabels = useTranslatedLabels(); - const dateRef = useRef(null); - const popoverContentRef = useRef(null); + const dateRef = useRef(null); + const popoverContentRef = useRef(null); - const handleCalendarOnClick = (newDate) => { + const handleCalendarOnClick = (newDate: Dayjs) => { const newValue = newDate.format(format.toUpperCase()); if (!value) { setDayjsDate(newDate); setInnerValue(newValue); } setLastValidYear(newDate.get("year")); - newDate?.set("day", newDate.get("date")).toJSON() - ? onChange?.({ - value: newValue, - date: newDate.toDate(), - }) - : onChange?.({ - value: newValue, - }); + if (newDate?.set("day", newDate.get("date")).toJSON()) { + onChange?.({ + value: newValue, + date: newDate.toDate(), + }); + } else { + onChange?.({ + value: newValue, + }); + } }; - const handleOnChange = ({ value: newValue, error: inputError }) => { - value ?? setInnerValue(newValue); + const handleOnChange = ({ value: newValue, error: inputError }: { value: string; error?: string }) => { + if (value == null) { + setInnerValue(newValue); + } const newDate = getDate(newValue, format, lastValidYear, setLastValidYear); const invalidDateMessage = - newValue !== "" && !newDate.isValid() && translatedLabels.dateInput.invalidDateErrorMessage; - const callbackParams = - inputError || invalidDateMessage - ? { value: newValue, error: inputError || invalidDateMessage } - : { value: newValue }; + newValue !== "" && !newDate.isValid() && translatedLabels?.dateInput?.invalidDateErrorMessage; + const callbackParams = { + value: newValue, + error: inputError || invalidDateMessage || undefined, + }; if (newDate.isValid()) { setDayjsDate(newDate); onChange?.({ @@ -110,20 +132,25 @@ const DxcDateInput = forwardRef( } else { onChange?.(callbackParams); setLastValidYear((validYear) => dayjsDate?.get("year") ?? validYear); - setDayjsDate(null); + setDayjsDate(dayjs(null)); } }; - const handleOnBlur = ({ value, error: inputError }) => { - const date = getDate(value, format, lastValidYear, setLastValidYear); - const invalidDateMessage = value !== "" && !date.isValid() && translatedLabels.dateInput.invalidDateErrorMessage; - const callbackParams = - inputError || invalidDateMessage ? { value, error: inputError || invalidDateMessage } : { value }; - date.isValid() - ? onBlur?.({ - ...callbackParams, - date: date.toDate(), - }) - : onBlur?.(callbackParams); + const handleOnBlur = ({ value: blurValue, error: inputError }: { value: string; error?: string }) => { + const date = getDate(blurValue, format, lastValidYear, setLastValidYear); + const invalidDateMessage = + blurValue !== "" && !date.isValid() && translatedLabels?.dateInput?.invalidDateErrorMessage; + const callbackParams = { + value: blurValue, + error: inputError || invalidDateMessage || undefined, + }; + if (date.isValid()) { + onBlur?.({ + ...callbackParams, + date: date.toDate(), + }); + } else { + onBlur?.(callbackParams); + } }; const adjustSideOffset = useCallback(() => { @@ -135,7 +162,13 @@ const DxcDateInput = forwardRef( const errorMessageHeight = dateRef.current .querySelector('[id^="error-input"]') ?.getBoundingClientRect().height; - setSideOffset(popoverRect.top > triggerRect.bottom ? -errorMessageHeight : SIDEOFFSET); + setSideOffset( + triggerRect?.bottom != null && popoverRect.top > triggerRect.bottom + ? errorMessageHeight != null + ? -errorMessageHeight + : 0 + : SIDEOFFSET + ); } }, 0); } @@ -149,16 +182,20 @@ const DxcDateInput = forwardRef( setIsOpen(false); }; - const handleDatePickerEscKeydown = (event: React.KeyboardEvent) => { + const handleDatePickerEscKeydown = (event: KeyboardEvent) => { if (event.key === "Escape") { event.preventDefault(); - isOpen && event.stopPropagation(); + if (isOpen) { + event.stopPropagation(); + } closeCalendar(); - dateRef?.current.getElementsByTagName("input")[0].focus(); + dateRef?.current?.getElementsByTagName("input")[0]?.focus(); } }; - const handleDatePickerOnBlur = (event) => { - if (!event?.currentTarget.contains(event.relatedTarget)) closeCalendar(); + const handleDatePickerOnBlur = (event: FocusEvent) => { + if (!event?.currentTarget.contains(event.relatedTarget)) { + closeCalendar(); + } }; useEffect(() => { @@ -169,15 +206,17 @@ const DxcDateInput = forwardRef( }, [adjustSideOffset]); useEffect(() => { - if (value || value === "") setDayjsDate(getDate(value, format, lastValidYear, setLastValidYear)); + if (value || value === "") { + setDayjsDate(getDate(value, format, lastValidYear, setLastValidYear)); + } }, [value, format, lastValidYear]); useEffect(() => { if (!disabled) { - const actionButtonRef = dateRef?.current.querySelector("[aria-label='Select date']"); - actionButtonRef?.setAttribute("aria-haspopup", true); + const actionButtonRef = dateRef?.current?.querySelector("[aria-label='Select date']"); + actionButtonRef?.setAttribute("aria-haspopup", "true"); actionButtonRef?.setAttribute("role", "combobox"); - actionButtonRef?.setAttribute("aria-expanded", isOpen); + actionButtonRef?.setAttribute("aria-expanded", `${isOpen}`); actionButtonRef?.setAttribute("aria-controls", calendarId); if (isOpen) { actionButtonRef?.setAttribute("aria-describedby", calendarId); @@ -190,11 +229,11 @@ const DxcDateInput = forwardRef( {label && ( )} {helperText && {helperText}} @@ -204,7 +243,7 @@ const DxcDateInput = forwardRef( name={name} defaultValue={defaultValue} value={value ?? innerValue} - placeholder={placeholder ? format.toUpperCase() : null} + placeholder={placeholder ? format.toUpperCase() : undefined} action={{ onClick: openCalendar, icon: "filled_calendar_today", @@ -227,7 +266,7 @@ const DxcDateInput = forwardRef( +const calculateWidth = (margin: DateInputPropsType["margin"], size: DateInputPropsType["size"]) => size === "fillParent" ? `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})` - : sizes[size]; + : size && sizes[size]; const DateInputContainer = styled.div<{ margin: DateInputPropsType["margin"]; size: DateInputPropsType["size"] }>` - ${(props) => props.size == "fillParent" && "width: 100%;"} + ${(props) => props.size === "fillParent" && "width: 100%;"} display: flex; flex-direction: column; width: ${(props) => calculateWidth(props.margin, props.size)}; - ${(props) => props.size !== "fillParent" && "min-width:" + calculateWidth(props.margin, props.size)}; + ${(props) => props.size !== "fillParent" && `min-width:${calculateWidth(props.margin, props.size)}`}; margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; margin-top: ${(props) => props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; diff --git a/packages/lib/src/dialog/Dialog.tsx b/packages/lib/src/dialog/Dialog.tsx index bc217cb52c..f3be2a65b4 100644 --- a/packages/lib/src/dialog/Dialog.tsx +++ b/packages/lib/src/dialog/Dialog.tsx @@ -34,7 +34,7 @@ const DxcDialog = ({ }, [onCloseClick]); return ( - + {createPortal( @@ -53,7 +53,7 @@ const DxcDialog = ({ onClick={() => { onCloseClick?.(); }} - aria-label={translatedLabels.dialog.closeIconAriaLabel} + aria-label={translatedLabels?.dialog?.closeIconAriaLabel} tabIndex={tabIndex} > @@ -91,7 +91,9 @@ const Overlay = styled.div` background-color: ${(props) => props.theme.overlayColor}; `; -const Dialog = styled.div<{ isCloseVisible: DialogPropsType["isCloseVisible"] }>` +const Dialog = styled.div<{ + isCloseVisible: DialogPropsType["isCloseVisible"]; +}>` position: relative; box-sizing: border-box; max-width: 80%; diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index ad36a565eb..a4bde31dd3 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -1,7 +1,7 @@ import * as Popover from "@radix-ui/react-popover"; -import { useCallback, useId, useLayoutEffect, useRef, useState } from "react"; +import { FocusEvent, KeyboardEvent, useCallback, useId, useLayoutEffect, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import { getMargin } from "../common/utils"; +import getMargin from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; import useTheme from "../useTheme"; @@ -32,8 +32,8 @@ const DxcDropdown = ({ const [visualFocusIndex, setVisualFocusIndex] = useState(0); const colorsTheme = useTheme(); - const triggerRef = useRef(null); - const menuRef = useRef(null); + const triggerRef = useRef(null); + const menuRef = useRef(null); const width = useWidth(triggerRef.current); const handleOnOpenMenu = () => { @@ -44,21 +44,25 @@ const DxcDropdown = ({ setVisualFocusIndex(0); }; const handleMenuItemOnClick = useCallback( - (value) => { - onSelectOption(value); - handleOnCloseMenu(); - triggerRef.current?.focus(); + (value?: string) => { + if (value) { + onSelectOption(value); + handleOnCloseMenu(); + triggerRef.current?.focus(); + } }, [onSelectOption] ); - const handleOnBlur = (event) => { - !event.currentTarget.contains(event.relatedTarget) && handleOnCloseMenu(); + const handleOnBlur = (event: FocusEvent) => { + if (!event.currentTarget.contains(event.relatedTarget)) { + handleOnCloseMenu(); + } }; const handleTriggerOnClick = () => { - changeIsOpen((isOpen) => !isOpen); + changeIsOpen((isCurrentlyOpen) => !isCurrentlyOpen); }; - const handleTriggerOnKeyDown = (event) => { + const handleTriggerOnKeyDown = (event: KeyboardEvent) => { switch (event.key) { case "Up": case "ArrowUp": @@ -73,23 +77,25 @@ const DxcDropdown = ({ event.preventDefault(); handleOnOpenMenu(); break; + default: + break; } }; const setPreviousIndexFocus = () => { setVisualFocusIndex((currentFocusIndex) => { - let index = currentFocusIndex === 0 ? options.length - 1 : currentFocusIndex - 1; + const index = currentFocusIndex === 0 ? options.length - 1 : currentFocusIndex - 1; return index; }); }; const setNextIndexFocus = () => { setVisualFocusIndex((currentFocusIndex) => { - let index = currentFocusIndex === options.length - 1 ? 0 : currentFocusIndex + 1; + const index = currentFocusIndex === options.length - 1 ? 0 : currentFocusIndex + 1; return index; }); }; const handleMenuOnKeyDown = useCallback( - (event: React.KeyboardEvent) => { + (event: KeyboardEvent) => { switch (event.key) { case "Up": case "ArrowUp": @@ -104,12 +110,14 @@ const DxcDropdown = ({ case " ": case "Enter": event.preventDefault(); - handleMenuItemOnClick(options[visualFocusIndex].value); + handleMenuItemOnClick(options[visualFocusIndex]?.value); break; case "Esc": case "Escape": event.preventDefault(); - isOpen && event.stopPropagation(); + if (isOpen) { + event.stopPropagation(); + } handleOnCloseMenu(); triggerRef.current?.focus(); break; @@ -127,6 +135,8 @@ const DxcDropdown = ({ handleOnCloseMenu(); triggerRef.current?.focus(); break; + default: + break; } }, [onSelectOption, visualFocusIndex, options] @@ -134,11 +144,14 @@ const DxcDropdown = ({ useLayoutEffect(() => { const visualFocusedMenuItem = menuRef?.current?.querySelectorAll("[role='menuitem']")[visualFocusIndex]; - visualFocusedMenuItem?.scrollIntoView?.({ block: "nearest", inline: "start" }); + visualFocusedMenuItem?.scrollIntoView?.({ + block: "nearest", + inline: "start", + }); }, [visualFocusIndex]); return ( - + +const calculateWidth = (margin: DropdownPropsType["margin"], size: DropdownPropsType["size"]) => size === "fillParent" ? `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})` - : sizes[size]; + : size && sizes[size]; -const DropdownContainer = styled.div<{ margin: DropdownPropsType["margin"]; size: DropdownPropsType["size"] }>` +const DropdownContainer = styled.div<{ + margin: DropdownPropsType["margin"]; + size: DropdownPropsType["size"]; +}>` width: ${(props) => calculateWidth(props.margin, props.size)}; margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; margin-top: ${(props) => @@ -295,7 +311,9 @@ const DropdownTriggerLabel = styled.span` overflow: hidden; `; -const DropdownTriggerIcon = styled.span<{ disabled: DropdownPropsType["disabled"] }>` +const DropdownTriggerIcon = styled.span<{ + disabled: DropdownPropsType["disabled"]; +}>` display: flex; color: ${(props) => (props.disabled ? props.theme.disabledColor : props.theme.buttonIconColor)}; font-size: ${(props) => props.theme.buttonIconSize}; diff --git a/packages/lib/src/file-input/FileInput.tsx b/packages/lib/src/file-input/FileInput.tsx index fe18b47770..1330b2888d 100644 --- a/packages/lib/src/file-input/FileInput.tsx +++ b/packages/lib/src/file-input/FileInput.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useId, useState, forwardRef } from "react"; +import { useCallback, useEffect, useId, useState, forwardRef, DragEvent, ChangeEvent } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcButton from "../button/Button"; import { spaces } from "../common/variables"; @@ -7,19 +7,20 @@ import useTranslatedLabels from "../useTranslatedLabels"; import FileItem from "./FileItem"; import FileInputPropsType, { FileData, RefType } from "./types"; -const getFilePreview = async (file: File): Promise => { - if (file?.type?.includes("video")) return "filled_movie"; - else if (file?.type?.includes("audio")) return "music_video"; - else if (file?.type?.includes("image")) { - return new Promise((resolve) => { - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.onload = (e) => { - resolve(e.target.result as string); - }; - }); - } else return "draft"; -}; +const getFilePreview = async (file: File): Promise => + file?.type?.includes("video") + ? "filled_movie" + : file?.type?.includes("audio") + ? "music_video" + : file?.type?.includes("image") + ? new Promise((resolve) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = (e) => { + resolve(e?.target?.result as string); + }; + }) + : "draft"; const isFileIncluded = (file: FileData, fileList: FileData[]) => { const fileListInfo = fileList.map((existingFile) => existingFile.file); @@ -60,16 +61,22 @@ const DxcFileInput = forwardRef( const colorsTheme = useTheme(); const translatedLabels = useTranslatedLabels(); - const checkFileSize = (file: File) => { - if (file.size < minSize) return translatedLabels.fileInput.fileSizeGreaterThanErrorMessage; - else if (file.size > maxSize) return translatedLabels.fileInput.fileSizeLessThanErrorMessage; - }; + const checkFileSize = (file: File) => + minSize && file.size < minSize + ? translatedLabels?.fileInput?.fileSizeGreaterThanErrorMessage + : maxSize && file.size > maxSize + ? translatedLabels?.fileInput?.fileSizeLessThanErrorMessage + : undefined; const getFilesToAdd = async (selectedFiles: File[]) => { const filesToAdd = await Promise.all(selectedFiles.map((selectedFile) => getFilePreview(selectedFile))).then( (previews: string[]) => selectedFiles.map((file, index) => { - const fileInfo = { file, error: checkFileSize(file), preview: previews[index] }; + const fileInfo = { + file, + error: checkFileSize(file), + preview: previews[index], + }; return fileInfo; }) ); @@ -78,53 +85,59 @@ const DxcFileInput = forwardRef( const addFile = async (selectedFiles: File[]) => { const filesToAdd = await getFilesToAdd( - multiple ? selectedFiles : selectedFiles.length === 1 ? selectedFiles : [selectedFiles[0]] + multiple ? selectedFiles : selectedFiles.length === 1 ? selectedFiles : [selectedFiles[0] as File] ); const finalFiles = multiple ? [...files, ...filesToAdd] : filesToAdd; callbackFile?.(finalFiles); }; - const selectFiles = (e) => { + const selectFiles = (e: ChangeEvent) => { const selectedFiles = e.target.files; - const filesArray = Object.keys(selectedFiles).map((key) => selectedFiles[key]); - addFile(filesArray); - e.target.value = null; + if (selectedFiles) { + const filesArray = Array.from(selectedFiles); + addFile(filesArray); + e.target.value = ""; + } }; const onDelete = useCallback( - (fileName) => { + (fileName: string) => { const filesCopy = [...files]; - const fileToRemove = filesCopy.find((file) => { - return file.file.name === fileName; - }); - const fileIndex = filesCopy.indexOf(fileToRemove); - filesCopy.splice(fileIndex, 1); - callbackFile?.(filesCopy); + const fileToRemove = filesCopy.find((file) => file.file.name === fileName); + if (fileToRemove) { + const fileIndex = filesCopy.indexOf(fileToRemove); + filesCopy.splice(fileIndex, 1); + callbackFile?.(filesCopy); + } }, [files, callbackFile] ); const handleClick = () => { - document.getElementById(fileInputId).click(); + document?.getElementById(fileInputId)?.click(); }; - const handleDrag = (e) => { + const handleDrag = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); }; - const handleDragIn = (e) => { - if (e.dataTransfer.items?.length > 0) setIsDragging(true); + const handleDragIn = (e: DragEvent) => { + if (e?.dataTransfer?.items?.length > 0) { + setIsDragging(true); + } }; - const handleDragOut = (e) => { + const handleDragOut = (e: DragEvent) => { // only if dragged items leave container (outside, not to childs) - if (!e.currentTarget.contains(e.relatedTarget)) setIsDragging(false); + if (!e.currentTarget?.contains(e.relatedTarget as HTMLDivElement)) { + setIsDragging(false); + } }; - const handleDrop = (e) => { + const handleDrop = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); const filesObject = e.dataTransfer.files; if (filesObject?.length > 0) { - const filesArray = Object.keys(filesObject).map((key) => filesObject[key]); + const filesArray = Array.from(filesObject); addFile(filesArray); } }; @@ -136,10 +149,9 @@ const DxcFileInput = forwardRef( value.map(async (file) => { if (file.preview) { return file; - } else { - const preview = await getFilePreview(file.file); - return { ...file, preview }; } + const preview = await getFilePreview(file.file); + return { ...file, preview }; }) )) as FileData[]; setFiles(valueFiles); @@ -149,7 +161,7 @@ const DxcFileInput = forwardRef( }, [value]); return ( - + diff --git a/packages/lib/src/image/Image.tsx b/packages/lib/src/image/Image.tsx index aedf700d5c..fbdc752962 100644 --- a/packages/lib/src/image/Image.tsx +++ b/packages/lib/src/image/Image.tsx @@ -1,4 +1,5 @@ import styled, { ThemeProvider } from "styled-components"; +import { ReactNode } from "react"; import useTheme from "../useTheme"; import BaseTypography from "../utils/BaseTypography"; import ImagePropsType, { CaptionWrapperProps } from "./types"; @@ -23,27 +24,26 @@ const DxcImage = ({ }: ImagePropsType) => { const colorsTheme = useTheme(); - return ( - - ( -
- {children} - - {caption} - -
- )} + const wrapperFunction = (children: ReactNode) => ( +
+ {children} + + {caption} + +
+ ); + + return ( + + {alt} (spacingName ? spacingName : "0rem"); +const getSpacingValue = (spacingName?: CoreSpacingTokensType) => spacingName ?? "0rem"; const StyledInset = styled.div` ${({ space, horizontal, vertical, top, right, bottom, left }) => ` diff --git a/packages/lib/src/layout/ApplicationLayout.tsx b/packages/lib/src/layout/ApplicationLayout.tsx index 2c04afc69b..c474b371b9 100644 --- a/packages/lib/src/layout/ApplicationLayout.tsx +++ b/packages/lib/src/layout/ApplicationLayout.tsx @@ -11,7 +11,10 @@ import { Tooltip } from "../tooltip/Tooltip"; import ApplicationLayoutPropsType, { AppLayoutMainPropsType } from "./types"; import { bottomLinks, findChildType, socialLinks, useResponsive, year } from "./utils"; -const ApplicationLayoutContainer = styled.div<{ isSidenavVisible: boolean; hasSidenav: boolean }>` +const ApplicationLayoutContainer = styled.div<{ + isSidenavVisible: boolean; + hasSidenav: boolean; +}>` position: absolute; top: 64px; bottom: 0; @@ -125,26 +128,26 @@ const DxcApplicationLayout = ({ const translatedLabels = useTranslatedLabels(); const handleSidenavVisibility = () => { - setIsSidenavVisibleResponsive((isSidenavVisibleResponsive) => !isSidenavVisibleResponsive); + setIsSidenavVisibleResponsive((currentIsSidenavVisibleResponsive) => !currentIsSidenavVisibleResponsive); }; useEffect(() => { - !isResponsive && setIsSidenavVisibleResponsive(false); + if (!isResponsive) { + setIsSidenavVisibleResponsive(false); + } }, [isResponsive]); return ( - + {header ?? } {sidenav && isResponsive && ( - + {visibilityToggleLabel} @@ -162,7 +165,7 @@ const DxcApplicationLayout = ({ {findChildType(children, Main)} {footer ?? ( diff --git a/packages/lib/src/nav-tabs/NavTabs.tsx b/packages/lib/src/nav-tabs/NavTabs.tsx index 2de372366f..d0aa4d8ee7 100644 --- a/packages/lib/src/nav-tabs/NavTabs.tsx +++ b/packages/lib/src/nav-tabs/NavTabs.tsx @@ -1,30 +1,38 @@ -import { Children, ReactElement, useEffect, useMemo, useRef, useState } from "react"; +import { Children, KeyboardEvent, ReactElement, ReactNode, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import useTheme from "../useTheme"; -import { NavTabsContext } from "./NavTabsContext"; -import DxcTab from "./Tab"; import NavTabsPropsType from "./types"; +import DxcTab from "./Tab"; +import NavTabsContext from "./NavTabsContext"; -const getPropInChild = (child, propName) => - child.props - ? child.props[propName] - ? child.props[propName] - : child.props.children - ? getPropInChild(child.props.children, propName) - : undefined - : undefined; +const getPropInChild = (child: ReactNode, propName: string) => { + if (child && typeof child === "object" && "props" in child) { + const childWithProps = child as ReactElement; + if (childWithProps.props[propName]) { + return childWithProps.props[propName]; + } + if (childWithProps.props.children) { + return getPropInChild(childWithProps.props.children, propName); + } + } + return undefined; +}; -const getLabelFromTab = (child) => { +const getLabelFromTab = (child: ReactNode): string | undefined => { if (typeof child === "string") { - return child.toString(); - } else if (child.props.children) { - return Array.isArray(child.props.children) - ? getLabelFromTab(child.props.children[0]) - : getLabelFromTab(child.props.children); + return child; + } + if (child && typeof child === "object" && "props" in child) { + const childWithProps = child as ReactElement; + if (Array.isArray(childWithProps.props.children)) { + return getLabelFromTab(childWithProps.props.children[0]); + } + return getLabelFromTab(childWithProps.props.children); } + return undefined; }; -const getPreviousTabIndex = (array, initialIndex): number => { +const getPreviousTabIndex = (array: ReactElement[], initialIndex: number): number => { let index = initialIndex === 0 ? array.length - 1 : initialIndex - 1; while (getPropInChild(array[index], "disabled")) { index = index === 0 ? array.length - 1 : index - 1; @@ -32,7 +40,7 @@ const getPreviousTabIndex = (array, initialIndex): number => { return index; }; -const getNextTabIndex = (array, initialIndex): number => { +const getNextTabIndex = (array: ReactElement[], initialIndex: number): number => { let index = initialIndex === array.length - 1 ? 0 : initialIndex + 1; while (getPropInChild(array[index], "disabled")) { index = index === array.length - 1 ? 0 : index + 1; @@ -41,46 +49,52 @@ const getNextTabIndex = (array, initialIndex): number => { }; const DxcNavTabs = ({ iconPosition = "top", tabIndex = 0, children }: NavTabsPropsType): JSX.Element => { - const [innerFocusIndex, setInnerFocusIndex] = useState(null); - const [underlineWidth, setUnderlineWidth] = useState(null); - const refNavTabList = useRef(null); + const [innerFocusIndex, setInnerFocusIndex] = useState(null); + const [underlineWidth, setUnderlineWidth] = useState(null); + const refNavTabList = useRef(null); const colorsTheme = useTheme(); + const childArray = Children.toArray(children).filter( + (child) => typeof child === "object" && "props" in child + ) as ReactElement[]; + useEffect(() => { - setUnderlineWidth(refNavTabList?.current?.scrollWidth); + setUnderlineWidth(refNavTabList?.current?.scrollWidth ?? null); }, [children]); const contextValue = useMemo( () => ({ iconPosition, tabIndex, - focusedLabel: innerFocusIndex === null ? undefined : getLabelFromTab(children[innerFocusIndex]), + focusedLabel: innerFocusIndex === null ? undefined : getLabelFromTab(childArray[innerFocusIndex]), }), [iconPosition, tabIndex, innerFocusIndex] ); - const handleOnKeyDown = (event: React.KeyboardEvent) => { - const activeTab = Children.toArray(children).findIndex((child: ReactElement) => getPropInChild(child, "active")); + const handleOnKeyDown = (event: KeyboardEvent) => { + const activeTab = childArray.findIndex((child) => getPropInChild(child, "active")); switch (event.key) { case "Left": case "ArrowLeft": event.preventDefault(); - setInnerFocusIndex(getPreviousTabIndex(children, innerFocusIndex === null ? activeTab : innerFocusIndex)); + setInnerFocusIndex(getPreviousTabIndex(childArray, innerFocusIndex === null ? activeTab : innerFocusIndex)); break; case "Right": case "ArrowRight": event.preventDefault(); - setInnerFocusIndex(getNextTabIndex(children, innerFocusIndex === null ? activeTab : innerFocusIndex)); + setInnerFocusIndex(getNextTabIndex(childArray, innerFocusIndex === null ? activeTab : innerFocusIndex)); + break; + default: break; } }; return ( - + {children} - + ); diff --git a/packages/lib/src/nav-tabs/NavTabsContext.tsx b/packages/lib/src/nav-tabs/NavTabsContext.tsx index cdde7246c3..16b6e2a063 100644 --- a/packages/lib/src/nav-tabs/NavTabsContext.tsx +++ b/packages/lib/src/nav-tabs/NavTabsContext.tsx @@ -1,4 +1,6 @@ import { createContext } from "react"; import { NavTabsContextProps } from "./types"; -export const NavTabsContext = createContext(null); +const NavTabsContext = createContext(null); + +export default NavTabsContext; diff --git a/packages/lib/src/nav-tabs/Tab.tsx b/packages/lib/src/nav-tabs/Tab.tsx index 10082c17f3..f9031c7f33 100644 --- a/packages/lib/src/nav-tabs/Tab.tsx +++ b/packages/lib/src/nav-tabs/Tab.tsx @@ -1,12 +1,12 @@ -import React, { forwardRef, Ref, useContext, useEffect, useRef } from "react"; +import React, { useEffect, forwardRef, Ref, useContext, useRef, useImperativeHandle, KeyboardEvent } from "react"; import styled from "styled-components"; import DxcBadge from "../badge/Badge"; import DxcFlex from "../flex/Flex"; -import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; -import BaseTypography from "../utils/BaseTypography"; -import { NavTabsContext } from "./NavTabsContext"; import NavTabsPropsType, { TabProps } from "./types"; +import BaseTypography from "../utils/BaseTypography"; +import useTheme from "../useTheme"; +import NavTabsContext from "./NavTabsContext"; +import DxcIcon from "../icon/Icon"; const DxcTab = forwardRef( ( @@ -15,19 +15,25 @@ const DxcTab = forwardRef( ): JSX.Element => { const tabRef = useRef(); const colorsTheme = useTheme(); - const { iconPosition, tabIndex, focusedLabel } = useContext(NavTabsContext); + const { iconPosition, tabIndex, focusedLabel } = useContext(NavTabsContext) ?? {}; + const innerRef = useRef(null); + useImperativeHandle(ref, () => innerRef.current!, []); useEffect(() => { - focusedLabel === children.toString() && tabRef?.current?.focus(); + if (focusedLabel === children.toString()) { + tabRef?.current?.focus(); + } }, [focusedLabel]); - const handleOnKeyDown = (event: React.KeyboardEvent) => { + const handleOnKeyDown = (event: KeyboardEvent) => { switch (event.key) { case " ": case "Enter": event.preventDefault(); tabRef?.current?.click(); break; + default: + break; } }; @@ -38,13 +44,16 @@ const DxcTab = forwardRef( disabled={disabled} active={active} iconPosition={iconPosition} - hasIcon={icon != null ? true : false} - ref={(anchorRef) => { + hasIcon={icon != null} + ref={(anchorRef: HTMLAnchorElement) => { tabRef.current = anchorRef; if (ref) { - if (typeof ref === "function") ref(anchorRef); - else (ref as React.MutableRefObject).current = anchorRef; + if (typeof ref === "function") { + ref(anchorRef); + } else { + innerRef.current = anchorRef; + } } }} onKeyDown={handleOnKeyDown} @@ -63,15 +72,15 @@ const DxcTab = forwardRef( )} diff --git a/packages/lib/src/nav-tabs/types.ts b/packages/lib/src/nav-tabs/types.ts index bc98253a5b..1ade7d7675 100644 --- a/packages/lib/src/nav-tabs/types.ts +++ b/packages/lib/src/nav-tabs/types.ts @@ -1,9 +1,11 @@ -type SVG = React.ReactNode & React.SVGProps; +import { ReactNode, SVGProps } from "react"; + +type SVG = ReactNode & SVGProps; export type NavTabsContextProps = { iconPosition: "top" | "left"; tabIndex: number; - focusedLabel: string; + focusedLabel: string | undefined; }; export type TabProps = { @@ -45,7 +47,7 @@ type Props = { /** * Contains one or more DxcNavTabs.Tab. */ - children: React.ReactNode; + children: ReactNode; /** * Value of the tabindex attribute applied to each tab. */ diff --git a/packages/lib/src/number-input/NumberInput.tsx b/packages/lib/src/number-input/NumberInput.tsx index 718e5e5b27..ba7bb9a418 100644 --- a/packages/lib/src/number-input/NumberInput.tsx +++ b/packages/lib/src/number-input/NumberInput.tsx @@ -1,8 +1,8 @@ -import { forwardRef, useEffect, useRef } from "react"; +import { forwardRef, useEffect, useMemo, useRef } from "react"; import styled from "styled-components"; import DxcTextInput from "../text-input/TextInput"; -import { NumberInputContext } from "./NumberInputContext"; import NumberInputPropsType, { RefType } from "./types"; +import NumberInputContext from "./NumberInputContext"; const DxcNumberInput = forwardRef( ( @@ -31,7 +31,17 @@ const DxcNumberInput = forwardRef( }, ref ) => { - const numberInputRef = useRef(null); + const numberInputRef = useRef(null); + + const contextValue = useMemo( + () => ({ + typeNumber: "number", + minNumber: min, + maxNumber: max, + stepNumber: step, + }), + [min, max, step] + ); useEffect(() => { const input = numberInputRef.current?.getElementsByTagName("input")[0] as HTMLInputElement; @@ -45,7 +55,7 @@ const DxcNumberInput = forwardRef( }, []); return ( - + ( ); const NumberInputContainer = styled.div<{ size: NumberInputPropsType["size"] }>` - ${(props) => props.size == "fillParent" && "width: 100%;"} + ${(props) => props.size === "fillParent" && "width: 100%;"} // Chrome, Safari, Edge, Opera input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { diff --git a/packages/lib/src/number-input/NumberInputContext.tsx b/packages/lib/src/number-input/NumberInputContext.tsx index ffa1a5dac6..913d8a12af 100644 --- a/packages/lib/src/number-input/NumberInputContext.tsx +++ b/packages/lib/src/number-input/NumberInputContext.tsx @@ -1,4 +1,6 @@ import { createContext } from "react"; import { NumberInputContextProps } from "./types"; -export const NumberInputContext = createContext(null); +const NumberInputContext = createContext(null); + +export default NumberInputContext; diff --git a/packages/lib/src/paginator/Paginator.tsx b/packages/lib/src/paginator/Paginator.tsx index 765bebcb1e..0b4ddb34a8 100644 --- a/packages/lib/src/paginator/Paginator.tsx +++ b/packages/lib/src/paginator/Paginator.tsx @@ -15,7 +15,7 @@ const DxcPaginator = ({ itemsPerPageFunction, tabIndex = 0, }: PaginatorPropsType): JSX.Element => { - const totalPages = itemsPerPage > 0 && Math.ceil(totalItems / itemsPerPage); + const totalPages = itemsPerPage > 0 ? Math.ceil(totalItems / itemsPerPage) : 0; const currentPageInternal = currentPage === -1 ? totalPages : currentPage; const minItemsPerPage = currentPageInternal === 1 || currentPageInternal === 0 @@ -28,17 +28,20 @@ const DxcPaginator = ({ const translatedLabels = useTranslatedLabels(); return ( - + {itemsPerPageOptions && ( - {translatedLabels.paginator.itemsPerPageText} + {translatedLabels?.paginator?.itemsPerPageText} ({ label: num.toString(), value: num.toString() }))} + options={itemsPerPageOptions.map((num) => ({ + label: num.toString(), + value: num.toString(), + }))} onChange={(newValue) => { - itemsPerPageFunction(Number(newValue.value)); + itemsPerPageFunction?.(Number(newValue.value)); }} value={itemsPerPage.toString()} size="fillParent" @@ -48,7 +51,7 @@ const DxcPaginator = ({ )} - {translatedLabels.paginator.minToMaxOfText(minItemsPerPage, maxItemsPerPage, totalItems)} + {translatedLabels?.paginator?.minToMaxOfText?.(minItemsPerPage, maxItemsPerPage, totalItems)} {onPageChange && ( - {translatedLabels.paginator.goToPageText} + {translatedLabels?.paginator?.goToPageText} ({ @@ -84,7 +87,7 @@ const DxcPaginator = ({ value: (num + 1).toString(), }))} onChange={(newValue) => { - onPageChange(Number(newValue.value)); + onPageChange?.(Number(newValue.value)); }} value={currentPage.toString()} size="fillParent" @@ -93,7 +96,7 @@ const DxcPaginator = ({ ) : ( - {translatedLabels.paginator.pageOfText(currentPageInternal, totalPages)} + {translatedLabels?.paginator?.pageOfText?.(currentPageInternal, totalPages)} )} {onPageChange && ( { +const DxcParagraph = ({ children }: { children: ReactNode }): JSX.Element => { const colorsTheme = useTheme(); return ( {children} diff --git a/packages/lib/src/password-input/PasswordInput.tsx b/packages/lib/src/password-input/PasswordInput.tsx index 7b21435ea2..f9f79dfe38 100644 --- a/packages/lib/src/password-input/PasswordInput.tsx +++ b/packages/lib/src/password-input/PasswordInput.tsx @@ -5,7 +5,7 @@ import useTranslatedLabels from "../useTranslatedLabels"; import PasswordInputPropsType, { RefType } from "./types"; const setInputType = (type: string, element: HTMLDivElement | null) => { - element?.getElementsByTagName("input")[0].setAttribute("type", type); + element?.getElementsByTagName("input")[0]?.setAttribute("type", type); }; const setAriaAttributes = (ariaExpanded: "true" | "false", ariaLabel: string, element: HTMLDivElement | null) => { @@ -36,17 +36,21 @@ const DxcPasswordInput = forwardRef( ref ) => { const [isPasswordVisible, setIsPasswordVisible] = useState(false); - const inputRef = useRef(null); + const inputRef = useRef(null); const { passwordInput } = useTranslatedLabels(); useEffect(() => { (() => { if (isPasswordVisible) { setInputType("text", inputRef.current); - setAriaAttributes("true", passwordInput.inputHidePasswordTitle, inputRef.current); + if (passwordInput?.inputHidePasswordTitle) { + setAriaAttributes("true", passwordInput.inputHidePasswordTitle, inputRef.current); + } } else { setInputType("password", inputRef.current); - setAriaAttributes("false", passwordInput.inputShowPasswordTitle, inputRef.current); + if (passwordInput?.inputShowPasswordTitle) { + setAriaAttributes("false", passwordInput.inputShowPasswordTitle, inputRef.current); + } } })(); }, [isPasswordVisible, passwordInput]); @@ -60,10 +64,10 @@ const DxcPasswordInput = forwardRef( helperText={helperText} action={{ onClick: () => { - setIsPasswordVisible((isPasswordVisible) => !isPasswordVisible); + setIsPasswordVisible((isPasswordCurrentlyVisible) => !isPasswordCurrentlyVisible); }, icon: isPasswordVisible ? "Visibility_Off" : "Visibility", - title: isPasswordVisible ? passwordInput.inputHidePasswordTitle : passwordInput.inputShowPasswordTitle, + title: isPasswordVisible ? passwordInput?.inputHidePasswordTitle : passwordInput?.inputShowPasswordTitle, }} error={error} clearable={clearable} @@ -84,7 +88,7 @@ const DxcPasswordInput = forwardRef( ); const PasswordInput = styled.div<{ size: PasswordInputPropsType["size"] }>` - ${(props) => props.size == "fillParent" && "width: 100%;"} + ${(props) => props.size === "fillParent" && "width: 100%;"} & ::-ms-reveal { display: none; } diff --git a/packages/lib/src/progress-bar/ProgressBar.tsx b/packages/lib/src/progress-bar/ProgressBar.tsx index 7a18276e95..3210df1f5e 100644 --- a/packages/lib/src/progress-bar/ProgressBar.tsx +++ b/packages/lib/src/progress-bar/ProgressBar.tsx @@ -22,7 +22,7 @@ const DxcProgressBar = ({ }, [value]); return ( - + @@ -52,7 +52,9 @@ const DxcProgressBar = ({ ); }; -const BackgroundProgressBar = styled.div<{ overlay: ProgressBarPropsType["overlay"] }>` +const BackgroundProgressBar = styled.div<{ + overlay: ProgressBarPropsType["overlay"]; +}>` ${({ overlay, theme }) => overlay ? `background-color: ${theme.overlayColor}; @@ -102,7 +104,9 @@ const InfoProgressBar = styled.div` justify-content: space-between; `; -const ProgressBarLabel = styled.div<{ overlay: ProgressBarPropsType["overlay"] }>` +const ProgressBarLabel = styled.div<{ + overlay: ProgressBarPropsType["overlay"]; +}>` font-family: ${(props) => props.theme.labelFontFamily}; font-style: ${(props) => props.theme.labelFontStyle}; font-size: ${(props) => props.theme.labelFontSize}; @@ -140,7 +144,9 @@ const HelperText = styled.span<{ overlay: ProgressBarPropsType["overlay"] }>` line-height: 1.5em; `; -const LinearProgress = styled.div<{ helperText: ProgressBarPropsType["helperText"] }>` +const LinearProgress = styled.div<{ + helperText: ProgressBarPropsType["helperText"]; +}>` height: ${(props) => props.theme.thickness}; background-color: ${(props) => props.theme.totalLineColor}; border-radius: ${(props) => props.theme.borderRadius}; @@ -155,7 +161,7 @@ const LinearProgressBar = styled.span<{ container: string; }>` background-color: ${(props) => props.theme.trackLineColor}; - transform: ${(props) => `translateX(-${props.variant === "determinate" ? 100 - props.value : 0}%)`}; + transform: ${(props) => `translateX(-${props.variant === "determinate" ? 100 - (props.value ?? 0) : 0}%)`}; top: 0; left: 0; width: 100%; diff --git a/packages/lib/src/quick-nav/QuickNav.tsx b/packages/lib/src/quick-nav/QuickNav.tsx index 6399db67ca..d3cc3cdabb 100644 --- a/packages/lib/src/quick-nav/QuickNav.tsx +++ b/packages/lib/src/quick-nav/QuickNav.tsx @@ -13,10 +13,10 @@ const DxcQuickNav = ({ title, links }: QuickNavTypes): JSX.Element => { const colorsTheme = useTheme(); return ( - + - + {links.map((link) => (
  • diff --git a/packages/lib/src/radio-group/RadioGroup.tsx b/packages/lib/src/radio-group/RadioGroup.tsx index 9337f4a6d1..b85d9280f1 100644 --- a/packages/lib/src/radio-group/RadioGroup.tsx +++ b/packages/lib/src/radio-group/RadioGroup.tsx @@ -1,4 +1,4 @@ -import { forwardRef, useCallback, useId, useMemo, useState } from "react"; +import { FocusEvent, forwardRef, KeyboardEvent, useCallback, useId, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; @@ -47,7 +47,7 @@ const DxcRadioGroup = forwardRef( ? [ ...options, { - label: optionalItemLabel ?? translatedLabels.radioGroup.optionalItemLabelDefault, + label: optionalItemLabel ?? translatedLabels?.radioGroup?.optionalItemLabelDefault ?? "", value: "", disabled, }, @@ -62,47 +62,62 @@ const DxcRadioGroup = forwardRef( (newValue: string) => { const currentValue = value ?? innerValue; if (newValue !== currentValue && !readOnly) { - value ?? setInnerValue(newValue); + if (value == null) { + setInnerValue(newValue); + } onChange?.(newValue); } }, [value, innerValue, onChange] ); - const handleOnBlur = (event: React.FocusEvent) => { + 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)) { setFirstTimeFocus(true); const currentValue = value ?? innerValue; - !optional && !currentValue - ? onBlur?.({ value: currentValue, error: translatedLabels.formFields.requiredSelectionErrorMessage }) - : onBlur?.({ value: currentValue }); + if (!optional && !currentValue) { + onBlur?.({ + value: currentValue, + error: translatedLabels?.formFields?.requiredSelectionErrorMessage, + }); + } else { + onBlur?.({ value: currentValue }); + } } }; const handleOnFocus = () => { - firstTimeFocus && setFirstTimeFocus(false); + if (firstTimeFocus) { + setFirstTimeFocus(false); + } }; const setPreviousRadioChecked = () => { - setCurrentFocusIndex((currentFocusIndex) => { - let index = currentFocusIndex === 0 ? innerOptions.length - 1 : currentFocusIndex - 1; - while (innerOptions[index].disabled) { + setCurrentFocusIndex((currentFocusIndexValue) => { + let index = currentFocusIndexValue === 0 ? innerOptions.length - 1 : currentFocusIndexValue - 1; + while (innerOptions[index]?.disabled) { index = index === 0 ? innerOptions.length - 1 : index - 1; } - handleOnChange(innerOptions[index].value); + const option = innerOptions[index]; + if (option != null) { + handleOnChange(option.value); + } return index; }); }; const setNextRadioChecked = () => { - setCurrentFocusIndex((currentFocusIndex) => { - let index = currentFocusIndex === innerOptions.length - 1 ? 0 : currentFocusIndex + 1; - while (innerOptions[index].disabled) { + setCurrentFocusIndex((currentFocusIndexValue) => { + let index = currentFocusIndexValue === innerOptions.length - 1 ? 0 : currentFocusIndexValue + 1; + while (innerOptions[index]?.disabled) { index = index === innerOptions.length - 1 ? 0 : index + 1; } - handleOnChange(innerOptions[index].value); + const option = innerOptions[index]; + if (option != null) { + handleOnChange(option.value); + } return index; }); }; - const handleOnKeyDown = (event: React.KeyboardEvent) => { + const handleOnKeyDown = (event: KeyboardEvent) => { switch (event.key) { case "Left": case "ArrowLeft": @@ -120,18 +135,22 @@ const DxcRadioGroup = forwardRef( break; case " ": event.preventDefault(); - handleOnChange(innerOptions[currentFocusIndex].value); + if (innerOptions[currentFocusIndex] != null) { + handleOnChange(innerOptions[currentFocusIndex].value); + } + break; + default: break; } }; return ( - + {label && ( )} {helperText && {helperText}} @@ -143,7 +162,7 @@ const DxcRadioGroup = forwardRef( role="radiogroup" aria-disabled={disabled} aria-labelledby={radioGroupLabelId} - aria-invalid={error ? true : false} + aria-invalid={!!error} aria-errormessage={error ? errorId : undefined} aria-required={!disabled && !readOnly && !optional} aria-readonly={readOnly} @@ -153,7 +172,7 @@ const DxcRadioGroup = forwardRef( {innerOptions.map((option, index) => ( { handleOnChange(option.value); @@ -184,7 +203,10 @@ const RadioGroupContainer = styled.div` flex-direction: column; `; -const Label = styled.span<{ helperText: RadioGroupPropsType["helperText"]; disabled: RadioGroupPropsType["disabled"] }>` +const Label = styled.span<{ + helperText: RadioGroupPropsType["helperText"]; + disabled: RadioGroupPropsType["disabled"]; +}>` color: ${(props) => (props.disabled ? props.theme.disabledLabelFontColor : props.theme.labelFontColor)}; font-family: ${(props) => props.theme.fontFamily}; font-size: ${(props) => props.theme.labelFontSize}; diff --git a/packages/lib/src/resultset-table/ResultsetTable.tsx b/packages/lib/src/resultset-table/ResultsetTable.tsx index ddc7e2fdfa..c4a53dd459 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.tsx @@ -1,7 +1,7 @@ -import { useEffect, useMemo, useRef, useState } from "react"; +import { ReactNode, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import CoreTokens from "../common/coreTokens"; -import { getMargin } from "../common/utils"; +import getMargin from "../common/utils"; import { spaces } from "../common/variables"; import DxcPaginator from "../paginator/Paginator"; import DxcTable, { DxcActionsCell } from "../table/Table"; @@ -9,26 +9,26 @@ import useTheme from "../useTheme"; import icons from "./Icons"; import ResultsetTablePropsType, { Column, Row } from "./types"; -const normalizeSortValue = (sortValue: string | Date | React.ReactNode) => +const normalizeSortValue = (sortValue: string | Date | ReactNode) => typeof sortValue === "string" ? sortValue.toUpperCase() : sortValue; -const isDateType = (value: React.ReactNode | Date): boolean => { - return value instanceof Date; -}; +const isDateType = (value: ReactNode | Date): boolean => value instanceof Date; const sortArray = (index: number, order: "ascending" | "descending", resultset: { id: string; cells: Row }[]) => resultset.slice().sort((element1, element2) => { const sortValueA = normalizeSortValue(element1?.cells[index]?.sortValue || element1?.cells[index]?.displayValue); const sortValueB = normalizeSortValue(element2?.cells[index]?.sortValue || element2?.cells[index]?.displayValue); let comparison = 0; - if (typeof sortValueA === "object" && !isDateType(sortValueA)) { - comparison = -1; - } else if (typeof sortValueB === "object" && !isDateType(sortValueB)) { - comparison = 1; - } else if (sortValueA > sortValueB) { - comparison = 1; - } else if (sortValueA < sortValueB) { - comparison = -1; + if (sortValueA != null && sortValueB != null) { + if (typeof sortValueA === "object" && !isDateType(sortValueA)) { + comparison = -1; + } else if (typeof sortValueB === "object" && !isDateType(sortValueB)) { + comparison = 1; + } else if (sortValueA > sortValueB) { + comparison = 1; + } else if (sortValueA < sortValueB) { + comparison = -1; + } } return order === "descending" ? comparison * -1 : comparison; }); @@ -105,15 +105,13 @@ const DxcResultsetTable = ({ if (!hidePaginator) { if (rows.length === 0) { changePage(0); - } else { - if (page === 0) { - changePage(1); - } else if (rows.length < prevRowCountRef.current) { - const lastPage = Math.ceil(rows.length / itemsPerPage); - const prevLastPage = Math.ceil(prevRowCountRef.current / itemsPerPage); - if (lastPage < prevLastPage) { - changePage(Math.min(lastPage, page)); - } + } else if (page === 0) { + changePage(1); + } else if (rows.length < prevRowCountRef.current) { + const lastPage = Math.ceil(rows.length / itemsPerPage); + const prevLastPage = Math.ceil(prevRowCountRef.current / itemsPerPage); + if (lastPage < prevLastPage) { + changePage(Math.min(lastPage, page)); } } prevRowCountRef.current = rows.length; @@ -121,7 +119,7 @@ const DxcResultsetTable = ({ }, [rows.length]); return ( - + @@ -133,9 +131,10 @@ const DxcResultsetTable = ({ > { - column.isSortable && changeSorting(index); + if (column.isSortable) { + changeSorting(index); + } }} tabIndex={column.isSortable ? tabIndex : -1} isSortable={column.isSortable} @@ -184,10 +183,12 @@ const DxcResultsetTable = ({ ); }; -const calculateWidth = (margin: string | object) => +const calculateWidth = (margin: ResultsetTablePropsType["margin"]) => `calc(100% - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})`; -const DxcResultsetTableContainer = styled.div<{ margin: ResultsetTablePropsType["margin"] }>` +const DxcResultsetTableContainer = styled.div<{ + margin: ResultsetTablePropsType["margin"]; +}>` width: ${(props) => calculateWidth(props.margin)}; margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; margin-top: ${(props) => @@ -200,7 +201,10 @@ const DxcResultsetTableContainer = styled.div<{ margin: ResultsetTablePropsType[ props.margin && typeof props.margin === "object" && props.margin.left ? spaces[props.margin.left] : ""}; `; -const HeaderContainer = styled.span<{ isSortable: Column["isSortable"]; mode: ResultsetTablePropsType["mode"] }>` +const HeaderContainer = styled.span<{ + isSortable: Column["isSortable"]; + mode: ResultsetTablePropsType["mode"]; +}>` display: flex; align-items: center; justify-content: ${(props) => diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index a211d9d2e3..73e3832446 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -1,8 +1,8 @@ import * as Popover from "@radix-ui/react-popover"; -import { forwardRef, useCallback, useId, useMemo, useRef, useState } from "react"; +import { ChangeEvent, FocusEvent, forwardRef, KeyboardEvent, MouseEvent, useCallback, useId, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; +import getMargin from "../common/utils"; import DxcIcon from "../icon/Icon"; import { Tooltip, TooltipWrapper } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; @@ -72,7 +72,9 @@ const DxcSelect = forwardRef( ); const openListbox = () => { - if (!isOpen && canOpenOptions(options, disabled)) changeIsOpen(true); + if (!isOpen && canOpenOptions(options, disabled)) { + changeIsOpen(true); + } }; const closeListbox = () => { if (isOpen) { @@ -81,83 +83,104 @@ const DxcSelect = forwardRef( } }; - const handleSelectChangeValue = (newOption: ListOptionType) => { - let newValue: string | string[]; - - if (multiple) { - if ((value ?? innerValue).includes(newOption.value)) - newValue = ( - (value && Array.isArray(value) && value) ?? - (innerValue && Array.isArray(innerValue) && innerValue) - ).filter((optionVal) => optionVal !== newOption.value); - else - newValue = [ - ...((value && Array.isArray(value) && value) ?? (innerValue && Array.isArray(innerValue) && innerValue)), - newOption.value, - ]; - } else newValue = newOption.value; - - value ?? setInnerValue(newValue); - notOptionalCheck(newValue, multiple, optional) - ? onChange?.({ - value: newValue as string & string[], - error: translatedLabels.formFields.requiredValueErrorMessage, - }) - : onChange?.({ value: newValue as string & string[] }); + const handleSelectChangeValue = (newOption: ListOptionType | undefined) => { + if (newOption) { + const currentValue = value ?? innerValue; + const newValue = multiple + ? (currentValue as string[]).includes(newOption.value) + ? (currentValue as string[]).filter((optionVal: string) => optionVal !== newOption.value) + : [...(currentValue as string[]), newOption.value] + : newOption.value; + + if (value == null) { + setInnerValue(newValue); + } + + onChange?.({ + value: newValue as string & string[], + ...(notOptionalCheck(newValue, multiple, optional) && { + error: translatedLabels?.formFields?.requiredValueErrorMessage, + }), + }); + } }; const handleSelectOnClick = () => { - searchable && selectSearchInputRef.current.focus(); + if (searchable) { + selectSearchInputRef?.current?.focus(); + } if (isOpen) { closeListbox(); setSearchValue(""); - } else openListbox(); + } else { + openListbox(); + } }; - const handleSelectOnFocus = (event: React.FocusEvent) => { - if (!event.currentTarget.contains(event.relatedTarget)) searchable && selectSearchInputRef.current.focus(); + const handleSelectOnFocus = (event: FocusEvent) => { + if (!event.currentTarget.contains(event.relatedTarget) && searchable) { + selectSearchInputRef?.current?.focus(); + } }; - const handleSelectOnBlur = (event: React.FocusEvent) => { + const handleSelectOnBlur = (event: FocusEvent) => { if (!event.currentTarget.contains(event.relatedTarget)) { closeListbox(); setSearchValue(""); const currentValue = value ?? innerValue; - notOptionalCheck(currentValue, multiple, optional) - ? onBlur?.({ - value: currentValue as string & string[], - error: translatedLabels.formFields.requiredValueErrorMessage, - }) - : onBlur?.({ value: currentValue as string & string[] }); + if (notOptionalCheck(currentValue, multiple, optional)) { + onBlur?.({ + value: currentValue as string & string[], + error: translatedLabels?.formFields?.requiredValueErrorMessage, + }); + } else { + onBlur?.({ value: currentValue as string & string[] }); + } } }; - const handleSelectOnKeyDown = (event: React.KeyboardEvent) => { + const handleSelectOnKeyDown = (event: KeyboardEvent) => { switch (event.key) { case "Down": case "ArrowDown": event.preventDefault(); - singleSelectionIndex !== undefined && - (!isOpen || (visualFocusIndex === -1 && singleSelectionIndex > -1 && singleSelectionIndex <= lastOptionIndex)) - ? changeVisualFocusIndex(singleSelectionIndex) - : changeVisualFocusIndex((visualFocusIndex) => { - if (visualFocusIndex < lastOptionIndex) return visualFocusIndex + 1; - else if (visualFocusIndex === lastOptionIndex) return 0; - }); + if ( + singleSelectionIndex != null && + (!isOpen || + (visualFocusIndex === -1 && singleSelectionIndex > -1 && singleSelectionIndex <= lastOptionIndex)) + ) { + changeVisualFocusIndex(singleSelectionIndex); + } else { + changeVisualFocusIndex((currentVisualFocusIndex) => { + if (currentVisualFocusIndex < lastOptionIndex) { + return currentVisualFocusIndex + 1; + } + return 0; + }); + } openListbox(); break; case "Up": case "ArrowUp": event.preventDefault(); - singleSelectionIndex !== undefined && - (!isOpen || (visualFocusIndex === -1 && singleSelectionIndex > -1 && singleSelectionIndex <= lastOptionIndex)) - ? changeVisualFocusIndex(singleSelectionIndex) - : changeVisualFocusIndex((visualFocusIndex) => - visualFocusIndex === 0 || visualFocusIndex === -1 ? lastOptionIndex : visualFocusIndex - 1 - ); + if ( + singleSelectionIndex != null && + (!isOpen || + (visualFocusIndex === -1 && singleSelectionIndex > -1 && singleSelectionIndex <= lastOptionIndex)) + ) { + changeVisualFocusIndex(singleSelectionIndex); + } else { + changeVisualFocusIndex((currentVisualFocusIndex) => + currentVisualFocusIndex === 0 || currentVisualFocusIndex === -1 + ? lastOptionIndex + : currentVisualFocusIndex - 1 + ); + } openListbox(); break; case "Esc": case "Escape": event.preventDefault(); - isOpen && event.stopPropagation(); + if (isOpen) { + event.stopPropagation(); + } closeListbox(); setSearchValue(""); break; @@ -166,55 +189,70 @@ const DxcSelect = forwardRef( let accLength = optional && !multiple ? 1 : 0; if (searchable) { if (filteredOptions.length > 0) { - if (optional && !multiple && visualFocusIndex === 0 && groupsHaveOptions(filteredOptions)) + if (optional && !multiple && visualFocusIndex === 0 && groupsHaveOptions(filteredOptions)) { handleSelectChangeValue(optionalItem); - else - isArrayOfOptionGroups(filteredOptions) - ? groupsHaveOptions(filteredOptions) && - filteredOptions.some((groupOption) => { - const groupLength = accLength + groupOption.options.length; - groupLength > visualFocusIndex && - handleSelectChangeValue(groupOption.options[visualFocusIndex - accLength]); - accLength = groupLength; - return groupLength > visualFocusIndex; - }) - : handleSelectChangeValue(filteredOptions[visualFocusIndex - accLength]); - } - } else { - if (optional && !multiple && visualFocusIndex === 0) handleSelectChangeValue(optionalItem); - else - isArrayOfOptionGroups(options) - ? options.some((groupOption) => { + } else if (isArrayOfOptionGroups(filteredOptions)) { + if (groupsHaveOptions(filteredOptions)) { + filteredOptions.some((groupOption) => { const groupLength = accLength + groupOption.options.length; - groupLength > visualFocusIndex && + if (groupLength > visualFocusIndex) { handleSelectChangeValue(groupOption.options[visualFocusIndex - accLength]); + } accLength = groupLength; return groupLength > visualFocusIndex; - }) - : handleSelectChangeValue(options[visualFocusIndex - accLength]); + }); + } + } else { + handleSelectChangeValue(filteredOptions[visualFocusIndex - accLength]); + } + } + } else if (optional && !multiple && visualFocusIndex === 0) { + handleSelectChangeValue(optionalItem); + } else if (isArrayOfOptionGroups(options)) { + options.some((groupOption) => { + const groupLength = accLength + groupOption.options.length; + if (groupLength > visualFocusIndex) { + handleSelectChangeValue(groupOption.options[visualFocusIndex - accLength]); + } + accLength = groupLength; + return groupLength > visualFocusIndex; + }); + } else { + handleSelectChangeValue(options[visualFocusIndex - accLength]); + } + if (!multiple) { + closeListbox(); } - !multiple && closeListbox(); setSearchValue(""); } break; + default: + break; } }; - const handleSearchIOnChange = (event: React.ChangeEvent) => { + const handleSearchIOnChange = (event: ChangeEvent) => { setSearchValue(event.target.value); changeVisualFocusIndex(-1); openListbox(); }; - const handleClearOptionsActionOnClick = (event: React.MouseEvent) => { + const handleClearOptionsActionOnClick = (event: MouseEvent) => { event.stopPropagation(); - value ?? setInnerValue([]); - !optional - ? onChange?.({ value: [] as string & string[], error: translatedLabels.formFields.requiredValueErrorMessage }) - : onChange?.({ value: [] as string & string[] }); + if (value == null) { + setInnerValue([]); + } + if (!optional) { + onChange?.({ + value: [] as string[] as string & string[], + error: translatedLabels?.formFields?.requiredValueErrorMessage, + }); + } else { + onChange?.({ value: [] as string[] as string & string[] }); + } }; - const handleClearSearchActionOnClick = (event: React.MouseEvent) => { + const handleClearSearchActionOnClick = (event: MouseEvent) => { event.stopPropagation(); setSearchValue(""); }; @@ -222,30 +260,32 @@ const DxcSelect = forwardRef( const handleOptionOnClick = useCallback( (option: ListOptionType) => { handleSelectChangeValue(option); - !multiple && closeListbox(); + if (!multiple) { + closeListbox(); + } setSearchValue(""); }, [handleSelectChangeValue, closeListbox, multiple] ); - const handleOnMouseEnter = (event: React.MouseEvent) => { + const handleOnMouseEnter = (event: MouseEvent) => { const text = event.currentTarget; setHasTooltip(text.scrollWidth > text.clientWidth); }; return ( - + {label && ( )} {helperText && {helperText}} @@ -268,14 +308,14 @@ const DxcSelect = forwardRef( aria-haspopup="listbox" aria-labelledby={label ? selectLabelId : undefined} aria-activedescendant={visualFocusIndex >= 0 ? `option-${visualFocusIndex}` : undefined} - aria-invalid={error ? true : false} + aria-invalid={!!error} aria-errormessage={error ? errorId : undefined} aria-required={!disabled && !optional} > {multiple && Array.isArray(selectedOption) && selectedOption.length > 0 && ( {selectedOption.length} - + { @@ -284,7 +324,7 @@ const DxcSelect = forwardRef( }} onClick={handleClearOptionsActionOnClick} tabIndex={-1} - aria-label={translatedLabels.select.actionClearSelectionTitle} + aria-label={translatedLabels?.select?.actionClearSelectionTitle} > @@ -299,10 +339,7 @@ const DxcSelect = forwardRef( disabled={disabled} value={ multiple - ? ( - (value && Array.isArray(value) && value) ?? - (innerValue && Array.isArray(innerValue) && innerValue) - ).join(",") + ? (Array.isArray(value) ? value : Array.isArray(innerValue) ? innerValue : []).join(",") : (value ?? innerValue) } readOnly @@ -341,7 +378,7 @@ const DxcSelect = forwardRef( )} {searchable && searchValue.length > 0 && ( - + { // Avoid input to lose focus @@ -349,7 +386,7 @@ const DxcSelect = forwardRef( }} onClick={handleClearSearchActionOnClick} tabIndex={-1} - aria-label={translatedLabels.select.actionClearSearchTitle} + aria-label={translatedLabels?.select?.actionClearSearchTitle} > @@ -410,14 +447,17 @@ const sizes = { const calculateWidth = (margin: SelectPropsType["margin"], size: SelectPropsType["size"]) => size === "fillParent" ? `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})` - : sizes[size]; + : size && sizes[size]; -const SelectContainer = styled.div<{ margin: SelectPropsType["margin"]; size: SelectPropsType["size"] }>` +const SelectContainer = styled.div<{ + margin: SelectPropsType["margin"]; + size: SelectPropsType["size"]; +}>` box-sizing: border-box; display: flex; flex-direction: column; width: ${(props) => calculateWidth(props.margin, props.size)}; - ${(props) => props.size !== "fillParent" && "min-width:" + calculateWidth(props.margin, props.size)}; + ${(props) => props.size !== "fillParent" && `min-width:${calculateWidth(props.margin, props.size)}`}; margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")}; margin-top: ${(props) => props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""}; @@ -430,7 +470,10 @@ const SelectContainer = styled.div<{ margin: SelectPropsType["margin"]; size: Se font-family: ${(props) => props.theme.fontFamily}; `; -const Label = styled.label<{ disabled: SelectPropsType["disabled"]; helperText: SelectPropsType["helperText"] }>` +const Label = styled.label<{ + disabled: SelectPropsType["disabled"]; + helperText: SelectPropsType["helperText"]; +}>` color: ${(props) => (props.disabled ? props.theme.disabledColor : props.theme.labelFontColor)}; font-size: ${(props) => props.theme.labelFontSize}; font-style: ${(props) => props.theme.labelFontStyle}; @@ -453,7 +496,10 @@ const HelperText = styled.span<{ disabled: SelectPropsType["disabled"] }>` margin-bottom: 0.25rem; `; -const Select = styled.div<{ disabled: SelectPropsType["disabled"]; error: SelectPropsType["error"] }>` +const Select = styled.div<{ + disabled: SelectPropsType["disabled"]; + error: SelectPropsType["error"]; +}>` display: flex; position: relative; align-items: center; @@ -545,7 +591,10 @@ const SearchableValueContainer = styled.div` width: 100%; `; -const SelectedOption = styled.span<{ disabled: SelectPropsType["disabled"]; atBackground: boolean }>` +const SelectedOption = styled.span<{ + disabled: SelectPropsType["disabled"]; + atBackground: boolean; +}>` grid-area: 1 / 1 / 1 / 1; display: inline-flex; align-items: center; @@ -554,11 +603,14 @@ const SelectedOption = styled.span<{ disabled: SelectPropsType["disabled"]; atBa user-select: none; overflow: hidden; - color: ${(props) => { - if (props.disabled) return props.theme.disabledColor; - else if (props.atBackground) return props.theme.placeholderFontColor; - else return props.theme.valueFontColor; - }}; + color: ${(props) => + props.disabled + ? props.theme.disabledColor + : props.atBackground + ? props.theme.placeholderFontColor + : props.theme.valueFontColor}; + + font-family: ${(props) => props.theme.fontFamily}; font-size: ${(props) => props.theme.valueFontSize}; font-style: ${(props) => props.theme.valueFontStyle}; font-weight: ${(props) => props.theme.valueFontWeight}; diff --git a/packages/lib/src/select/selectUtils.ts b/packages/lib/src/select/selectUtils.ts index d0840beb2f..bf7358a461 100644 --- a/packages/lib/src/select/selectUtils.ts +++ b/packages/lib/src/select/selectUtils.ts @@ -16,7 +16,7 @@ const isOptionGroup = (option: ListOptionType | ListOptionGroupType): option is * Checks if the options are an array of groups. */ const isArrayOfOptionGroups = (options: ListOptionType[] | ListOptionGroupType[]): options is ListOptionGroupType[] => - isOptionGroup(options[0]); + options[0] != null && isOptionGroup(options[0]); /** * Checks if the groups have options. @@ -38,7 +38,7 @@ const filterOptionsBySearchValue = ( searchValue: string ): ListOptionType[] | ListOptionGroupType[] => { if (options?.length > 0) { - if (isArrayOfOptionGroups(options)) + if (isArrayOfOptionGroups(options)) { return options.map((optionGroup) => { const group = { label: optionGroup.label, @@ -48,8 +48,10 @@ const filterOptionsBySearchValue = ( }; return group; }); - else return options.filter((option) => option.label.toUpperCase().includes(searchValue.toUpperCase())); + } + return options.filter((option) => option.label.toUpperCase().includes(searchValue.toUpperCase())); } + return []; }; /** @@ -63,14 +65,21 @@ const getLastOptionIndex = ( multiple: boolean ) => { let last = 0; - const reducer = (acc: number, current: ListOptionGroupType) => acc + current.options?.length; + const reducer = (acc: number, current: ListOptionGroupType) => acc + (current?.options?.length || 0); - if (searchable && filteredOptions?.length > 0) - isArrayOfOptionGroups(filteredOptions) - ? (last = filteredOptions.reduce(reducer, 0) - 1) - : (last = filteredOptions.length - 1); - else if (options?.length > 0) - isArrayOfOptionGroups(options) ? (last = options.reduce(reducer, 0) - 1) : (last = options.length - 1); + if (searchable && filteredOptions?.length > 0) { + if (isArrayOfOptionGroups(filteredOptions)) { + last = filteredOptions.reduce(reducer, 0) - 1; + } else { + last = filteredOptions.length - 1; + } + } else if (options?.length > 0) { + if (isArrayOfOptionGroups(options)) { + last = options.reduce(reducer, 0) - 1; + } else { + last = options.length - 1; + } + } return optional && !multiple ? last + 1 : last; }; @@ -86,41 +95,45 @@ const getSelectedOption = ( optionalItem: ListOptionType ) => { let selectedOption: ListOptionType | ListOptionType[] = multiple ? [] : ({} as ListOptionType); - let singleSelectionIndex: number; + let singleSelectionIndex: number | null = null; if (multiple) { if (options?.length > 0) { options.forEach((option: ListOptionType | ListOptionGroupType) => { - if (isOptionGroup(option)) - option.options.forEach((singleOption) => { - if (value.includes(singleOption.value) && Array.isArray(selectedOption)) selectedOption.push(singleOption); - }); - else if (value.includes(option.value) && Array.isArray(selectedOption)) selectedOption.push(option); - }); - } - } else { - if (optional && value === "") { - selectedOption = optionalItem; - singleSelectionIndex = 0; - } else if (options?.length > 0) { - let group_index = 0; - options.some((option: ListOptionType | ListOptionGroupType, index: number) => { if (isOptionGroup(option)) { - option.options.some((singleOption) => { - if (singleOption.value === value) { - selectedOption = singleOption; - singleSelectionIndex = optional ? group_index + 1 : group_index; - return true; + option.options.forEach((singleOption) => { + if (value.includes(singleOption.value) && Array.isArray(selectedOption)) { + selectedOption.push(singleOption); } - group_index++; }); - } else if (option.value === value) { - selectedOption = option; - singleSelectionIndex = optional ? index + 1 : index; - return true; + } else if (value.includes(option.value) && Array.isArray(selectedOption)) { + selectedOption.push(option); } }); } + } else if (optional && value === "") { + selectedOption = optionalItem; + singleSelectionIndex = 0; + } else if (options?.length > 0) { + let groupIndex = 0; + options.some((option: ListOptionType | ListOptionGroupType, index: number) => { + if (isOptionGroup(option)) { + option.options.some((singleOption) => { + if (singleOption.value === value) { + selectedOption = singleOption; + singleSelectionIndex = optional ? groupIndex + 1 : groupIndex; + return true; + } + groupIndex += 1; + return false; + }); + } else if (option.value === value) { + selectedOption = option; + singleSelectionIndex = optional ? index + 1 : index; + return true; + } + return false; + }); } return { diff --git a/packages/lib/src/slider/Slider.tsx b/packages/lib/src/slider/Slider.tsx index 4748f1437d..2a1bce3d14 100644 --- a/packages/lib/src/slider/Slider.tsx +++ b/packages/lib/src/slider/Slider.tsx @@ -1,8 +1,8 @@ -import { forwardRef, useId, useMemo, useState } from "react"; +import { ChangeEvent, forwardRef, MouseEvent, useId, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import { getMargin } from "../common/utils"; -import { spaces } from "../common/variables"; import DxcTextInput from "../text-input/TextInput"; +import { spaces } from "../common/variables"; +import getMargin from "../common/utils"; import useTheme from "../useTheme"; import SliderPropsType, { RefType } from "./types"; @@ -49,10 +49,9 @@ const DxcSlider = forwardRef( const numberOfMarks = Math.floor(maxValue / step - minValue / step); const range = maxValue - minValue; const ticks = []; - let index = 0; if (marks) { - while (index <= numberOfMarks) { + for (let index = 0; index <= numberOfMarks; index++) { ticks.push( ( key={`tickmark-${index}-${labelId}`} /> ); - - index++; } return ticks; - } else return null; + } + return null; }, [minValue, maxValue, step, value, innerValue]); - const handleSliderChange = (event) => { - const valueToCheck = event.target.value; - (valueToCheck !== value || valueToCheck !== innerValue) && setInnerValue(valueToCheck); - onChange?.(valueToCheck); + const handleSliderChange = (event: ChangeEvent) => { + const intValue = parseInt(event.target.value, 10); + if (intValue !== value || intValue !== innerValue) { + setInnerValue(intValue); + } + onChange?.(intValue); }; const handleSliderDragging = () => { setDragging(true); }; - const handleSliderOnChangeCommitted = (event) => { + const handleSliderOnChangeCommitted = (event: MouseEvent) => { + const intValue = parseInt((event.target as HTMLInputElement).value, 10); if (dragging) { setDragging(false); - onDragEnd?.(event.target.value); + onDragEnd?.(intValue); } }; - const handlerInputChange = (event) => { + const handlerInputChange = (event: { value: string; error?: string }) => { const intValue = parseInt(event.value, 10); if (value == null) { - if (!Number.isNaN(intValue)) setInnerValue(intValue > maxValue ? maxValue : intValue); + if (!Number.isNaN(intValue)) { + setInnerValue(intValue > maxValue ? maxValue : intValue); + } } if (!Number.isNaN(intValue)) { onChange?.(intValue > maxValue ? maxValue : intValue); @@ -96,7 +99,7 @@ const DxcSlider = forwardRef( }; return ( - +
  • @@ -34,7 +34,7 @@ const Listbox = ({ {option.label} {option.options.map((singleOption) => { - globalIndex++; + globalIndex += 1; return ( ) ); - } else { - globalIndex++; - return ( - - ); } + globalIndex += 1; + return ( + + ); }; useLayoutEffect(() => { if (currentValue && !multiple) { const listEl = listboxRef?.current; - const selectedListOptionEl = listEl?.querySelector("[aria-selected='true']"); - listEl?.scrollTo?.({ top: selectedListOptionEl?.offsetTop - listEl?.clientHeight / 2 }); + const selectedListOptionEl = listEl?.querySelector("[aria-selected='true']") as HTMLUListElement; + listEl?.scrollTo?.({ + top: (selectedListOptionEl?.offsetTop || 0) - (listEl?.clientHeight || 0) / 2, + }); } }, [currentValue, multiple]); useLayoutEffect(() => { const visualFocusedOptionEl = listboxRef?.current?.querySelectorAll("[role='option']")[visualFocusIndex]; - visualFocusedOptionEl?.scrollIntoView?.({ block: "nearest", inline: "start" }); + visualFocusedOptionEl?.scrollIntoView?.({ + block: "nearest", + inline: "start", + }); }, [visualFocusIndex]); - const hasOptionGroups = options.some((option) => option.options?.length > 0); + const hasOptionGroups = options.some((option) => (option as ListOptionGroupType).options?.length > 0); return ( - {translatedLabels.select.noMatchesErrorMessage} + {translatedLabels?.select?.noMatchesErrorMessage} ) : ( optional && diff --git a/packages/lib/src/tabs/TabLegacy.tsx b/packages/lib/src/tabs/TabLegacy.tsx index 11145bad5b..75cf238ff2 100644 --- a/packages/lib/src/tabs/TabLegacy.tsx +++ b/packages/lib/src/tabs/TabLegacy.tsx @@ -1,4 +1,4 @@ -import { forwardRef, memo } from "react"; +import { forwardRef, memo, Ref } from "react"; import styled from "styled-components"; import DxcBadge from "../badge/Badge"; import DxcIcon from "../icon/Icon"; @@ -9,7 +9,7 @@ import { TabPropsLegacy } from "./types"; const Tab = forwardRef( ( { active, tab, tabIndex, hasLabelAndIcon, iconPosition, onClick, onMouseEnter, onMouseLeave }: TabPropsLegacy, - ref: React.Ref + ref: Ref ): JSX.Element => { const colorsTheme = useTheme(); @@ -47,15 +47,15 @@ const Tab = forwardRef( )} diff --git a/packages/lib/src/tabs/TabsLegacy.tsx b/packages/lib/src/tabs/TabsLegacy.tsx index 55b89c75d9..9635fbe8ff 100644 --- a/packages/lib/src/tabs/TabsLegacy.tsx +++ b/packages/lib/src/tabs/TabsLegacy.tsx @@ -7,7 +7,7 @@ import useTranslatedLabels from "../useTranslatedLabels"; import Tab from "./TabLegacy"; import TabsPropsType from "./types"; -const useResize = (refTabList) => { +const useResize = (refTabList: React.MutableRefObject) => { const [viewWidth, setViewWidth] = useState(0); const handleWindowSizeChange = useCallback(() => { @@ -36,10 +36,12 @@ const DxcTabs = ({ tabIndex = 0, }: TabsPropsType): JSX.Element => { const colorsTheme = useTheme(); - const hasLabelAndIcon = tabs && tabs.filter((tab) => tab.label && tab.icon).length > 0; - const firstFocus = tabs && tabs.findIndex((tab) => !tab.isDisabled); + const hasLabelAndIcon = tabs != null && tabs.filter((tab) => tab.label && tab.icon).length > 0; + const firstFocus = tabs != null ? tabs.findIndex((tab) => !tab.isDisabled) : null; const [innerActiveTabIndex, setInnerActiveTabIndex] = useState( - tabs && defaultActiveTabIndex && !tabs[defaultActiveTabIndex].isDisabled ? defaultActiveTabIndex : firstFocus + tabs != null && defaultActiveTabIndex && !tabs[defaultActiveTabIndex]?.isDisabled + ? defaultActiveTabIndex + : firstFocus ); const [activeIndicatorWidth, setActiveIndicatorWidth] = useState(0); const [activeIndicatorLeft, setActiveIndicatorLeft] = useState(0); @@ -51,43 +53,45 @@ const DxcTabs = ({ const [currentFocusIndex, setCurrentFocusIndex] = useState(activeTabIndex ?? innerActiveTabIndex); const [temporalFocusIndex, setTemporalFocusIndex] = useState(activeTabIndex ?? innerActiveTabIndex); const [minHeightTabs, setMinHeightTabs] = useState(0); - const refTabs = useRef([]); - const refTabList = useRef(null); + const refTabs = useRef([]); + const refTabList = useRef(null); const viewWidth = useResize(refTabList); const translatedLabels = useTranslatedLabels(); const enabledIndicator = useMemo(() => viewWidth < totalTabsWidth, [viewWidth]); useEffect(() => { - let sumWidth = refTabs?.current?.reduce(function (count, obj) { - return count + obj.offsetWidth; - }, 0); - setTotalTabsWidth(sumWidth); - setActiveIndicatorWidth(refTabs?.current[activeTabIndex ?? innerActiveTabIndex]?.offsetWidth); - setActiveIndicatorLeft(refTabs?.current[activeTabIndex ?? innerActiveTabIndex]?.offsetLeft); + if (activeTabIndex != null || innerActiveTabIndex != null) { + const sumWidth = refTabs?.current?.reduce((count, obj) => count + obj.offsetWidth, 0); + setTotalTabsWidth(sumWidth); + setActiveIndicatorWidth(refTabs?.current[activeTabIndex ?? innerActiveTabIndex!]?.offsetWidth ?? 0); + setActiveIndicatorLeft(refTabs?.current[activeTabIndex ?? innerActiveTabIndex!]?.offsetLeft ?? 0); + } }, [refTabs]); useEffect(() => { - setMinHeightTabs(refTabList?.current?.offsetHeight + 1); + setMinHeightTabs((refTabList?.current?.offsetHeight || 0) + 1); }, [refTabList]); useEffect(() => { - if (activeTabIndex >= 0) { - setActiveIndicatorWidth(refTabs?.current[activeTabIndex]?.offsetWidth); - setActiveIndicatorLeft(refTabs?.current[activeTabIndex]?.offsetLeft); + if (activeTabIndex && activeTabIndex >= 0) { + setActiveIndicatorWidth(refTabs?.current[activeTabIndex]?.offsetWidth ?? 0); + setActiveIndicatorLeft(refTabs?.current[activeTabIndex]?.offsetLeft ?? 0); } }, [activeTabIndex]); - const handleSelected = (newValue) => { - activeTabIndex ?? setInnerActiveTabIndex(newValue); + const handleSelected = (newValue: number) => { + if (activeTabIndex == null) { + setInnerActiveTabIndex(newValue); + } onTabClick?.(newValue); if (activeTabIndex === undefined) { - setActiveIndicatorWidth(refTabs?.current[newValue]?.offsetWidth); - setActiveIndicatorLeft(refTabs?.current[newValue]?.offsetLeft); + setActiveIndicatorWidth(refTabs?.current[newValue]?.offsetWidth ?? 0); + setActiveIndicatorLeft(refTabs?.current[newValue]?.offsetLeft ?? 0); } }; const scrollLeft = () => { - const scrollWidth = refTabList?.current?.offsetWidth * 0.75; + const scrollWidth = (refTabList?.current?.offsetHeight || 0) * 0.75; let moveX = 0; if (countClick <= scrollWidth) { moveX = 0; @@ -103,10 +107,10 @@ const DxcTabs = ({ }; const scrollRight = () => { - const scrollWidth = refTabList?.current?.offsetWidth * 0.75; + const scrollWidth = (refTabList?.current?.offsetHeight || 0) * 0.75; let moveX = 0; - if (countClick + scrollWidth + refTabList?.current?.offsetWidth >= totalTabsWidth) { - moveX = totalTabsWidth - refTabList?.current?.offsetWidth; + if (countClick + scrollWidth + (refTabList?.current?.offsetHeight || 0) >= totalTabsWidth) { + moveX = totalTabsWidth - (refTabList?.current?.offsetHeight || 0); setScrollRightEnabled(false); setScrollLeftEnabled(true); } else { @@ -119,51 +123,64 @@ const DxcTabs = ({ }; const setPreviousTabFocus = () => { - setTemporalFocusIndex((temporalFocusIndex) => { - let index = temporalFocusIndex === 0 ? tabs.length - 1 : temporalFocusIndex - 1; - while (tabs[index].isDisabled) { - index = index === 0 ? tabs.length - 1 : index - 1; - } - refTabs?.current[index].focus({ preventScroll: true }); - setScrollFocus(index); - return index; - }); + if (tabs) { + setTemporalFocusIndex((currentTemporalFocusIndex) => { + if (currentTemporalFocusIndex != null) { + let index = currentTemporalFocusIndex === 0 ? tabs.length - 1 : currentTemporalFocusIndex - 1; + while (tabs[index]?.isDisabled) { + index = index === 0 ? tabs.length - 1 : index - 1; + } + refTabs?.current[index]?.focus({ preventScroll: true }); + setScrollFocus(index); + return index; + } + return null; + }); + } }; const setNextTabFocus = () => { - setTemporalFocusIndex((temporalFocusIndex) => { - let index = temporalFocusIndex === tabs.length - 1 ? 0 : temporalFocusIndex + 1; - while (tabs[index].isDisabled) { - index = index === tabs.length - 1 ? 0 : index + 1; - } - refTabs?.current[index].focus({ preventScroll: true }); - setScrollFocus(index); - return index; - }); + if (tabs) { + setTemporalFocusIndex((currentTemporalFocusIndex) => { + if (currentTemporalFocusIndex != null) { + let index = currentTemporalFocusIndex === tabs.length - 1 ? 0 : currentTemporalFocusIndex + 1; + while (tabs[index]?.isDisabled) { + index = index === tabs.length - 1 ? 0 : index + 1; + } + refTabs?.current[index]?.focus({ preventScroll: true }); + setScrollFocus(index); + return index; + } + return null; + }); + } }; const setScrollFocus = (actualIndex: number) => { - let sumPrev = 0; - refTabs?.current?.map((item, index) => { - if (index <= actualIndex) { - sumPrev += item.offsetWidth; + if (tabs) { + let sumPrev = 0; + refTabs?.current?.forEach((item, index) => { + if (index <= actualIndex) { + sumPrev += item.offsetWidth; + } + }); + let moveX = 0; + + if (actualIndex === tabs.length - 1) { + moveX = totalTabsWidth - (refTabList?.current?.offsetHeight || 0); + setScrollLeftEnabled(true); + setScrollRightEnabled(false); + } else if (refTabList?.current?.offsetWidth && sumPrev > refTabList?.current?.offsetWidth) { + moveX = sumPrev - (refTabList?.current?.offsetHeight || 0) + 1; // plus 1px for the outline + setScrollLeftEnabled(true); + setScrollRightEnabled(true); + } else { + setScrollLeftEnabled(false); + setScrollRightEnabled(true); } - }); - let moveX = 0; - if (actualIndex === tabs.length - 1) { - moveX = totalTabsWidth - refTabList?.current?.offsetWidth; - setScrollLeftEnabled(true); - setScrollRightEnabled(false); - } else if (sumPrev > refTabList?.current?.offsetWidth) { - moveX = sumPrev - refTabList?.current?.offsetWidth + 1; //plus 1px for the outline - setScrollLeftEnabled(true); - setScrollRightEnabled(true); - } else { - setScrollLeftEnabled(false); - setScrollRightEnabled(true); + setTranslateScroll(-moveX); + setCountClick(moveX); } - setTranslateScroll(-moveX); - setCountClick(moveX); }; const handleOnKeyDown = (event: React.KeyboardEvent) => { @@ -180,27 +197,35 @@ const DxcTabs = ({ break; case "Enter": case " ": - event.preventDefault(); - setCurrentFocusIndex(temporalFocusIndex); - handleSelected(temporalFocusIndex); + if (temporalFocusIndex != null) { + event.preventDefault(); + setCurrentFocusIndex(temporalFocusIndex); + handleSelected(temporalFocusIndex); + } break; case "Tab": - if (temporalFocusIndex !== currentFocusIndex) { - event.preventDefault(); - setTemporalFocusIndex(currentFocusIndex); - refTabs?.current[currentFocusIndex].focus(); + if (currentFocusIndex != null) { + if (temporalFocusIndex !== currentFocusIndex) { + event.preventDefault(); + setTemporalFocusIndex(currentFocusIndex); + refTabs?.current[currentFocusIndex]?.focus(); + } + handleSelected(currentFocusIndex); } - handleSelected(currentFocusIndex); + break; + default: break; } }; - const isTabActive = (index) => (activeTabIndex >= 0 ? activeTabIndex === index : innerActiveTabIndex === index); + const isTabActive = (index: number) => + activeTabIndex != null && activeTabIndex >= 0 ? activeTabIndex === index : innerActiveTabIndex === index; const isActiveIndicatorDisabled = - firstFocus === -1 || (tabs && activeTabIndex >= 0 && tabs[activeTabIndex].isDisabled); + firstFocus === -1 || + (tabs != null && activeTabIndex !== undefined && activeTabIndex >= 0 && !!tabs[activeTabIndex]?.isDisabled); return ( - + @@ -208,24 +233,24 @@ const DxcTabs = ({ onClick={scrollLeft} enabled={enabledIndicator} disabled={!scrollLeftEnabled} - aria-label={translatedLabels.tabs.scrollLeft} + aria-label={translatedLabels?.tabs?.scrollLeft} tabIndex={scrollLeftEnabled ? tabIndex : -1} minHeightTabs={minHeightTabs} > - + - {tabs.map((tab, i) => ( + {tabs?.map((tab, i) => ( { + ref={(el: HTMLButtonElement) => { refTabs.current[i] = el; }} onClick={() => { @@ -253,11 +278,11 @@ const DxcTabs = ({ onClick={scrollRight} enabled={enabledIndicator} disabled={!scrollRightEnabled} - aria-label={translatedLabels.tabs.scrollRight} + aria-label={translatedLabels?.tabs?.scrollRight} tabIndex={scrollRightEnabled ? tabIndex : -1} minHeightTabs={minHeightTabs} > - + diff --git a/packages/lib/src/text-input/Suggestions.tsx b/packages/lib/src/text-input/Suggestions.tsx index c7fcf8a131..c772dc7965 100644 --- a/packages/lib/src/text-input/Suggestions.tsx +++ b/packages/lib/src/text-input/Suggestions.tsx @@ -17,17 +17,20 @@ const Suggestions = ({ styles, }: SuggestionsProps): JSX.Element => { const translatedLabels = useTranslatedLabels(); - const listboxRef = useRef(null); + const listboxRef = useRef(null); useEffect(() => { const visualFocusedOptionEl = listboxRef?.current?.querySelectorAll("[role='option']")[visualFocusIndex]; - visualFocusedOptionEl?.scrollIntoView?.({ block: "nearest", inline: "start" }); + visualFocusedOptionEl?.scrollIntoView?.({ + block: "nearest", + inline: "start", + }); }, [visualFocusIndex]); return ( { event.preventDefault(); }} @@ -52,7 +55,9 @@ const Suggestions = ({ /> ))} {isSearching && ( - {translatedLabels.textInput.searchingMessage} + + {translatedLabels?.textInput?.searchingMessage} + )} {searchHasErrors && ( @@ -60,7 +65,7 @@ const Suggestions = ({ - {translatedLabels.textInput.fetchingDataErrorMessage} + {translatedLabels?.textInput?.fetchingDataErrorMessage} )} diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx index bd56963947..2cd3fa6c19 100644 --- a/packages/lib/src/toast/Toast.tsx +++ b/packages/lib/src/toast/Toast.tsx @@ -116,17 +116,22 @@ const ToastIcon = memo( loading, semantic, }: Pick) => { - if (semantic === "default") return typeof icon === "string" ? : icon; - else if (semantic === "info" && loading) + if (semantic === "default") { + return typeof icon === "string" ? : icon; + } + if (semantic === "info" && loading) { return ( ); - else return !hideSemanticIcon && ; + } + return !hideSemanticIcon && ; } ); +ToastIcon.displayName = "ToastIcon"; + const DxcToast = ({ action, duration, @@ -144,14 +149,14 @@ const DxcToast = ({ () => { setIsClosing(true); }, - loading ? null : duration - 300 + loading ? undefined : duration - 300 ); const clearTimer = useTimeout( () => { onClear(); }, - loading ? null : duration + loading ? undefined : duration ); return ( @@ -173,7 +178,7 @@ const DxcToast = ({ )} { if (!loading) { clearClosingAnimationTimer(); @@ -190,4 +195,6 @@ const DxcToast = ({ ); }; +DxcToast.displayName = "DxcToast"; + export default memo(DxcToast); diff --git a/packages/lib/src/utils/BaseTypography.tsx b/packages/lib/src/utils/BaseTypography.tsx index 9671e979d3..42ec55c213 100644 --- a/packages/lib/src/utils/BaseTypography.tsx +++ b/packages/lib/src/utils/BaseTypography.tsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useMemo } from "react"; +import { createContext, ReactNode, useContext, useMemo } from "react"; import styled from "styled-components"; type TypographyContextProps = { @@ -20,7 +20,7 @@ type TypographyContextProps = { const TypographyContext = createContext(null); type BaseTypographyProps = TypographyContextProps & { - children: React.ReactNode; + children: ReactNode; }; const ValidTypographyTags = [ @@ -44,9 +44,8 @@ const ValidTypographyTags = [ "strong", ]; -const isValidTypography = (tag: keyof HTMLElementTagNameMap) => { - return ValidTypographyTags.includes(tag); -}; +const isValidTypography = (tag: keyof HTMLElementTagNameMap | undefined) => + tag != null && ValidTypographyTags.includes(tag); const BaseTypography = ({ as, diff --git a/packages/lib/src/utils/FocusLock.tsx b/packages/lib/src/utils/FocusLock.tsx index 889510c5be..a09e179eaf 100644 --- a/packages/lib/src/utils/FocusLock.tsx +++ b/packages/lib/src/utils/FocusLock.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { KeyboardEvent, MutableRefObject, ReactNode, useCallback, useEffect, useRef, useState } from "react"; const not = { negTabIndex: ':not([tabindex^="-"])', @@ -19,15 +19,19 @@ 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 | null): HTMLElement[] | null => { + if (container != null) { + return 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" + ); + } + return null; +}; /** * This function will try to focus the element and return true if it was able to receive the focus. @@ -56,24 +60,29 @@ const radixPortalContains = (activeElement: Element): boolean => { /** * Custom hook that returns an array of focusable elements inside a container. - * @param ref: React.MutableRefObject + * @param ref: MutableRefObject * @returns */ -const useFocusableElements = (ref: React.MutableRefObject): HTMLElement[] => { - const [focusableElements, setFocusableElements] = useState(); +const useFocusableElements = (ref: MutableRefObject): HTMLElement[] | null => { + const [focusableElements, setFocusableElements] = useState(null); useEffect(() => { - if (ref.current != null) { + if (ref?.current != null) { setFocusableElements(getFocusableElements(ref.current)); const observer = new MutationObserver(() => { - setFocusableElements(getFocusableElements(ref.current)); + setFocusableElements(getFocusableElements(ref?.current)); + }); + observer.observe(ref.current, { + childList: true, + subtree: true, + attributes: true, }); - observer.observe(ref.current, { childList: true, subtree: true, attributes: true }); return () => { observer.disconnect(); }; } + return undefined; }, []); return focusableElements; @@ -84,29 +93,39 @@ const useFocusableElements = (ref: React.MutableRefObject): HTML * When the focus is on the last focusable element and the user tries to focus the next element, it will focus the first element. * When the focus is on the first focusable element and the user tries to focus the previous element, it will focus the last element. * The focus can't be moved outside the children unless the children are removed from the DOM (for example, a Dialog, a Modal, etc). - * @param children: React.ReactNode + * @param children: ReactNode * @returns */ -const FocusLock = ({ children }: { children: React.ReactNode }): JSX.Element => { - const childrenContainerRef = useRef(); +const FocusLock = ({ children }: { children: ReactNode }): JSX.Element => { + const childrenContainerRef = useRef(null); const focusableElements = useFocusableElements(childrenContainerRef); const focusFirst = useCallback(() => { - 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 && focusableElements?.length > 0) { + focusableElements.some((element) => attemptFocus(element)); + } }, [focusableElements]); const focusLast = () => { focusableElements?.reverse()?.some((element) => attemptFocus(element)); }; - const focusLock = (event: React.KeyboardEvent) => { - if (event.key === "Tab") focusableElements.length === 0 && event.preventDefault(); + const focusLock = (event: KeyboardEvent) => { + if (event.key === "Tab" && focusableElements && focusableElements.length === 0) { + event.preventDefault(); + } }; useEffect(() => { - if (!childrenContainerRef.current?.contains(document.activeElement) && !radixPortalContains(document.activeElement)) + if ( + document?.activeElement && + !childrenContainerRef.current?.contains(document.activeElement) && + !radixPortalContains(document.activeElement) + ) { focusFirst(); + } }, [focusFirst]); return ( From 91f9abf7a5adff91e60d0a0c0fe9aa435c8da772 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Wed, 4 Dec 2024 11:11:55 +0100 Subject: [PATCH 03/41] Added remaining required changes for strict mode --- packages/lib/src/slider/Slider.test.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/lib/src/slider/Slider.test.tsx b/packages/lib/src/slider/Slider.test.tsx index 6f8fa28c8a..dda72ef77e 100644 --- a/packages/lib/src/slider/Slider.test.tsx +++ b/packages/lib/src/slider/Slider.test.tsx @@ -41,7 +41,12 @@ describe("Slider component tests", () => { expect(slider.getAttribute("aria-valuemin")).toBe("30"); expect(slider.getAttribute("aria-valuemax")).toBe("125"); userEvent.tab(); - fireEvent.keyDown(slider, { key: "ArrowRight", code: "ArrowRight", keyCode: 39, charCode: 39 }); + fireEvent.keyDown(slider, { + key: "ArrowRight", + code: "ArrowRight", + keyCode: 39, + charCode: 39, + }); expect(slider.getAttribute("aria-valuenow")).toBe("125"); expect(getByText("30")).toBeTruthy(); expect(getByText("125")).toBeTruthy(); @@ -101,7 +106,7 @@ describe("Slider component tests", () => { act(() => { fireEvent.mouseUp(slider, { target: { value: 120 } }); }); - expect(onDragEnd).toHaveBeenCalledWith("120"); + expect(onDragEnd).toHaveBeenCalledWith(120); }); test("Calls correct function onDragEnd when it is controlled", () => { @@ -114,7 +119,7 @@ describe("Slider component tests", () => { act(() => { fireEvent.mouseUp(slider, { target: { value: 120 } }); }); - expect(onDragEnd).toHaveBeenCalledWith("120"); + expect(onDragEnd).toHaveBeenCalledWith(120); expect(slider.getAttribute("aria-valuenow")).toBe("50"); }); @@ -142,7 +147,12 @@ describe("Slider component tests", () => { ); const slider = getByRole("slider"); userEvent.tab(); - fireEvent.keyDown(slider, { key: "ArrowRight", code: "ArrowRight", keyCode: 39, charCode: 39 }); + fireEvent.keyDown(slider, { + key: "ArrowRight", + code: "ArrowRight", + keyCode: 39, + charCode: 39, + }); rerender(); expect(slider.getAttribute("aria-valuenow")).toBe("0"); }); From 0350f136827561ebc2e8a995d0ba477b8af9b283 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Wed, 4 Dec 2024 11:12:19 +0100 Subject: [PATCH 04/41] Removed unintentional changes --- packages/lib/src/toast/Toast.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx index 2cd3fa6c19..c0951acc8a 100644 --- a/packages/lib/src/toast/Toast.tsx +++ b/packages/lib/src/toast/Toast.tsx @@ -130,8 +130,6 @@ const ToastIcon = memo( } ); -ToastIcon.displayName = "ToastIcon"; - const DxcToast = ({ action, duration, @@ -195,6 +193,4 @@ const DxcToast = ({ ); }; -DxcToast.displayName = "DxcToast"; - export default memo(DxcToast); From 2c6fec923581a81cbadcf8e30c653ad10eb75805 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Thu, 5 Dec 2024 17:44:20 +0100 Subject: [PATCH 05/41] Fixed problems with tests due to focus lock --- packages/lib/src/utils/FocusLock.tsx | 52 ++++++++++------------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/packages/lib/src/utils/FocusLock.tsx b/packages/lib/src/utils/FocusLock.tsx index cb365fe50b..8079a112cc 100644 --- a/packages/lib/src/utils/FocusLock.tsx +++ b/packages/lib/src/utils/FocusLock.tsx @@ -1,4 +1,4 @@ -import { KeyboardEvent, MutableRefObject, ReactNode, useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; const not = { negTabIndex: ':not([tabindex^="-"])', @@ -19,19 +19,15 @@ const focusableQuery = [ `[tabindex]${not.negTabIndex}${not.disabled}`, ].join(","); -const getFocusableElements = (container: HTMLElement | null): HTMLElement[] | null => { - if (container != null) { - return 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" - ); - } - return null; -}; +const getFocusableElements = (container: HTMLElement | null): 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" + ); /** * This function will try to focus the element and return true if it was able to receive the focus. @@ -60,10 +56,10 @@ const radixPortalContains = (activeElement: Node): boolean => { /** * Custom hook that returns an array of focusable elements inside a container. - * @param ref: MutableRefObject + * @param ref: React.MutableRefObject * @returns */ -const useFocusableElements = (ref: MutableRefObject): HTMLElement[] | null => { +const useFocusableElements = (ref: React.MutableRefObject): HTMLElement[] | null => { const [focusableElements, setFocusableElements] = useState(null); useEffect(() => { @@ -73,17 +69,11 @@ const useFocusableElements = (ref: MutableRefObject): HTM const observer = new MutationObserver(() => { setFocusableElements(getFocusableElements(ref?.current)); }); - observer.observe(ref.current, { - childList: true, - subtree: true, - attributes: true, - }); observer.observe(ref.current, { childList: true, subtree: true }); return () => { observer.disconnect(); }; } - return undefined; }, []); return focusableElements; @@ -94,20 +84,18 @@ const useFocusableElements = (ref: MutableRefObject): HTM * When the focus is on the last focusable element and the user tries to focus the next element, it will focus the first element. * When the focus is on the first focusable element and the user tries to focus the previous element, it will focus the last element. * The focus can't be moved outside the children unless the children are removed from the DOM (for example, a Dialog, a Modal, etc). - * @param children: ReactNode + * @param children: React.ReactNode * @returns */ -const FocusLock = ({ children }: { children: ReactNode }): JSX.Element => { +const FocusLock = ({ children }: { children: React.ReactNode }): JSX.Element => { const childrenContainerRef = useRef(null); const focusableElements = useFocusableElements(childrenContainerRef); const initialFocus = useRef(false); const focusFirst = useCallback(() => { - if (focusableElements?.length === 0) { - childrenContainerRef.current?.focus(); - } else if (focusableElements && focusableElements?.length > 0) { + if (focusableElements?.length === 0) childrenContainerRef.current?.focus(); + else if (focusableElements && focusableElements?.length > 0) focusableElements.some((element) => attemptFocus(element)); - } }, [focusableElements]); const focusLast = () => { @@ -117,14 +105,12 @@ const FocusLock = ({ children }: { children: ReactNode }): JSX.Element => { ?.some((element) => attemptFocus(element)); }; - const focusLock = (event: KeyboardEvent) => { - if (event.key === "Tab" && focusableElements && focusableElements.length === 0) { - event.preventDefault(); - } + const focusLock = (event: React.KeyboardEvent) => { + if (event.key === "Tab" && focusableElements && focusableElements.length === 0) event.preventDefault(); }; useEffect(() => { - if (focusableElements !== undefined && !initialFocus.current) { + if (focusableElements != null && !initialFocus.current) { initialFocus.current = true; focusFirst(); } From 05958a5ddaee81513d97ab2246ed4f8c072399f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:14:41 +0100 Subject: [PATCH 06/41] Restore Breadcrumbs item keys --- packages/lib/src/breadcrumbs/Breadcrumbs.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.tsx index c4b5c09389..52a30c90e1 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.tsx @@ -33,8 +33,8 @@ const DxcBreadcrumbs = ({ {items && items.length > Math.max(itemsBeforeCollapse, 2) ? ( <> - {showRoot && } - + {showRoot && } + - + ) : ( items.map((item, index, { length }) => ( From 7880bd3ead65f5027dc1499d869152244d5f63b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:40:32 +0100 Subject: [PATCH 07/41] Badge component updates --- packages/lib/src/badge/Badge.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lib/src/badge/Badge.tsx b/packages/lib/src/badge/Badge.tsx index bf2e241804..54369e04ca 100644 --- a/packages/lib/src/badge/Badge.tsx +++ b/packages/lib/src/badge/Badge.tsx @@ -104,10 +104,10 @@ const DxcBadge = ({ ); const getColor = (mode: BadgePropsType["mode"], color: BadgePropsType["color"]) => - mode === "contextual" ? color && contextualColorMap[color].text : CoreTokens.color_white; + mode === "contextual" && color ? contextualColorMap[color].text : CoreTokens.color_white; const getBackgroundColor = (mode: BadgePropsType["mode"], color: BadgePropsType["color"]) => - mode === "contextual" ? color && contextualColorMap[color].background : CoreTokens.color_red_700; + mode === "contextual" && color ? contextualColorMap[color].background : CoreTokens.color_red_700; const getPadding = (mode: BadgePropsType["mode"], size: BadgePropsType["size"]) => size && (mode === "contextual" ? sizeMap[size].padding.contextual : sizeMap[size].padding.notification); From 0972270859282bbafb44f68b0f8245bd4b2799d5 Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Fri, 13 Dec 2024 14:52:00 +0100 Subject: [PATCH 08/41] Added more corrections from review --- apps/website/screens/theme-generator/utils.ts | 2 +- apps/website/tsconfig.json | 3 +-- packages/lib/src/HalstackContext.tsx | 4 +--- packages/lib/src/accordion-group/AccordionGroup.tsx | 2 +- .../lib/src/accordion-group/AccordionGroupContext.tsx | 4 +--- packages/lib/src/accordion/Accordion.tsx | 2 +- packages/lib/src/alert/Alert.tsx | 8 +++----- packages/lib/src/button/Button.tsx | 2 +- packages/lib/src/checkbox/Checkbox.tsx | 2 +- packages/lib/src/chip/Chip.tsx | 2 +- packages/lib/src/common/utils.ts | 4 +--- packages/lib/src/date-input/DateInput.tsx | 2 +- packages/lib/src/dropdown/Dropdown.tsx | 2 +- packages/lib/src/resultset-table/ResultsetTable.tsx | 2 +- packages/lib/src/select/Select.tsx | 2 +- packages/lib/src/slider/Slider.tsx | 2 +- packages/lib/src/switch/Switch.tsx | 2 +- packages/lib/src/table/Table.tsx | 2 +- packages/lib/src/tag/Tag.tsx | 2 +- packages/lib/src/text-input/TextInput.tsx | 2 +- packages/lib/src/textarea/Textarea.tsx | 2 +- packages/typescript-config/react-library.json | 1 - 22 files changed, 23 insertions(+), 33 deletions(-) diff --git a/apps/website/screens/theme-generator/utils.ts b/apps/website/screens/theme-generator/utils.ts index 64d2eeeccf..4c648ee555 100644 --- a/apps/website/screens/theme-generator/utils.ts +++ b/apps/website/screens/theme-generator/utils.ts @@ -10,7 +10,7 @@ export const makeReadableSidenav = (token: string) => return i === 0 ? v.toUpperCase() : " " + v; }); -const isObject = (item: unknown) => item && typeof item === "object" && !Array.isArray(item); +const isObject = (item: unknown) => item != null && typeof item === "object" && !Array.isArray(item); export const deepMerge = (target: T, ...sources: Partial[]): T => { if (!sources.length) return target; diff --git a/apps/website/tsconfig.json b/apps/website/tsconfig.json index 9e853b6beb..4778c348cb 100644 --- a/apps/website/tsconfig.json +++ b/apps/website/tsconfig.json @@ -7,8 +7,7 @@ "lib": ["dom", "dom.iterable", "esnext"], "paths": { "@/common/*": ["screens/common/*"] - }, - "strict": true + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index 6cfa406495..4fd5e6ec4e 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -430,6 +430,4 @@ const HalstackProvider = ({ theme, advancedTheme, labels, children }: HalstackPr ); }; -export { HalstackProvider, HalstackLanguageContext }; - -export default HalstackContext; +export { HalstackContext as default, HalstackProvider, HalstackLanguageContext }; diff --git a/packages/lib/src/accordion-group/AccordionGroup.tsx b/packages/lib/src/accordion-group/AccordionGroup.tsx index d412ea9dc2..54e331700c 100644 --- a/packages/lib/src/accordion-group/AccordionGroup.tsx +++ b/packages/lib/src/accordion-group/AccordionGroup.tsx @@ -1,6 +1,6 @@ import { Children, useCallback, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import useTheme from "../useTheme"; import AccordionGroupAccordion from "./AccordionGroupAccordion"; diff --git a/packages/lib/src/accordion-group/AccordionGroupContext.tsx b/packages/lib/src/accordion-group/AccordionGroupContext.tsx index 70f6812ec4..55c46ee9b8 100644 --- a/packages/lib/src/accordion-group/AccordionGroupContext.tsx +++ b/packages/lib/src/accordion-group/AccordionGroupContext.tsx @@ -1,6 +1,4 @@ import { createContext } from "react"; import type { AccordionGroupAccordionContextProps } from "./types"; -const AccordionGroupAccordionContext = createContext(null); - -export default AccordionGroupAccordionContext; +export default createContext(null); diff --git a/packages/lib/src/accordion/Accordion.tsx b/packages/lib/src/accordion/Accordion.tsx index 24bf7cc78e..e3637a9ffe 100644 --- a/packages/lib/src/accordion/Accordion.tsx +++ b/packages/lib/src/accordion/Accordion.tsx @@ -1,6 +1,6 @@ import { useId, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import useTheme from "../useTheme"; import AccordionPropsType from "./types"; diff --git a/packages/lib/src/alert/Alert.tsx b/packages/lib/src/alert/Alert.tsx index 60cc490209..519d5a35c3 100644 --- a/packages/lib/src/alert/Alert.tsx +++ b/packages/lib/src/alert/Alert.tsx @@ -125,7 +125,7 @@ const getIcon = (semantic: AlertPropsType["semantic"]) => { } }; -const DxcAlert = ({ +export default function DxcAlert({ closable = true, message = [], mode = "inline", @@ -133,7 +133,7 @@ const DxcAlert = ({ secondaryAction, semantic = "info", title = "", -}: AlertPropsType) => { +}: AlertPropsType) { const [messages, setMessages] = useState(Array.isArray(message) ? message : [message]); const [currentIndex, setCurrentIndex] = useState(0); @@ -245,6 +245,4 @@ const DxcAlert = ({ ); -}; - -export default DxcAlert; +} diff --git a/packages/lib/src/button/Button.tsx b/packages/lib/src/button/Button.tsx index 5a9a3d471f..b3ced6662d 100644 --- a/packages/lib/src/button/Button.tsx +++ b/packages/lib/src/button/Button.tsx @@ -1,6 +1,6 @@ import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import useTheme from "../useTheme"; import type ButtonPropsType from "./types"; import DxcIcon from "../icon/Icon"; diff --git a/packages/lib/src/checkbox/Checkbox.tsx b/packages/lib/src/checkbox/Checkbox.tsx index 02e252b40c..6de35f6eb5 100644 --- a/packages/lib/src/checkbox/Checkbox.tsx +++ b/packages/lib/src/checkbox/Checkbox.tsx @@ -1,7 +1,7 @@ import { useState, useRef, useId, forwardRef, KeyboardEvent } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import CheckboxPropsType, { RefType } from "./types"; diff --git a/packages/lib/src/chip/Chip.tsx b/packages/lib/src/chip/Chip.tsx index 5981acb045..7681b56a45 100644 --- a/packages/lib/src/chip/Chip.tsx +++ b/packages/lib/src/chip/Chip.tsx @@ -1,5 +1,5 @@ import styled, { ThemeProvider } from "styled-components"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; import useTheme from "../useTheme"; diff --git a/packages/lib/src/common/utils.ts b/packages/lib/src/common/utils.ts index a87c7d612f..077cd1ad97 100644 --- a/packages/lib/src/common/utils.ts +++ b/packages/lib/src/common/utils.ts @@ -10,11 +10,9 @@ export type Margin = { export type SVG = React.ReactNode & React.SVGProps; export type Side = keyof Margin; -const getMargin = (marginProp: Space | Margin | undefined, side: Side) => +export const getMargin = (marginProp: Space | Margin | undefined, side: Side) => marginProp && typeof marginProp === "object" ? (marginProp[side] && spaces[marginProp[side]]) || "0px" : marginProp && typeof marginProp === "string" ? spaces[marginProp] : "0px"; - -export default getMargin; diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index b89a14b411..9b03f6ded6 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -18,7 +18,7 @@ import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import DateInputPropsType, { RefType } from "./types"; import DatePicker from "./DatePicker"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcTextInput from "../text-input/TextInput"; diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index a4bde31dd3..37ed78acc7 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -1,7 +1,7 @@ import * as Popover from "@radix-ui/react-popover"; import { FocusEvent, KeyboardEvent, useCallback, useId, useLayoutEffect, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; import useTheme from "../useTheme"; diff --git a/packages/lib/src/resultset-table/ResultsetTable.tsx b/packages/lib/src/resultset-table/ResultsetTable.tsx index c4a53dd459..59d2b022c4 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.tsx @@ -1,7 +1,7 @@ import { ReactNode, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import CoreTokens from "../common/coreTokens"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcPaginator from "../paginator/Paginator"; import DxcTable, { DxcActionsCell } from "../table/Table"; diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index 73e3832446..3ddc741f06 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -2,7 +2,7 @@ import * as Popover from "@radix-ui/react-popover"; import { ChangeEvent, FocusEvent, forwardRef, KeyboardEvent, MouseEvent, useCallback, useId, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import DxcIcon from "../icon/Icon"; import { Tooltip, TooltipWrapper } from "../tooltip/Tooltip"; import useTheme from "../useTheme"; diff --git a/packages/lib/src/slider/Slider.tsx b/packages/lib/src/slider/Slider.tsx index 2a1bce3d14..5469ad1a87 100644 --- a/packages/lib/src/slider/Slider.tsx +++ b/packages/lib/src/slider/Slider.tsx @@ -2,7 +2,7 @@ import { ChangeEvent, forwardRef, MouseEvent, useId, useMemo, useState } from "r import styled, { ThemeProvider } from "styled-components"; import DxcTextInput from "../text-input/TextInput"; import { spaces } from "../common/variables"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import useTheme from "../useTheme"; import SliderPropsType, { RefType } from "./types"; diff --git a/packages/lib/src/switch/Switch.tsx b/packages/lib/src/switch/Switch.tsx index e99b16a2a5..90e2b8418a 100644 --- a/packages/lib/src/switch/Switch.tsx +++ b/packages/lib/src/switch/Switch.tsx @@ -1,7 +1,7 @@ import { forwardRef, KeyboardEvent, useId, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; import SwitchPropsType, { RefType } from "./types"; diff --git a/packages/lib/src/table/Table.tsx b/packages/lib/src/table/Table.tsx index 1ce6624cd5..62f1aae2c6 100644 --- a/packages/lib/src/table/Table.tsx +++ b/packages/lib/src/table/Table.tsx @@ -1,6 +1,6 @@ import styled, { ThemeProvider } from "styled-components"; import { spaces, AdvancedTheme } from "../common/variables"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import DxcDropdown from "../dropdown/Dropdown"; import DxcFlex from "../flex/Flex"; import { DeepPartial, HalstackProvider } from "../HalstackContext"; diff --git a/packages/lib/src/tag/Tag.tsx b/packages/lib/src/tag/Tag.tsx index e54253a4a0..e1bd13065c 100644 --- a/packages/lib/src/tag/Tag.tsx +++ b/packages/lib/src/tag/Tag.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import useTheme from "../useTheme"; import DxcIcon from "../icon/Icon"; diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx index be932fa8cb..ef30062e09 100644 --- a/packages/lib/src/text-input/TextInput.tsx +++ b/packages/lib/src/text-input/TextInput.tsx @@ -15,7 +15,7 @@ import { } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcActionIcon from "../action-icon/ActionIcon"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; diff --git a/packages/lib/src/textarea/Textarea.tsx b/packages/lib/src/textarea/Textarea.tsx index 9ff3f0cc73..7d9c20d290 100644 --- a/packages/lib/src/textarea/Textarea.tsx +++ b/packages/lib/src/textarea/Textarea.tsx @@ -1,6 +1,6 @@ import { ChangeEvent, FocusEvent, forwardRef, useEffect, useId, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import getMargin from "../common/utils"; +import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import useTheme from "../useTheme"; import useTranslatedLabels from "../useTranslatedLabels"; diff --git a/packages/typescript-config/react-library.json b/packages/typescript-config/react-library.json index e6b27bee8b..058d6e2a82 100644 --- a/packages/typescript-config/react-library.json +++ b/packages/typescript-config/react-library.json @@ -6,7 +6,6 @@ "declaration": true, "declarationMap": true, "jsx": "react-jsx", - "strict": true, "allowSyntheticDefaultImports": true } } From c710645a9b113a0bbaf1c7742dd0b5d8455c761d Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Fri, 13 Dec 2024 14:59:42 +0100 Subject: [PATCH 09/41] Maintained same structure for HalstackContext --- packages/lib/src/HalstackContext.tsx | 31 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index 4fd5e6ec4e..3db2aa4068 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -122,21 +122,22 @@ const parseTheme = (theme: DeepPartial): AdvancedTheme => { chipTokens.hoverIconColor = subLightness(10, theme?.chip?.iconColor) ?? chipTokens.hoverIconColor; chipTokens.activeIconColor = subLightness(30, theme?.chip?.iconColor) ?? chipTokens.activeIconColor; - const { contextualMenu } = componentTokensCopy; - contextualMenu.selectedMenuItemBackgroundColor = - theme?.contextualMenu?.accentColor ?? contextualMenu.selectedMenuItemBackgroundColor; - contextualMenu.hoverSelectedMenuItemBackgroundColor = - subLightness(5, theme?.contextualMenu?.accentColor) ?? contextualMenu.hoverSelectedMenuItemBackgroundColor; - contextualMenu.activeSelectedMenuItemBackgroundColor = - subLightness(5, theme?.contextualMenu?.accentColor) ?? contextualMenu.activeSelectedMenuItemBackgroundColor; - contextualMenu.backgroundColor = theme?.contextualMenu?.baseColor ?? contextualMenu.backgroundColor; - contextualMenu.hoverMenuItemBackgroundColor = - subLightness(5, theme?.contextualMenu?.baseColor) ?? contextualMenu.hoverMenuItemBackgroundColor; - contextualMenu.activeMenuItemBackgroundColor = - subLightness(5, theme?.contextualMenu?.baseColor) ?? contextualMenu.activeMenuItemBackgroundColor; - contextualMenu.menuItemFontColor = theme?.contextualMenu?.fontColor ?? contextualMenu.menuItemFontColor; - contextualMenu.sectionTitleFontColor = theme?.contextualMenu?.fontColor ?? contextualMenu.sectionTitleFontColor; - contextualMenu.iconColor = theme?.contextualMenu?.iconColor ?? contextualMenu.iconColor; + const contextualMenuTokens = componentTokensCopy.contextualMenu; + contextualMenuTokens.selectedMenuItemBackgroundColor = + theme?.contextualMenu?.accentColor ?? contextualMenuTokens.selectedMenuItemBackgroundColor; + contextualMenuTokens.hoverSelectedMenuItemBackgroundColor = + subLightness(5, theme?.contextualMenu?.accentColor) ?? contextualMenuTokens.hoverSelectedMenuItemBackgroundColor; + contextualMenuTokens.activeSelectedMenuItemBackgroundColor = + subLightness(5, theme?.contextualMenu?.accentColor) ?? contextualMenuTokens.activeSelectedMenuItemBackgroundColor; + contextualMenuTokens.backgroundColor = theme?.contextualMenu?.baseColor ?? contextualMenuTokens.backgroundColor; + contextualMenuTokens.hoverMenuItemBackgroundColor = + subLightness(5, theme?.contextualMenu?.baseColor) ?? contextualMenuTokens.hoverMenuItemBackgroundColor; + contextualMenuTokens.activeMenuItemBackgroundColor = + subLightness(5, theme?.contextualMenu?.baseColor) ?? contextualMenuTokens.activeMenuItemBackgroundColor; + contextualMenuTokens.menuItemFontColor = theme?.contextualMenu?.fontColor ?? contextualMenuTokens.menuItemFontColor; + contextualMenuTokens.sectionTitleFontColor = + theme?.contextualMenu?.fontColor ?? contextualMenuTokens.sectionTitleFontColor; + contextualMenuTokens.iconColor = theme?.contextualMenu?.iconColor ?? contextualMenuTokens.iconColor; const dataGridTokens = componentTokensCopy.dataGrid; dataGridTokens.headerBackgroundColor = theme?.dataGrid?.baseColor ?? dataGridTokens.headerBackgroundColor; From 3e120af7eee0b0f3e22b09e2d12563c9446a3cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:59:57 +0100 Subject: [PATCH 10/41] Contextual Menu updates --- .../components/alert/code/AlertCodePage.tsx | 2 +- .../src/contextual-menu/ContextualMenu.tsx | 111 ++++++++---------- packages/lib/src/contextual-menu/types.ts | 4 +- 3 files changed, 50 insertions(+), 67 deletions(-) diff --git a/apps/website/screens/components/alert/code/AlertCodePage.tsx b/apps/website/screens/components/alert/code/AlertCodePage.tsx index 93f5187ef0..4fcb17c975 100644 --- a/apps/website/screens/components/alert/code/AlertCodePage.tsx +++ b/apps/website/screens/components/alert/code/AlertCodePage.tsx @@ -47,7 +47,7 @@ const sections = [ message (e.g. closing the modal alert). - false + true diff --git a/packages/lib/src/contextual-menu/ContextualMenu.tsx b/packages/lib/src/contextual-menu/ContextualMenu.tsx index b8cfe580b5..73e7256c78 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.tsx @@ -15,34 +15,56 @@ import ContextualMenuPropsType, { } from "./types"; import Section from "./Section"; +const ContextualMenu = styled.div` + box-sizing: border-box; + margin: 0; + border: 1px solid ${({ theme }) => theme.borderColor}; + border-radius: 0.25rem; + padding: ${CoreTokens.spacing_16} ${CoreTokens.spacing_8}; + display: grid; + gap: ${CoreTokens.spacing_4}; + min-width: 248px; + max-height: 100%; + background-color: ${({ theme }) => theme.backgroundColor}; + overflow-y: auto; + overflow-x: hidden; + &::-webkit-scrollbar { + width: 8px; + height: 8px; + } + &::-webkit-scrollbar-thumb { + background-color: ${CoreTokens.color_grey_700}; + border-radius: 0.25rem; + } + &::-webkit-scrollbar-track { + background-color: ${CoreTokens.color_grey_300}; + border-radius: 0.25rem; + } +`; + +const StyledSubMenu = styled.ul` + margin: 0; + padding: 0; + display: grid; + gap: ${CoreTokens.spacing_4}; + list-style: none; +`; + const isGroupItem = (item: Item | GroupItem): item is GroupItem => "items" in item; const isSection = (item: SectionType | Item | GroupItem): item is SectionType => "items" in item && !("label" in item); - const addIdToItems = (items: ContextualMenuPropsType["items"]): (ItemWithId | GroupItemWithId | SectionWithId)[] => { let accId = 0; - const innerAddIdToItems = ( - innerItems: ContextualMenuPropsType["items"] - ): (ItemWithId | GroupItemWithId | SectionWithId)[] => - innerItems.map((item: Item | GroupItem | SectionType) => { - let newItem; - if (isSection(item)) { - newItem = { - ...item, - id: (accId += 1), - items: innerAddIdToItems(item.items), - } as SectionWithId; - } else if (isGroupItem(item)) { - newItem = { - ...item, - id: (accId += 1), - items: innerAddIdToItems(item.items), - } as GroupItemWithId; - } else { - newItem = { ...item, id: (accId += 1) } as ItemWithId; - } - return newItem; - }); + items: ContextualMenuPropsType["items"] + ): (ItemWithId | GroupItemWithId | SectionWithId)[] => { + return 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); }; @@ -54,7 +76,7 @@ export const SubMenu = ({ children, id }: SubMenuProps) => ( export const ContextualMenuContext = createContext(null); -const DxcContextualMenu = ({ items }: ContextualMenuPropsType) => { +export default function DxcContextualMenu({ items }: ContextualMenuPropsType) { const [selectedItemId, setSelectedItemId] = useState(-1); const contextualMenuRef = useRef(null); const itemsWithId = useMemo(() => addIdToItems(items), [items]); @@ -77,7 +99,7 @@ const DxcContextualMenu = ({ items }: ContextualMenuPropsType) => { - {isSection(itemsWithId[0] as SectionWithId) ? ( + {itemsWithId[0] && isSection(itemsWithId[0]) ? ( (itemsWithId as SectionWithId[]).map((item, index) => (
    )) @@ -92,41 +114,4 @@ const DxcContextualMenu = ({ items }: ContextualMenuPropsType) => { ); -}; - -const ContextualMenu = styled.div` - box-sizing: border-box; - margin: 0; - border: 1px solid ${({ theme }) => theme.borderColor}; - border-radius: 0.25rem; - padding: ${CoreTokens.spacing_16} ${CoreTokens.spacing_8}; - display: grid; - gap: ${CoreTokens.spacing_4}; - min-width: 248px; - max-height: 100%; - background-color: ${({ theme }) => theme.backgroundColor}; - overflow-y: auto; - overflow-x: hidden; - &::-webkit-scrollbar { - width: 8px; - height: 8px; - } - &::-webkit-scrollbar-thumb { - background-color: ${CoreTokens.color_grey_700}; - border-radius: 0.25rem; - } - &::-webkit-scrollbar-track { - background-color: ${CoreTokens.color_grey_300}; - border-radius: 0.25rem; - } -`; - -const StyledSubMenu = styled.ul` - margin: 0; - padding: 0; - display: grid; - gap: ${CoreTokens.spacing_4}; - list-style: none; -`; - -export default DxcContextualMenu; +} diff --git a/packages/lib/src/contextual-menu/types.ts b/packages/lib/src/contextual-menu/types.ts index 58a16745f6..f9846b53bd 100644 --- a/packages/lib/src/contextual-menu/types.ts +++ b/packages/lib/src/contextual-menu/types.ts @@ -21,9 +21,6 @@ type Props = { items: (Item | GroupItem)[] | Section[]; }; -/** - * Contextual menu internal types. - */ type ItemWithId = Item & { id: number }; type GroupItemWithId = { badge?: React.ReactElement; @@ -32,6 +29,7 @@ type GroupItemWithId = { label: string; }; type SectionWithId = { items: (ItemWithId | GroupItemWithId)[]; title?: string }; + type SingleItemProps = ItemWithId & { depthLevel: number }; type GroupItemProps = GroupItemWithId & { depthLevel: number }; type MenuItemProps = { item: ItemWithId | GroupItemWithId; depthLevel?: number }; From 87823c30aa54831eaaeab9f9aea327ee69539958 Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Mon, 16 Dec 2024 08:13:11 +0100 Subject: [PATCH 11/41] Improved getNavigationLinks implementation --- apps/website/screens/common/pagesList.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/website/screens/common/pagesList.ts b/apps/website/screens/common/pagesList.ts index 034510a390..546dad4589 100644 --- a/apps/website/screens/common/pagesList.ts +++ b/apps/website/screens/common/pagesList.ts @@ -14,8 +14,8 @@ export type LinksSectionDetails = { }; type NavigationLinks = { - previousLink: LinkDetails | null; - nextLink: LinkDetails | null; + previousLink?: LinkDetails; + nextLink?: LinkDetails; }; export const themeGeneratorLinks = ["/theme-generator/opinionated-theme/", "/theme-generator/advanced-theme/"]; @@ -69,14 +69,10 @@ export const getNavigationLinks = (currentPath: string): NavigationLinks => { const links = LinksSections.flatMap((section) => section.links); const currentLinkIndex = getCurrentLinkIndex(links, currentPath); if (currentLinkIndex === -1) { - return { previousLink: null, nextLink: null }; + return {}; } - const nextLinkIndex = currentLinkIndex + 1; - const nextLinkExists = nextLinkIndex < links.length; - const previousLinkIndex = currentLinkIndex - 1; - const previousLinkExists = previousLinkIndex >= 0; return { - previousLink: previousLinkExists ? (links[previousLinkIndex] ?? null) : null, - nextLink: nextLinkExists ? (links[nextLinkIndex] ?? null) : null, + previousLink: currentLinkIndex + 1 < links.length ? links[currentLinkIndex + 1] : undefined, + nextLink: currentLinkIndex - 1 >= 0 ? links[currentLinkIndex - 1] : undefined, }; }; From eb472683cbfc78a74ee8dce3a5c074b8223b613c Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Mon, 16 Dec 2024 14:43:32 +0100 Subject: [PATCH 12/41] Removed unneeded React imports --- apps/website/screens/common/example/Example.tsx | 2 +- apps/website/screens/theme-generator/ImportDialog.tsx | 2 +- apps/website/screens/theme-generator/ThemeGenerator.tsx | 2 +- .../screens/theme-generator/components/previews/Accordion.tsx | 2 +- .../screens/theme-generator/components/previews/Alert.tsx | 2 +- .../screens/theme-generator/components/previews/Checkbox.tsx | 2 +- .../screens/theme-generator/components/previews/DataGrid.tsx | 2 +- .../screens/theme-generator/components/previews/Dialog.tsx | 2 +- .../screens/theme-generator/components/previews/FileInput.tsx | 2 +- .../screens/theme-generator/components/previews/Paginator.tsx | 2 +- .../screens/theme-generator/components/previews/ProgressBar.tsx | 2 +- .../screens/theme-generator/components/previews/Slider.tsx | 2 +- .../screens/theme-generator/components/previews/Spinner.tsx | 2 +- .../screens/theme-generator/components/previews/Switch.tsx | 2 +- .../screens/theme-generator/components/previews/Table.tsx | 2 +- .../screens/theme-generator/components/previews/Tabs.tsx | 2 +- .../theme-generator/components/widgets/AlphaValueInput.tsx | 2 +- .../theme-generator/components/widgets/BorderWidthInput.tsx | 2 +- .../screens/theme-generator/components/widgets/ColorPicker.tsx | 2 +- .../screens/theme-generator/components/widgets/FontFamily.tsx | 2 +- .../screens/theme-generator/components/widgets/ImageConfig.tsx | 2 +- .../screens/theme-generator/components/widgets/IntegerInput.tsx | 2 +- .../screens/theme-generator/components/widgets/LengthInput.tsx | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/website/screens/common/example/Example.tsx b/apps/website/screens/common/example/Example.tsx index f3b6eb3244..f03270bd2a 100644 --- a/apps/website/screens/common/example/Example.tsx +++ b/apps/website/screens/common/example/Example.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import styled from "styled-components"; import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live"; import theme from "./liveEditorTheme"; diff --git a/apps/website/screens/theme-generator/ImportDialog.tsx b/apps/website/screens/theme-generator/ImportDialog.tsx index 86ad0cc1cd..4ba79f3963 100644 --- a/apps/website/screens/theme-generator/ImportDialog.tsx +++ b/apps/website/screens/theme-generator/ImportDialog.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcButton, DxcDialog, DxcTextarea, DxcHeading, DxcFlex } from "@dxc-technology/halstack-react"; import { deepMerge } from "./utils"; import ImportDialogProps, { IndexedThemeInput, IndexedTheme } from "./types"; diff --git a/apps/website/screens/theme-generator/ThemeGenerator.tsx b/apps/website/screens/theme-generator/ThemeGenerator.tsx index 70701cd2cf..e182c1eaa5 100644 --- a/apps/website/screens/theme-generator/ThemeGenerator.tsx +++ b/apps/website/screens/theme-generator/ThemeGenerator.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from "react"; +import { useState, useCallback } from "react"; import { DxcApplicationLayout, DxcButton, diff --git a/apps/website/screens/theme-generator/components/previews/Accordion.tsx b/apps/website/screens/theme-generator/components/previews/Accordion.tsx index 1fb8b20df5..d66d254050 100644 --- a/apps/website/screens/theme-generator/components/previews/Accordion.tsx +++ b/apps/website/screens/theme-generator/components/previews/Accordion.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcAccordion, DxcAccordionGroup, DxcInset } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import facebookIcon from "../../images/FacebookIcon"; diff --git a/apps/website/screens/theme-generator/components/previews/Alert.tsx b/apps/website/screens/theme-generator/components/previews/Alert.tsx index 5d2b860083..35d3292b6d 100644 --- a/apps/website/screens/theme-generator/components/previews/Alert.tsx +++ b/apps/website/screens/theme-generator/components/previews/Alert.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcAlert, DxcButton } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; diff --git a/apps/website/screens/theme-generator/components/previews/Checkbox.tsx b/apps/website/screens/theme-generator/components/previews/Checkbox.tsx index f5331b425e..8b82a5f802 100644 --- a/apps/website/screens/theme-generator/components/previews/Checkbox.tsx +++ b/apps/website/screens/theme-generator/components/previews/Checkbox.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcCheckbox } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; diff --git a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx index 5594833f0c..3ef5ac31cf 100644 --- a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx +++ b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcContainer, DxcDataGrid } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; diff --git a/apps/website/screens/theme-generator/components/previews/Dialog.tsx b/apps/website/screens/theme-generator/components/previews/Dialog.tsx index 46702e8c8e..ff3bf5929e 100644 --- a/apps/website/screens/theme-generator/components/previews/Dialog.tsx +++ b/apps/website/screens/theme-generator/components/previews/Dialog.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcDialog, DxcButton, diff --git a/apps/website/screens/theme-generator/components/previews/FileInput.tsx b/apps/website/screens/theme-generator/components/previews/FileInput.tsx index 181804e106..0520480f42 100644 --- a/apps/website/screens/theme-generator/components/previews/FileInput.tsx +++ b/apps/website/screens/theme-generator/components/previews/FileInput.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcFileInput } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; diff --git a/apps/website/screens/theme-generator/components/previews/Paginator.tsx b/apps/website/screens/theme-generator/components/previews/Paginator.tsx index 6d24805bf3..3548cb10db 100644 --- a/apps/website/screens/theme-generator/components/previews/Paginator.tsx +++ b/apps/website/screens/theme-generator/components/previews/Paginator.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcPaginator } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; diff --git a/apps/website/screens/theme-generator/components/previews/ProgressBar.tsx b/apps/website/screens/theme-generator/components/previews/ProgressBar.tsx index b9064476d5..e3a842f41d 100644 --- a/apps/website/screens/theme-generator/components/previews/ProgressBar.tsx +++ b/apps/website/screens/theme-generator/components/previews/ProgressBar.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import styled from "styled-components"; import { DxcButton, DxcProgressBar } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; diff --git a/apps/website/screens/theme-generator/components/previews/Slider.tsx b/apps/website/screens/theme-generator/components/previews/Slider.tsx index 02c04d4a77..3865e31045 100644 --- a/apps/website/screens/theme-generator/components/previews/Slider.tsx +++ b/apps/website/screens/theme-generator/components/previews/Slider.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcSlider } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; diff --git a/apps/website/screens/theme-generator/components/previews/Spinner.tsx b/apps/website/screens/theme-generator/components/previews/Spinner.tsx index b070bb0e12..b012139e13 100644 --- a/apps/website/screens/theme-generator/components/previews/Spinner.tsx +++ b/apps/website/screens/theme-generator/components/previews/Spinner.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcSpinner, DxcButton } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; diff --git a/apps/website/screens/theme-generator/components/previews/Switch.tsx b/apps/website/screens/theme-generator/components/previews/Switch.tsx index 22e9f64e35..38912747b8 100644 --- a/apps/website/screens/theme-generator/components/previews/Switch.tsx +++ b/apps/website/screens/theme-generator/components/previews/Switch.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { DxcSwitch } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; diff --git a/apps/website/screens/theme-generator/components/previews/Table.tsx b/apps/website/screens/theme-generator/components/previews/Table.tsx index cbc9ad670a..b38e5d491d 100644 --- a/apps/website/screens/theme-generator/components/previews/Table.tsx +++ b/apps/website/screens/theme-generator/components/previews/Table.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import styled from "styled-components"; import { DxcTable, DxcResultsetTable } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; diff --git a/apps/website/screens/theme-generator/components/previews/Tabs.tsx b/apps/website/screens/theme-generator/components/previews/Tabs.tsx index c97ae55fad..bad1db1ca2 100644 --- a/apps/website/screens/theme-generator/components/previews/Tabs.tsx +++ b/apps/website/screens/theme-generator/components/previews/Tabs.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import styled from "styled-components"; import { DxcFlex, DxcTabs } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; diff --git a/apps/website/screens/theme-generator/components/widgets/AlphaValueInput.tsx b/apps/website/screens/theme-generator/components/widgets/AlphaValueInput.tsx index 8e6f810c32..16fdeb4b86 100644 --- a/apps/website/screens/theme-generator/components/widgets/AlphaValueInput.tsx +++ b/apps/website/screens/theme-generator/components/widgets/AlphaValueInput.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import StyledInput from "./common/StyledInput"; import ThemeInputWidgetProps from "./common/types"; diff --git a/apps/website/screens/theme-generator/components/widgets/BorderWidthInput.tsx b/apps/website/screens/theme-generator/components/widgets/BorderWidthInput.tsx index 3b25688e26..8f985ff207 100644 --- a/apps/website/screens/theme-generator/components/widgets/BorderWidthInput.tsx +++ b/apps/website/screens/theme-generator/components/widgets/BorderWidthInput.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import styled from "styled-components"; import StyledInput from "./common/StyledInput"; import ThemeInputWidgetProps from "./common/types"; diff --git a/apps/website/screens/theme-generator/components/widgets/ColorPicker.tsx b/apps/website/screens/theme-generator/components/widgets/ColorPicker.tsx index 7c6377d123..03e30e8a6d 100644 --- a/apps/website/screens/theme-generator/components/widgets/ColorPicker.tsx +++ b/apps/website/screens/theme-generator/components/widgets/ColorPicker.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import styled from "styled-components"; import { SketchPicker } from "react-color"; import ThemeInputWidgetProps from "./common/types"; diff --git a/apps/website/screens/theme-generator/components/widgets/FontFamily.tsx b/apps/website/screens/theme-generator/components/widgets/FontFamily.tsx index 8e0e22fffc..bfe81d91dd 100644 --- a/apps/website/screens/theme-generator/components/widgets/FontFamily.tsx +++ b/apps/website/screens/theme-generator/components/widgets/FontFamily.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import StyledInput from "./common/StyledInput"; import StyledSelect from "./common/StyledSelect"; import ThemeInputWidgetProps from "./common/types"; diff --git a/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx b/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx index d61e9851de..81912a765a 100644 --- a/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx +++ b/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx @@ -1,5 +1,5 @@ import { DxcFlex } from "@dxc-technology/halstack-react"; -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import styled from "styled-components"; import ThemeInputWidgetProps from "./common/types"; diff --git a/apps/website/screens/theme-generator/components/widgets/IntegerInput.tsx b/apps/website/screens/theme-generator/components/widgets/IntegerInput.tsx index c76ea37e9d..8076a03237 100644 --- a/apps/website/screens/theme-generator/components/widgets/IntegerInput.tsx +++ b/apps/website/screens/theme-generator/components/widgets/IntegerInput.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import StyledInput from "./common/StyledInput"; import ThemeInputWidgetProps from "./common/types"; diff --git a/apps/website/screens/theme-generator/components/widgets/LengthInput.tsx b/apps/website/screens/theme-generator/components/widgets/LengthInput.tsx index 72d36ce4fd..87c8d368d5 100644 --- a/apps/website/screens/theme-generator/components/widgets/LengthInput.tsx +++ b/apps/website/screens/theme-generator/components/widgets/LengthInput.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import StyledInput from "./common/StyledInput"; import StyledSelect from "./common/StyledSelect"; import ThemeInputWidgetProps from "./common/types"; From 5361bb2804ec8c1c64586ae83c17717fe4e7035b Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Tue, 17 Dec 2024 10:53:50 +0100 Subject: [PATCH 13/41] Added fixes for PR review --- packages/lib/src/checkbox/Checkbox.tsx | 20 ++++--- packages/lib/src/container/Container.tsx | 11 ++-- .../lib/src/contextual-menu/GroupItem.tsx | 14 +++-- .../lib/src/contextual-menu/SingleItem.tsx | 4 +- packages/lib/src/date-input/Calendar.tsx | 14 +++-- packages/lib/src/date-input/DateInput.tsx | 8 +-- packages/lib/src/date-input/types.ts | 2 + packages/lib/src/file-input/FileInput.tsx | 37 ++++++------- packages/lib/src/header/Header.tsx | 52 ++++++++++--------- packages/lib/src/image/Image.tsx | 17 +++--- packages/lib/src/nav-tabs/NavTabs.tsx | 13 ++--- packages/lib/src/nav-tabs/NavTabsContext.tsx | 4 +- packages/lib/src/nav-tabs/Tab.tsx | 1 - .../src/number-input/NumberInputContext.tsx | 4 +- packages/lib/src/select/Listbox.tsx | 39 +++++++------- packages/lib/src/select/selectUtils.ts | 12 ++--- packages/lib/src/tabs/TabsContext.tsx | 4 +- 17 files changed, 125 insertions(+), 131 deletions(-) diff --git a/packages/lib/src/checkbox/Checkbox.tsx b/packages/lib/src/checkbox/Checkbox.tsx index 6de35f6eb5..5bb8aff14f 100644 --- a/packages/lib/src/checkbox/Checkbox.tsx +++ b/packages/lib/src/checkbox/Checkbox.tsx @@ -270,14 +270,18 @@ const MainContainer = styled.div<{ &:hover ${Checkbox} { border: 2px solid - ${(props) => - !props.disabled && - (props.readOnly ? getReadOnlyColor(props.theme, "hoverBorder") : getEnabledColor(props.theme, "hoverBorder"))}; - color: ${(props) => - !props.disabled && - (props.readOnly - ? getReadOnlyColor(props.theme, "hoverBackground") - : getEnabledColor(props.theme, "hoverBackground"))}; + ${(props) => { + if (!props.disabled) + return props.readOnly + ? getReadOnlyColor(props.theme, "hoverBorder") + : getEnabledColor(props.theme, "hoverBorder"); + }}; + color: ${(props) => { + if (!props.disabled) + return props.readOnly + ? getReadOnlyColor(props.theme, "hoverBackground") + : getEnabledColor(props.theme, "hoverBackground"); + }}; } `; diff --git a/packages/lib/src/container/Container.tsx b/packages/lib/src/container/Container.tsx index 9b66705e5f..47ae248b10 100644 --- a/packages/lib/src/container/Container.tsx +++ b/packages/lib/src/container/Container.tsx @@ -4,9 +4,9 @@ import ContainerPropsType, { BorderProperties, StyledProps } from "./types"; import { spaces } from "../common/variables"; const getBorderStyles = (direction: "top" | "bottom" | "left" | "right", borderProperties: BorderProperties) => - `border-${direction}-width: ${borderProperties?.width ?? ""}; - border-${direction}-style: ${borderProperties?.style ?? ""}; - border-${direction}-color: ${borderProperties?.color ? getCoreColorToken(borderProperties?.color) : ""};`; + `border-${direction}: ${borderProperties?.width ?? ""} ${borderProperties?.style ?? ""} ${ + borderProperties?.color ? (getCoreColorToken(borderProperties?.color) ?? "") : "" + };`; const Container = styled.div` box-sizing: ${({ boxSizing }) => boxSizing}; @@ -67,9 +67,8 @@ const Container = styled.div` margin-bottom: ${({ margin }) => (typeof margin === "object" && margin.bottom ? spaces[margin.bottom] : "")}; margin-left: ${({ margin }) => (typeof margin === "object" && margin.left ? spaces[margin.left] : "")}; - outline-width: ${({ outline }) => `${outline?.width ?? ""}`}; - outline-style: ${({ outline }) => `${outline?.style ?? ""}`}; - outline-color: ${({ outline }) => (outline?.color ? `${getCoreColorToken(outline?.color)}` : "")}; + outline: ${({ outline }) => + `${outline?.width ?? ""} ${outline?.style ?? ""} ${outline?.color ? (getCoreColorToken(outline?.color) ?? "") : ""}`}; outline-offset: ${({ outline }) => outline?.offset}; overflow: ${({ $overflow }) => (typeof $overflow === "string" ? $overflow : "")}; diff --git a/packages/lib/src/contextual-menu/GroupItem.tsx b/packages/lib/src/contextual-menu/GroupItem.tsx index 9d1c7e4e4a..7220b3f8c6 100644 --- a/packages/lib/src/contextual-menu/GroupItem.tsx +++ b/packages/lib/src/contextual-menu/GroupItem.tsx @@ -5,14 +5,12 @@ import ItemAction from "./ItemAction"; import MenuItem from "./MenuItem"; import { GroupItemProps, ItemWithId } from "./types"; -const isGroupSelected = (items: GroupItemProps["items"], selectedItemId: number | undefined): boolean => - items.some((item) => - "items" in item - ? isGroupSelected(item.items, selectedItemId) - : selectedItemId !== -1 - ? item.id === selectedItemId - : (item as ItemWithId).selectedByDefault - ); +const isGroupSelected = (items: GroupItemProps["items"], selectedItemId?: number): boolean => + 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; + }); const GroupItem = ({ items, ...props }: GroupItemProps) => { const groupMenuId = `group-menu-${useId()}`; diff --git a/packages/lib/src/contextual-menu/SingleItem.tsx b/packages/lib/src/contextual-menu/SingleItem.tsx index 10900a0971..54cb247e73 100644 --- a/packages/lib/src/contextual-menu/SingleItem.tsx +++ b/packages/lib/src/contextual-menu/SingleItem.tsx @@ -21,9 +21,7 @@ const SingleItem = ({ id, onSelect, selectedByDefault, ...props }: SingleItemPro ); diff --git a/packages/lib/src/date-input/Calendar.tsx b/packages/lib/src/date-input/Calendar.tsx index db620d80a5..55dc04e737 100644 --- a/packages/lib/src/date-input/Calendar.tsx +++ b/packages/lib/src/date-input/Calendar.tsx @@ -1,13 +1,11 @@ import { Dayjs } from "dayjs"; import { useState, useMemo, useEffect, useId, memo, KeyboardEvent, FocusEvent } from "react"; import styled from "styled-components"; -import { CalendarPropsType } from "./types"; +import { CalendarPropsType, DateType } from "./types"; import useTranslatedLabels from "../useTranslatedLabels"; -type Date = { day: number; month: number; year: number }; - const getDays = (innerDate: Dayjs) => { - const monthDayCells: Date[] = []; + const monthDayCells: DateType[] = []; const lastMonthNumberOfDays = innerDate.set("month", innerDate.get("month") - 1).endOf("month"); const firstDayOfMonth = innerDate.startOf("month").day() === 0 ? 6 : innerDate.startOf("month").day() - 1; const daysInMonth = firstDayOfMonth + innerDate.daysInMonth(); @@ -43,12 +41,12 @@ const getDateToFocus = (selectedDate: Dayjs, innerDate: Dayjs, today: Dayjs) => ? today : innerDate.set("date", 1); -const isDaySelected = (date: Date, selectedDate: Dayjs) => +const isDaySelected = (date: DateType, selectedDate: Dayjs) => selectedDate?.get("month") === date.month && selectedDate?.get("year") === date.year && selectedDate?.get("date") === date.day; -const divideDaysIntoWeeks = (data: Date[], weekSize: number) => +const divideDaysIntoWeeks = (data: DateType[], weekSize: number) => Array.from({ length: Math.ceil(data.length / weekSize) }, (_, rowIndex) => data.slice(rowIndex * weekSize, (rowIndex + 1) * weekSize) ); @@ -67,7 +65,7 @@ const Calendar = ({ const translatedLabels = useTranslatedLabels(); const weekDays = translatedLabels?.calendar?.daysShort ?? []; - const onDateClickHandler = (date: Date) => { + const onDateClickHandler = (date: DateType) => { const newDate = innerDate.set("month", date.month).set("date", date.day); onDaySelect(newDate); setDateToFocus(newDate); @@ -100,7 +98,7 @@ const Calendar = ({ } }, [innerDate, dateToFocus, selectedDate, today]); - const handleDayKeyboardEvent = (event: KeyboardEvent, date: Date) => { + const handleDayKeyboardEvent = (event: KeyboardEvent, date: DateType) => { let dateToFocusTemp = date.month === innerDate.get("month") ? innerDate.set("date", date.day) diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index 9b03f6ded6..7a69d59b66 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -31,8 +31,8 @@ const getValueForPicker = (value: string, format: string) => dayjs(value, format const getDate = ( value: string, format: string, - lastValidYear: number | undefined, - setLastValidYear: Dispatch> + lastValidYear: number | null, + setLastValidYear: Dispatch> ) => { if ((value || value === "") && format.toUpperCase().includes("YYYY")) { return getValueForPicker(value, format); @@ -80,12 +80,12 @@ const DxcDateInput = forwardRef( const [isOpen, setIsOpen] = useState(false); const calendarId = `date-picker-${useId()}`; const [dayjsDate, setDayjsDate] = useState(getValueForPicker(value ?? defaultValue ?? "", format)); - const [lastValidYear, setLastValidYear] = useState( + const [lastValidYear, setLastValidYear] = useState( innerValue || value ? !format.toUpperCase().includes("YYYY") && +getValueForPicker(value ?? innerValue, format).format("YY") < 68 ? 2000 : 1900 - : undefined + : null ); const [sideOffset, setSideOffset] = useState(SIDEOFFSET); const colorsTheme = useTheme(); diff --git a/packages/lib/src/date-input/types.ts b/packages/lib/src/date-input/types.ts index 8b4edf93a1..b7203b20b0 100644 --- a/packages/lib/src/date-input/types.ts +++ b/packages/lib/src/date-input/types.ts @@ -95,6 +95,8 @@ type Props = { tabIndex?: number; }; +export type DateType = { day: number; month: number; year: number }; + export type DatePickerPropsType = { /** * Initial selected date value. If invalid the actual date will be used instead. diff --git a/packages/lib/src/file-input/FileInput.tsx b/packages/lib/src/file-input/FileInput.tsx index 1330b2888d..ca6ac3e04a 100644 --- a/packages/lib/src/file-input/FileInput.tsx +++ b/packages/lib/src/file-input/FileInput.tsx @@ -7,20 +7,19 @@ import useTranslatedLabels from "../useTranslatedLabels"; import FileItem from "./FileItem"; import FileInputPropsType, { FileData, RefType } from "./types"; -const getFilePreview = async (file: File): Promise => - file?.type?.includes("video") - ? "filled_movie" - : file?.type?.includes("audio") - ? "music_video" - : file?.type?.includes("image") - ? new Promise((resolve) => { - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.onload = (e) => { - resolve(e?.target?.result as string); - }; - }) - : "draft"; +const getFilePreview = async (file: File): Promise => { + if (file?.type?.includes("video")) return "filled_movie"; + else if (file?.type?.includes("audio")) return "music_video"; + else if (file?.type?.includes("image")) { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = (e) => { + resolve(e?.target?.result as string); + }; + }); + } else return "draft"; +}; const isFileIncluded = (file: FileData, fileList: FileData[]) => { const fileListInfo = fileList.map((existingFile) => existingFile.file); @@ -61,12 +60,10 @@ const DxcFileInput = forwardRef( const colorsTheme = useTheme(); const translatedLabels = useTranslatedLabels(); - const checkFileSize = (file: File) => - minSize && file.size < minSize - ? translatedLabels?.fileInput?.fileSizeGreaterThanErrorMessage - : maxSize && file.size > maxSize - ? translatedLabels?.fileInput?.fileSizeLessThanErrorMessage - : undefined; + const checkFileSize = (file: File) => { + if (minSize && file.size < minSize) return translatedLabels?.fileInput?.fileSizeGreaterThanErrorMessage; + else if (maxSize && file.size > maxSize) return translatedLabels?.fileInput?.fileSizeLessThanErrorMessage; + }; const getFilesToAdd = async (selectedFiles: File[]) => { const filesToAdd = await Promise.all(selectedFiles.map((selectedFile) => getFilePreview(selectedFile))).then( diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx index d7eecab705..07ad2274be 100644 --- a/packages/lib/src/header/Header.tsx +++ b/packages/lib/src/header/Header.tsx @@ -15,30 +15,30 @@ const Dropdown = (props: ComponentProps) => ( ); -const getLogoElement = (themeInput: string | undefined, logoLabel: string | undefined) => - !themeInput ? ( - - DXC Logo - - - - +const getLogoElement = (themeInput?: string, logoLabel?: string) => { + if (!themeInput) { + return ( + + DXC Logo + + + + + - - - ) : typeof themeInput === "string" ? ( - - ) : ( - themeInput - ); + + ); + } else if (typeof themeInput === "string") return ; + else return themeInput; +}; type ContentProps = { isResponsive: boolean; @@ -123,7 +123,11 @@ const DxcHeader = ({ {headerResponsiveLogo} - + diff --git a/packages/lib/src/image/Image.tsx b/packages/lib/src/image/Image.tsx index 543afa46be..2d7c506ff6 100644 --- a/packages/lib/src/image/Image.tsx +++ b/packages/lib/src/image/Image.tsx @@ -1,5 +1,5 @@ import styled, { ThemeProvider } from "styled-components"; -import { ReactNode } from "react"; +import { ReactNode, useCallback } from "react"; import useTheme from "../useTheme"; import ImagePropsType, { CaptionWrapperProps } from "./types"; @@ -41,16 +41,19 @@ export default function DxcImage({ }: ImagePropsType) { const colorsTheme = useTheme(); - const wrapperFunction = (children: ReactNode) => ( -
    - {children} - {caption} -
    + const figureWrapper = useCallback( + (children: ReactNode) => ( +
    + {children} + {caption} +
    + ), + [caption] ); return ( - + {alt} { const childWithProps = child as ReactElement; if (childWithProps.props[propName]) { return childWithProps.props[propName]; - } - if (childWithProps.props.children) { + } else if (childWithProps.props.children) { return getPropInChild(childWithProps.props.children, propName); } } - return undefined; }; -const getLabelFromTab = (child: ReactNode): string | undefined => { +const getLabelFromTab = (child: ReactNode) => { if (typeof child === "string") { return child; - } - if (child && typeof child === "object" && "props" in child) { + } else if (child && typeof child === "object" && "props" in child) { const childWithProps = child as ReactElement; if (Array.isArray(childWithProps.props.children)) { return getLabelFromTab(childWithProps.props.children[0]); + } else { + return getLabelFromTab(childWithProps.props.children); } - return getLabelFromTab(childWithProps.props.children); } - return undefined; }; const getPreviousTabIndex = (array: ReactElement[], initialIndex: number): number => { diff --git a/packages/lib/src/nav-tabs/NavTabsContext.tsx b/packages/lib/src/nav-tabs/NavTabsContext.tsx index 16b6e2a063..b78ed9f69a 100644 --- a/packages/lib/src/nav-tabs/NavTabsContext.tsx +++ b/packages/lib/src/nav-tabs/NavTabsContext.tsx @@ -1,6 +1,4 @@ import { createContext } from "react"; import { NavTabsContextProps } from "./types"; -const NavTabsContext = createContext(null); - -export default NavTabsContext; +export default createContext(null); diff --git a/packages/lib/src/nav-tabs/Tab.tsx b/packages/lib/src/nav-tabs/Tab.tsx index 7c305c2ba2..0f108628a9 100644 --- a/packages/lib/src/nav-tabs/Tab.tsx +++ b/packages/lib/src/nav-tabs/Tab.tsx @@ -13,7 +13,6 @@ const DxcTab = forwardRef( ref: Ref ): JSX.Element => { const tabRef = useRef(); - const colorsTheme = useTheme(); const { iconPosition, tabIndex, focusedLabel } = useContext(NavTabsContext) ?? {}; const innerRef = useRef(null); useImperativeHandle(ref, () => innerRef.current!, []); diff --git a/packages/lib/src/number-input/NumberInputContext.tsx b/packages/lib/src/number-input/NumberInputContext.tsx index 913d8a12af..c5eaa53bc3 100644 --- a/packages/lib/src/number-input/NumberInputContext.tsx +++ b/packages/lib/src/number-input/NumberInputContext.tsx @@ -1,6 +1,4 @@ import { createContext } from "react"; import { NumberInputContextProps } from "./types"; -const NumberInputContext = createContext(null); - -export default NumberInputContext; +export default createContext(null); diff --git a/packages/lib/src/select/Listbox.tsx b/packages/lib/src/select/Listbox.tsx index 4475ca5412..49eb26d350 100644 --- a/packages/lib/src/select/Listbox.tsx +++ b/packages/lib/src/select/Listbox.tsx @@ -34,7 +34,7 @@ const Listbox = ({ {option.label} {option.options.map((singleOption) => { - globalIndex += 1; + globalIndex++; return ( ) ); + } else { + globalIndex++; + return ( + + ); } - globalIndex += 1; - return ( - - ); }; useLayoutEffect(() => { @@ -80,7 +81,7 @@ const Listbox = ({ const listEl = listboxRef?.current; const selectedListOptionEl = listEl?.querySelector("[aria-selected='true']") as HTMLUListElement; listEl?.scrollTo?.({ - top: (selectedListOptionEl?.offsetTop || 0) - (listEl?.clientHeight || 0) / 2, + top: (selectedListOptionEl?.offsetTop ?? 0) - (listEl?.clientHeight ?? 0) / 2, }); } }, [currentValue, multiple]); diff --git a/packages/lib/src/select/selectUtils.ts b/packages/lib/src/select/selectUtils.ts index bf7358a461..06821f7b95 100644 --- a/packages/lib/src/select/selectUtils.ts +++ b/packages/lib/src/select/selectUtils.ts @@ -38,7 +38,7 @@ const filterOptionsBySearchValue = ( searchValue: string ): ListOptionType[] | ListOptionGroupType[] => { if (options?.length > 0) { - if (isArrayOfOptionGroups(options)) { + if (isArrayOfOptionGroups(options)) return options.map((optionGroup) => { const group = { label: optionGroup.label, @@ -48,10 +48,10 @@ const filterOptionsBySearchValue = ( }; return group; }); - } - return options.filter((option) => option.label.toUpperCase().includes(searchValue.toUpperCase())); + else return options.filter((option) => option.label.toUpperCase().includes(searchValue.toUpperCase())); + } else { + return []; } - return []; }; /** @@ -65,7 +65,7 @@ const getLastOptionIndex = ( multiple: boolean ) => { let last = 0; - const reducer = (acc: number, current: ListOptionGroupType) => acc + (current?.options?.length || 0); + const reducer = (acc: number, current: ListOptionGroupType) => acc + (current?.options?.length ?? 0); if (searchable && filteredOptions?.length > 0) { if (isArrayOfOptionGroups(filteredOptions)) { @@ -124,7 +124,7 @@ const getSelectedOption = ( singleSelectionIndex = optional ? groupIndex + 1 : groupIndex; return true; } - groupIndex += 1; + groupIndex++; return false; }); } else if (option.value === value) { diff --git a/packages/lib/src/tabs/TabsContext.tsx b/packages/lib/src/tabs/TabsContext.tsx index d2825afd00..74ac6c6e67 100644 --- a/packages/lib/src/tabs/TabsContext.tsx +++ b/packages/lib/src/tabs/TabsContext.tsx @@ -1,6 +1,4 @@ import { createContext } from "react"; import { TabsContextProps } from "./types"; -const TabsContext = createContext(null); - -export default TabsContext +export default createContext(null); From 30bab73e2750d49cc6612321d20a7c8bf50ca532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:10:17 +0100 Subject: [PATCH 14/41] Discussed updates --- .../ContextualMenu.stories.tsx | 17 +++--- .../src/contextual-menu/ContextualMenu.tsx | 6 +- .../contextual-menu/ContextualMenuContext.tsx | 4 ++ .../lib/src/contextual-menu/GroupItem.tsx | 3 +- .../lib/src/contextual-menu/SingleItem.tsx | 6 +- packages/lib/src/toast/Toast.tsx | 55 +++++++++---------- packages/lib/src/toast/ToastContext.tsx | 4 ++ packages/lib/src/toast/ToastsQueue.tsx | 36 ++++-------- packages/lib/src/toast/types.ts | 4 +- packages/lib/src/toast/useToast.tsx | 4 +- packages/lib/src/utils/useTimeout.tsx | 14 ++--- 11 files changed, 71 insertions(+), 82 deletions(-) create mode 100644 packages/lib/src/contextual-menu/ContextualMenuContext.tsx create mode 100644 packages/lib/src/toast/ToastContext.tsx diff --git a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx index dc8b6904ec..43cb920d5e 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx @@ -4,9 +4,10 @@ import Title from "../../.storybook/components/Title"; import DxcBadge from "../badge/Badge"; import DxcContainer from "../container/Container"; import useTheme from "../useTheme"; -import DxcContextualMenu, { ContextualMenuContext } from "./ContextualMenu"; +import DxcContextualMenu from "./ContextualMenu"; import SingleItem from "./SingleItem"; import { userEvent, within } from "@storybook/test"; +import ContextualMenuContext from "./ContextualMenuContext"; export default { title: "Contextual Menu", @@ -183,33 +184,33 @@ export const SingleItemStates = () => { {} }}> <ExampleContainer> - <SingleItem {...items[0]} id={0} depthLevel={0} /> + <SingleItem {...items[0]!} id={0} depthLevel={0} /> </ExampleContainer> <Title title="Focus" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-focus"> - <SingleItem {...items[0]} id={0} depthLevel={0} /> + <SingleItem {...items[0]!} id={0} depthLevel={0} /> </ExampleContainer> <Title title="Hover" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-hover"> - <SingleItem {...items[0]} id={0} depthLevel={0} /> + <SingleItem {...items[0]!} id={0} depthLevel={0} /> </ExampleContainer> <Title title="Active" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-active"> - <SingleItem {...items[0]} id={0} depthLevel={0} /> + <SingleItem {...items[0]!} id={0} depthLevel={0} /> </ExampleContainer> </ContextualMenuContext.Provider> <ContextualMenuContext.Provider value={{ selectedItemId: 0, setSelectedItemId: () => {} }}> <Title title="Selected" theme="light" level={3} /> <ExampleContainer> - <SingleItem {...items[0]} id={0} depthLevel={0} /> + <SingleItem {...items[0]!} id={0} depthLevel={0} /> </ExampleContainer> <Title title="Selected hover" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-hover"> - <SingleItem {...items[0]} id={0} depthLevel={0} /> + <SingleItem {...items[0]!} id={0} depthLevel={0} /> </ExampleContainer> <Title title="Selected active" theme="light" level={3} /> <ExampleContainer pseudoState="pseudo-active"> - <SingleItem {...items[0]} id={0} depthLevel={0} /> + <SingleItem {...items[0]!} id={0} depthLevel={0} /> </ExampleContainer> </ContextualMenuContext.Provider> </DxcContainer> diff --git a/packages/lib/src/contextual-menu/ContextualMenu.tsx b/packages/lib/src/contextual-menu/ContextualMenu.tsx index 73e7256c78..214cae378f 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.tsx @@ -1,10 +1,9 @@ -import { createContext, useLayoutEffect, useMemo, useRef, useState } from "react"; +import { useLayoutEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import CoreTokens from "../common/coreTokens"; import useTheme from "../useTheme"; import MenuItem from "./MenuItem"; import ContextualMenuPropsType, { - ContextualMenuContextProps, GroupItem, GroupItemWithId, Item, @@ -14,6 +13,7 @@ import ContextualMenuPropsType, { SectionWithId, } from "./types"; import Section from "./Section"; +import ContextualMenuContext from "./ContextualMenuContext"; const ContextualMenu = styled.div` box-sizing: border-box; @@ -74,8 +74,6 @@ export const SubMenu = ({ children, id }: SubMenuProps) => ( </StyledSubMenu> ); -export const ContextualMenuContext = createContext<ContextualMenuContextProps | null>(null); - export default function DxcContextualMenu({ items }: ContextualMenuPropsType) { const [selectedItemId, setSelectedItemId] = useState(-1); const contextualMenuRef = useRef<HTMLDivElement | null>(null); diff --git a/packages/lib/src/contextual-menu/ContextualMenuContext.tsx b/packages/lib/src/contextual-menu/ContextualMenuContext.tsx new file mode 100644 index 0000000000..767f9f8513 --- /dev/null +++ b/packages/lib/src/contextual-menu/ContextualMenuContext.tsx @@ -0,0 +1,4 @@ +import { createContext } from "react"; +import { ContextualMenuContextProps } from "./types"; + +export default createContext<ContextualMenuContextProps | null>(null); diff --git a/packages/lib/src/contextual-menu/GroupItem.tsx b/packages/lib/src/contextual-menu/GroupItem.tsx index 7220b3f8c6..95cad49611 100644 --- a/packages/lib/src/contextual-menu/GroupItem.tsx +++ b/packages/lib/src/contextual-menu/GroupItem.tsx @@ -1,9 +1,10 @@ import { useContext, useMemo, useState, memo, useId } from "react"; import DxcIcon from "../icon/Icon"; -import { ContextualMenuContext, SubMenu } from "./ContextualMenu"; +import { SubMenu } from "./ContextualMenu"; import ItemAction from "./ItemAction"; import MenuItem from "./MenuItem"; import { GroupItemProps, ItemWithId } from "./types"; +import ContextualMenuContext from "./ContextualMenuContext"; const isGroupSelected = (items: GroupItemProps["items"], selectedItemId?: number): boolean => items.some((item) => { diff --git a/packages/lib/src/contextual-menu/SingleItem.tsx b/packages/lib/src/contextual-menu/SingleItem.tsx index 54cb247e73..447a2c6a41 100644 --- a/packages/lib/src/contextual-menu/SingleItem.tsx +++ b/packages/lib/src/contextual-menu/SingleItem.tsx @@ -1,9 +1,9 @@ import { useContext, useEffect } from "react"; -import { ContextualMenuContext } from "./ContextualMenu"; import ItemAction from "./ItemAction"; import { SingleItemProps } from "./types"; +import ContextualMenuContext from "./ContextualMenuContext"; -const SingleItem = ({ id, onSelect, selectedByDefault, ...props }: SingleItemProps) => { +const SingleItem = ({ id, onSelect, selectedByDefault = false, ...props }: SingleItemProps) => { const { selectedItemId, setSelectedItemId } = useContext(ContextualMenuContext) ?? {}; const handleClick = () => { @@ -21,7 +21,7 @@ const SingleItem = ({ id, onSelect, selectedByDefault, ...props }: SingleItemPro <ItemAction aria-pressed={selectedItemId === -1 ? selectedByDefault : selectedItemId === id} onClick={handleClick} - selected={selectedItemId === -1 ? (selectedByDefault ?? false) : selectedItemId === id} + selected={selectedItemId === -1 ? selectedByDefault : selectedItemId === id} {...props} /> ); diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx index 53e644fac7..3d76aea535 100644 --- a/packages/lib/src/toast/Toast.tsx +++ b/packages/lib/src/toast/Toast.tsx @@ -12,6 +12,28 @@ import useTimeout from "../utils/useTimeout"; import useTranslatedLabels from "../useTranslatedLabels"; import { responsiveSizes } from "../common/variables"; +const fadeInUp = keyframes` + 0% { + transform: translateY(100%); + opacity: 0; + } + 100% { + transform: translateY(0); + opacity: 1; + } +`; + +const fadeOutDown = keyframes` + 0% { + transform: translateY(0); + opacity: 1; + } + 100% { + transform: translateY(100%); + opacity: 0; + } +`; + const getSemantic = (semantic: ToastPropsType["semantic"]) => { switch (semantic) { case "info": @@ -37,28 +59,6 @@ const getSemantic = (semantic: ToastPropsType["semantic"]) => { } }; -const fadeInUp = keyframes` - 0% { - transform: translateY(100%); - opacity: 0; - } - 100% { - transform: translateY(0); - opacity: 1; - } -`; - -const fadeOutDown = keyframes` - 0% { - transform: translateY(0); - opacity: 1; - } - 100% { - transform: translateY(100%); - opacity: 0; - } -`; - const Toast = styled.output<{ semantic: ToastPropsType["semantic"]; isClosing: boolean }>` box-sizing: border-box; min-width: 200px; @@ -116,17 +116,14 @@ const ToastIcon = memo( loading, semantic, }: Pick<ToastPropsType, "icon" | "hideSemanticIcon" | "loading" | "semantic">) => { - if (semantic === "default") { - return typeof icon === "string" ? <DxcIcon icon={icon} /> : icon; - } - if (semantic === "info" && loading) { + if (semantic === "default") return typeof icon === "string" ? <DxcIcon icon={icon} /> : icon; + else if (semantic === "info" && loading) return ( <HalstackProvider theme={spinnerTheme}> <DxcSpinner mode="small" /> </HalstackProvider> ); - } - return !hideSemanticIcon && <DxcIcon icon={getSemantic(semantic).icon} />; + else return !hideSemanticIcon && <DxcIcon icon={getSemantic(semantic).icon} />; } ); @@ -176,7 +173,6 @@ const DxcToast = ({ )} <DxcActionIcon icon="clear" - title={translatedLabels?.toast?.clearToastActionTitle ?? ""} onClick={() => { if (!loading) { clearClosingAnimationTimer(); @@ -187,6 +183,7 @@ const DxcToast = ({ onClear(); }, 300); }} + title={translatedLabels?.toast?.clearToastActionTitle ?? ""} /> </DxcFlex> </Toast> diff --git a/packages/lib/src/toast/ToastContext.tsx b/packages/lib/src/toast/ToastContext.tsx new file mode 100644 index 0000000000..e4f07e111a --- /dev/null +++ b/packages/lib/src/toast/ToastContext.tsx @@ -0,0 +1,4 @@ +import { createContext } from "react"; +import { ToastContextType } from "./types"; + +export default createContext<ToastContextType | null>(null); diff --git a/packages/lib/src/toast/ToastsQueue.tsx b/packages/lib/src/toast/ToastsQueue.tsx index 1292e96c18..9d0f9b1712 100644 --- a/packages/lib/src/toast/ToastsQueue.tsx +++ b/packages/lib/src/toast/ToastsQueue.tsx @@ -5,22 +5,7 @@ import CoreTokens from "../common/coreTokens"; import DxcToast from "./Toast"; import { QueuedToast, Semantic, ToastContextType, ToastsQueuePropsType, ToastType } from "./types"; import { responsiveSizes } from "../common/variables"; - -export const ToastContext = createContext<ToastContextType | null>(null); - -const generateUniqueToastId = (toasts: QueuedToast[]) => { - let uniqueId: string; - let exists: boolean; - - const isIdTaken = (id: string) => toasts.some((toast) => toast.id === id); - - do { - uniqueId = `${performance.now()}-${Math.random().toString(36).slice(2, 9)}`; - exists = isIdTaken(uniqueId); - } while (exists); - - return uniqueId; -}; +import ToastContext from "./ToastContext"; const ToastsQueue = styled.section` box-sizing: border-box; @@ -40,6 +25,16 @@ const ToastsQueue = styled.section` } `; +const generateUniqueToastId = (toasts: QueuedToast[]) => { + let id = ""; + let exists = true; + while (exists) { + id = `${performance.now()}-${Math.random().toString(36).slice(2, 9)}`; + exists = toasts.some((toast) => toast.id === id); + } + return id; +}; + const DxcToastsQueue = ({ children, duration = 3000 }: ToastsQueuePropsType) => { const [toasts, setToasts] = useState<QueuedToast[]>([]); const [isMounted, setIsMounted] = useState(false); // Next.js SSR mounting issue @@ -58,19 +53,12 @@ const DxcToastsQueue = ({ children, duration = 3000 }: ToastsQueuePropsType) => [duration] ); - const contextValue = useMemo( - () => ({ - add, - }), - [add] - ); - useEffect(() => { setIsMounted(true); }, []); return ( - <ToastContext.Provider value={contextValue}> + <ToastContext.Provider value={add}> {isMounted && createPortal( <ToastsQueue> diff --git a/packages/lib/src/toast/types.ts b/packages/lib/src/toast/types.ts index 114c019b17..d378786d60 100644 --- a/packages/lib/src/toast/types.ts +++ b/packages/lib/src/toast/types.ts @@ -28,9 +28,7 @@ type QueuedToast = ToastType & { semantic: Semantic; }; -type ToastContextType = { - add: (toast: ToastType, semantic: Semantic) => () => void; -}; +type ToastContextType = (toast: ToastType, semantic: Semantic) => () => void; type ToastPropsType = { action?: Action; diff --git a/packages/lib/src/toast/useToast.tsx b/packages/lib/src/toast/useToast.tsx index 693ad9d1d8..28f5819c2d 100644 --- a/packages/lib/src/toast/useToast.tsx +++ b/packages/lib/src/toast/useToast.tsx @@ -1,9 +1,9 @@ import { useContext, useMemo } from "react"; -import { ToastContext } from "./ToastsQueue"; +import ToastContext from "./ToastContext"; import { DefaultToast, SemanticToast, LoadingToast } from "./types"; const useToast = () => { - const { add } = useContext(ToastContext) ?? {}; + const add = useContext(ToastContext); const toast = useMemo( () => ({ diff --git a/packages/lib/src/utils/useTimeout.tsx b/packages/lib/src/utils/useTimeout.tsx index 194f100fa4..070c71d5b2 100644 --- a/packages/lib/src/utils/useTimeout.tsx +++ b/packages/lib/src/utils/useTimeout.tsx @@ -1,7 +1,5 @@ import { useRef, useCallback, useEffect } from "react"; -type UseTimeoutType = (callback: () => void, delay?: number) => () => void; - /** * Custom hook to handle setTimeout in a declarative way. * Inspired by Dan Abramov's article: https://overreacted.io/making-setinterval-declarative-with-react-hooks/ @@ -10,7 +8,7 @@ type UseTimeoutType = (callback: () => void, delay?: number) => () => void; * @param delay Time in milliseconds to wait before executing the callback * @returns Function to clear the timeout */ -const useTimeout: UseTimeoutType = (callback, delay) => { +export default function useTimeout(callback: () => void, delay?: number) { const savedCallback = useRef<() => void>(); const timerRef = useRef<ReturnType<typeof setTimeout>>(); const clearTimerCallback = useCallback(() => clearTimeout(timerRef.current), []); @@ -20,14 +18,14 @@ const useTimeout: UseTimeoutType = (callback, delay) => { }, [callback]); useEffect(() => { + function tick() { + savedCallback.current?.(); + } if (delay != null) { - timerRef.current = setTimeout((savedCallback.current!), delay); + timerRef.current = setTimeout(tick, delay); return clearTimerCallback; } - return undefined; }, [delay, clearTimerCallback]); return clearTimerCallback; -}; - -export default useTimeout; +} From b6ff887fd331795808f69cd382274e5fe78aa1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:11:55 +0100 Subject: [PATCH 15/41] Small change --- packages/lib/src/contextual-menu/ContextualMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/src/contextual-menu/ContextualMenu.tsx b/packages/lib/src/contextual-menu/ContextualMenu.tsx index 214cae378f..dbf9f7957d 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.tsx @@ -87,7 +87,7 @@ export default function DxcContextualMenu({ items }: ContextualMenuPropsType) { const contextualMenuEl = contextualMenuRef?.current; const selectedItemEl = contextualMenuEl?.querySelector("[aria-pressed='true']") as HTMLButtonElement; contextualMenuEl?.scrollTo?.({ - top: (selectedItemEl?.offsetTop || 0) - (contextualMenuEl?.clientHeight || 0) / 2, + top: (selectedItemEl?.offsetTop ?? 0) - (contextualMenuEl?.clientHeight ?? 0) / 2, }); setFirstUpdate(false); } From c1bf88373c876d22e7144fbf136b807319ab546d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:11:16 +0100 Subject: [PATCH 16/41] HalstackContext typing updates --- packages/lib/src/HalstackContext.tsx | 58 +++++++++++-------- .../src/accordion-group/AccordionGroup.tsx | 2 +- packages/lib/src/accordion/Accordion.tsx | 2 +- packages/lib/src/alert/Alert.tsx | 20 +++---- .../lib/src/bulleted-list/BulletedList.tsx | 2 +- packages/lib/src/button/Button.tsx | 2 +- packages/lib/src/card/Card.tsx | 2 +- packages/lib/src/checkbox/Checkbox.tsx | 4 +- packages/lib/src/chip/Chip.tsx | 2 +- .../ContextualMenu.stories.tsx | 2 +- .../src/contextual-menu/ContextualMenu.tsx | 2 +- packages/lib/src/data-grid/DataGrid.tsx | 2 +- packages/lib/src/date-input/Calendar.tsx | 2 +- .../lib/src/date-input/DateInput.stories.tsx | 2 +- packages/lib/src/date-input/DateInput.tsx | 4 +- packages/lib/src/date-input/DatePicker.tsx | 2 +- packages/lib/src/dialog/Dialog.tsx | 4 +- .../lib/src/dropdown/Dropdown.stories.tsx | 2 +- packages/lib/src/dropdown/Dropdown.tsx | 2 +- packages/lib/src/file-input/FileInput.tsx | 4 +- packages/lib/src/file-input/FileItem.tsx | 4 +- packages/lib/src/footer/Footer.tsx | 4 +- packages/lib/src/header/Header.tsx | 4 +- packages/lib/src/heading/Heading.tsx | 2 +- packages/lib/src/image/Image.tsx | 2 +- packages/lib/src/layout/ApplicationLayout.tsx | 2 +- packages/lib/src/link/Link.tsx | 2 +- packages/lib/src/nav-tabs/NavTabs.tsx | 2 +- packages/lib/src/nav-tabs/Tab.tsx | 2 +- packages/lib/src/paginator/Paginator.tsx | 4 +- packages/lib/src/paragraph/Paragraph.tsx | 2 +- .../lib/src/password-input/PasswordInput.tsx | 2 +- packages/lib/src/progress-bar/ProgressBar.tsx | 2 +- packages/lib/src/quick-nav/QuickNav.tsx | 4 +- packages/lib/src/radio-group/Radio.tsx | 2 +- packages/lib/src/radio-group/RadioGroup.tsx | 4 +- .../src/resultset-table/ResultsetTable.tsx | 2 +- packages/lib/src/select/Listbox.tsx | 2 +- packages/lib/src/select/Select.stories.tsx | 2 +- packages/lib/src/select/Select.tsx | 4 +- packages/lib/src/sidenav/Sidenav.tsx | 2 +- packages/lib/src/slider/Slider.tsx | 2 +- packages/lib/src/spinner/Spinner.tsx | 2 +- packages/lib/src/switch/Switch.tsx | 4 +- packages/lib/src/table/Table.tsx | 2 +- packages/lib/src/tabs/Tabs.tsx | 4 +- packages/lib/src/tabs/TabsLegacy.tsx | 4 +- packages/lib/src/tag/Tag.tsx | 2 +- packages/lib/src/text-input/Suggestions.tsx | 2 +- .../lib/src/text-input/TextInput.stories.tsx | 2 +- packages/lib/src/text-input/TextInput.tsx | 4 +- packages/lib/src/textarea/Textarea.tsx | 4 +- packages/lib/src/toast/Toast.tsx | 2 +- packages/lib/src/toggle-group/ToggleGroup.tsx | 2 +- packages/lib/src/useTheme.tsx | 10 ---- packages/lib/src/useTranslatedLabels.tsx | 10 ---- packages/lib/src/utils/FocusLock.tsx | 15 +++-- packages/lib/src/utils/useTheme.tsx | 10 ++++ .../lib/src/utils/useTranslatedLabels.tsx | 10 ++++ packages/lib/src/wizard/Wizard.tsx | 21 +++---- 60 files changed, 148 insertions(+), 142 deletions(-) delete mode 100644 packages/lib/src/useTheme.tsx delete mode 100644 packages/lib/src/useTranslatedLabels.tsx create mode 100644 packages/lib/src/utils/useTheme.tsx create mode 100644 packages/lib/src/utils/useTranslatedLabels.tsx diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index 3db2aa4068..6dd32a0223 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -16,8 +16,8 @@ export type DeepPartial<T> = { [P in keyof T]?: Partial<T[P]>; }; -const HalstackContext = createContext<DeepPartial<AdvancedTheme> | null>(null); -const HalstackLanguageContext = createContext<DeepPartial<TranslatedLabels> | null>(null); +const HalstackContext = createContext<AdvancedTheme>(componentTokens); +const HalstackLanguageContext = createContext<TranslatedLabels>(defaultTranslatedComponentLabels); const addLightness = (newLightness: number, hexColor?: string) => { try { @@ -47,22 +47,6 @@ const subLightness = (newLightness: number, hexColor?: string) => { } }; -const parseAdvancedTheme = (advancedTheme: DeepPartial<AdvancedTheme>): AdvancedTheme => { - const allTokensCopy: AdvancedTheme = JSON.parse(JSON.stringify(componentTokens)); - - (Object.keys(allTokensCopy) as (keyof AdvancedTheme)[]).forEach((component) => { - const componentTheme = advancedTheme[component]; - if (componentTheme != null) { - (Object.keys(componentTheme) as (keyof typeof componentTheme)[]).forEach((objectKey) => { - if (componentTheme[objectKey]) { - allTokensCopy[component][objectKey] = componentTheme[objectKey]; - } - }); - } - }); - return allTokensCopy; -}; - const parseTheme = (theme: DeepPartial<OpinionatedTheme>): AdvancedTheme => { const componentTokensCopy: AdvancedTheme = JSON.parse(JSON.stringify(componentTokens)); @@ -394,6 +378,22 @@ const parseTheme = (theme: DeepPartial<OpinionatedTheme>): AdvancedTheme => { return componentTokensCopy; }; +const parseAdvancedTheme = (advancedTheme: DeepPartial<AdvancedTheme>): AdvancedTheme => { + const allTokensCopy: AdvancedTheme = JSON.parse(JSON.stringify(componentTokens)); + + (Object.keys(allTokensCopy) as (keyof AdvancedTheme)[]).forEach((component) => { + const componentTheme = advancedTheme[component]; + if (componentTheme != null) { + (Object.keys(componentTheme) as (keyof typeof componentTheme)[]).forEach((objectKey) => { + if (componentTheme[objectKey]) { + allTokensCopy[component][objectKey] = componentTheme[objectKey]; + } + }); + } + }); + return allTokensCopy; +}; + const parseLabels = (labels: DeepPartial<TranslatedLabels>): TranslatedLabels => { const parsedLabels = defaultTranslatedComponentLabels; (Object.keys(labels) as (keyof TranslatedLabels)[]).forEach((component) => { @@ -419,15 +419,27 @@ type HalstackProviderPropsType = { }; const HalstackProvider = ({ theme, advancedTheme, labels, children }: HalstackProviderPropsType): JSX.Element => { const parsedTheme = useMemo( - () => (theme ? parseTheme(theme) : advancedTheme ? parseAdvancedTheme(advancedTheme) : componentTokens), + () => (theme ? parseTheme(theme) : advancedTheme ? parseAdvancedTheme(advancedTheme) : null), [theme, advancedTheme] ); - const parsedLabels = useMemo(() => (labels ? parseLabels(labels) : defaultTranslatedComponentLabels), [labels]); + const parsedLabels = useMemo(() => (labels ? parseLabels(labels) : null), [labels]); return ( - <HalstackContext.Provider value={parsedTheme}> - <HalstackLanguageContext.Provider value={parsedLabels}>{children}</HalstackLanguageContext.Provider> - </HalstackContext.Provider> + <> + {parsedTheme ? ( + <HalstackContext.Provider value={parsedTheme}> + {parsedLabels ? ( + <HalstackLanguageContext.Provider value={parsedLabels}>{children}</HalstackLanguageContext.Provider> + ) : ( + children + )} + </HalstackContext.Provider> + ) : parsedLabels ? ( + <HalstackLanguageContext.Provider value={parsedLabels}>{children}</HalstackLanguageContext.Provider> + ) : ( + children + )} + </> ); }; diff --git a/packages/lib/src/accordion-group/AccordionGroup.tsx b/packages/lib/src/accordion-group/AccordionGroup.tsx index 54e331700c..f98707630b 100644 --- a/packages/lib/src/accordion-group/AccordionGroup.tsx +++ b/packages/lib/src/accordion-group/AccordionGroup.tsx @@ -2,7 +2,7 @@ import { Children, useCallback, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import AccordionGroupAccordion from "./AccordionGroupAccordion"; import AccordionGroupPropsType from "./types"; import AccordionGroupAccordionContext from "./AccordionGroupContext"; diff --git a/packages/lib/src/accordion/Accordion.tsx b/packages/lib/src/accordion/Accordion.tsx index e3637a9ffe..11154a6250 100644 --- a/packages/lib/src/accordion/Accordion.tsx +++ b/packages/lib/src/accordion/Accordion.tsx @@ -2,7 +2,7 @@ import { useId, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import AccordionPropsType from "./types"; import DxcIcon from "../icon/Icon"; diff --git a/packages/lib/src/alert/Alert.tsx b/packages/lib/src/alert/Alert.tsx index 519d5a35c3..485764f290 100644 --- a/packages/lib/src/alert/Alert.tsx +++ b/packages/lib/src/alert/Alert.tsx @@ -1,7 +1,7 @@ import styled, { css, ThemeProvider } from "styled-components"; -import { useState, memo, useId, useEffect } from "react"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import { useState, memo, useId, useEffect, useCallback } from "react"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import AlertPropsType from "./types"; import DxcIcon from "../icon/Icon"; import DxcButton from "../button/Button"; @@ -144,9 +144,9 @@ export default function DxcAlert({ const handleNextOnClick = () => { setCurrentIndex((prevIndex) => (prevIndex < messages.length ? prevIndex + 1 : prevIndex)); }; - const handlePrevOnClick = () => { + const handlePrevOnClick = useCallback(() => { setCurrentIndex((prevIndex) => (prevIndex > 0 ? prevIndex - 1 : prevIndex)); - }; + }, []); const handleOnClose = () => { messages[currentIndex]?.onClose?.(); if (mode !== "modal") { @@ -156,7 +156,7 @@ export default function DxcAlert({ useEffect(() => { if (currentIndex === messages.length) handlePrevOnClick(); - }, [currentIndex, messages]); + }, [currentIndex, messages, handlePrevOnClick]); return ( <ThemeProvider theme={colorsTheme.alert}> @@ -195,7 +195,7 @@ export default function DxcAlert({ <DxcFlex alignItems="center" gap="0.25rem"> <DxcActionIcon icon="chevron_left" - title={translatedLabels?.alert?.previousMessageActionTitle ?? ""} + title={translatedLabels.alert.previousMessageActionTitle} onClick={handlePrevOnClick} disabled={currentIndex === 0} /> @@ -204,7 +204,7 @@ export default function DxcAlert({ </NavigationText> <DxcActionIcon icon="chevron_right" - title={translatedLabels?.alert?.nextMessageActionTitle ?? ""} + title={translatedLabels.alert.nextMessageActionTitle} onClick={handleNextOnClick} disabled={currentIndex === messages.length - 1} /> @@ -217,8 +217,8 @@ export default function DxcAlert({ icon="close" title={ messages.length > 1 - ? (translatedLabels?.alert?.closeMessageActionTitle ?? "") - : (translatedLabels?.alert?.closeAlertActionTitle ?? "") + ? translatedLabels.alert.closeMessageActionTitle + : translatedLabels.alert.closeAlertActionTitle } onClick={handleOnClose} /> diff --git a/packages/lib/src/bulleted-list/BulletedList.tsx b/packages/lib/src/bulleted-list/BulletedList.tsx index e489552c99..14c563b54a 100644 --- a/packages/lib/src/bulleted-list/BulletedList.tsx +++ b/packages/lib/src/bulleted-list/BulletedList.tsx @@ -3,7 +3,7 @@ import styled, { ThemeProvider } from "styled-components"; import DxcFlex from "../flex/Flex"; import DxcTypography from "../typography/Typography"; import BulletedListPropsType, { BulletedListItemPropsType } from "./types"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import DxcIcon from "../icon/Icon"; const BulletedListItem = ({ children }: BulletedListItemPropsType): JSX.Element => <>{children}</>; diff --git a/packages/lib/src/button/Button.tsx b/packages/lib/src/button/Button.tsx index b3ced6662d..b5a70ce65f 100644 --- a/packages/lib/src/button/Button.tsx +++ b/packages/lib/src/button/Button.tsx @@ -1,7 +1,7 @@ import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; import { getMargin } from "../common/utils"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import type ButtonPropsType from "./types"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; diff --git a/packages/lib/src/card/Card.tsx b/packages/lib/src/card/Card.tsx index c727364dc6..c9a977f567 100644 --- a/packages/lib/src/card/Card.tsx +++ b/packages/lib/src/card/Card.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import CardPropsType from "./types"; import CoreTokens from "../common/coreTokens"; diff --git a/packages/lib/src/checkbox/Checkbox.tsx b/packages/lib/src/checkbox/Checkbox.tsx index 5bb8aff14f..22500473f7 100644 --- a/packages/lib/src/checkbox/Checkbox.tsx +++ b/packages/lib/src/checkbox/Checkbox.tsx @@ -2,8 +2,8 @@ import { useState, useRef, useId, forwardRef, KeyboardEvent } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; import { getMargin } from "../common/utils"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import CheckboxPropsType, { RefType } from "./types"; const checkedIcon = ( diff --git a/packages/lib/src/chip/Chip.tsx b/packages/lib/src/chip/Chip.tsx index 7681b56a45..b842ca663c 100644 --- a/packages/lib/src/chip/Chip.tsx +++ b/packages/lib/src/chip/Chip.tsx @@ -2,7 +2,7 @@ import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import ChipPropsType from "./types"; const DxcChip = ({ diff --git a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx index 43cb920d5e..606533f745 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx @@ -3,7 +3,7 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcBadge from "../badge/Badge"; import DxcContainer from "../container/Container"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import DxcContextualMenu from "./ContextualMenu"; import SingleItem from "./SingleItem"; import { userEvent, within } from "@storybook/test"; diff --git a/packages/lib/src/contextual-menu/ContextualMenu.tsx b/packages/lib/src/contextual-menu/ContextualMenu.tsx index dbf9f7957d..cb954efda6 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.tsx @@ -1,7 +1,7 @@ import { useLayoutEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import CoreTokens from "../common/coreTokens"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import MenuItem from "./MenuItem"; import ContextualMenuPropsType, { GroupItem, diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index f34c91106c..fa226e199f 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -20,7 +20,7 @@ import { getMinItemsPerPageIndex, getMaxItemsPerPageIndex, } from "./utils"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import DxcPaginator from "../paginator/Paginator"; import { DxcActionsCell } from "../table/Table"; diff --git a/packages/lib/src/date-input/Calendar.tsx b/packages/lib/src/date-input/Calendar.tsx index 55dc04e737..fde9b0dd23 100644 --- a/packages/lib/src/date-input/Calendar.tsx +++ b/packages/lib/src/date-input/Calendar.tsx @@ -2,7 +2,7 @@ import { Dayjs } from "dayjs"; import { useState, useMemo, useEffect, useId, memo, KeyboardEvent, FocusEvent } from "react"; import styled from "styled-components"; import { CalendarPropsType, DateType } from "./types"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; const getDays = (innerDate: Dayjs) => { const monthDayCells: DateType[] = []; diff --git a/packages/lib/src/date-input/DateInput.stories.tsx b/packages/lib/src/date-input/DateInput.stories.tsx index fce54ba071..b118b2acc4 100644 --- a/packages/lib/src/date-input/DateInput.stories.tsx +++ b/packages/lib/src/date-input/DateInput.stories.tsx @@ -7,7 +7,7 @@ import preview from "../../.storybook/preview"; import { disabledRules } from "../../test/accessibility/rules/specific/date-input/disabledRules"; import DxcContainer from "../container/Container"; import { HalstackProvider } from "../HalstackContext"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import Calendar from "./Calendar"; import DxcDateInput from "./DateInput"; import DxcDatePicker from "./DatePicker"; diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index 7a69d59b66..93eb0515ba 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -14,8 +14,8 @@ import dayjs, { Dayjs } from "dayjs"; import styled, { ThemeProvider } from "styled-components"; import * as Popover from "@radix-ui/react-popover"; import customParseFormat from "dayjs/plugin/customParseFormat"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import DateInputPropsType, { RefType } from "./types"; import DatePicker from "./DatePicker"; import { getMargin } from "../common/utils"; diff --git a/packages/lib/src/date-input/DatePicker.tsx b/packages/lib/src/date-input/DatePicker.tsx index 73b836ba67..8050d135c1 100644 --- a/packages/lib/src/date-input/DatePicker.tsx +++ b/packages/lib/src/date-input/DatePicker.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { DatePickerPropsType } from "./types"; import Calendar from "./Calendar"; import YearPicker from "./YearPicker"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import DxcIcon from "../icon/Icon"; import {Tooltip} from "../tooltip/Tooltip"; diff --git a/packages/lib/src/dialog/Dialog.tsx b/packages/lib/src/dialog/Dialog.tsx index 07614a085c..be2d8ea88a 100644 --- a/packages/lib/src/dialog/Dialog.tsx +++ b/packages/lib/src/dialog/Dialog.tsx @@ -3,8 +3,8 @@ import { createPortal } from "react-dom"; import styled, { createGlobalStyle, ThemeProvider } from "styled-components"; import { responsiveSizes } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import FocusLock from "../utils/FocusLock"; import DialogPropsType from "./types"; diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 1898f06d8b..00eb04e67b 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -4,7 +4,7 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcFlex from "../flex/Flex"; import { HalstackProvider } from "../HalstackContext"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import DxcDropdown from "./Dropdown"; import DropdownMenu from "./DropdownMenu"; import { Option } from "./types"; diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index 37ed78acc7..6d52957299 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -4,7 +4,7 @@ import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import useWidth from "../utils/useWidth"; import DropdownMenu from "./DropdownMenu"; import DropdownPropsType from "./types"; diff --git a/packages/lib/src/file-input/FileInput.tsx b/packages/lib/src/file-input/FileInput.tsx index ca6ac3e04a..c935004603 100644 --- a/packages/lib/src/file-input/FileInput.tsx +++ b/packages/lib/src/file-input/FileInput.tsx @@ -2,8 +2,8 @@ import { useCallback, useEffect, useId, useState, forwardRef, DragEvent, ChangeE import styled, { ThemeProvider } from "styled-components"; import DxcButton from "../button/Button"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import FileItem from "./FileItem"; import FileInputPropsType, { FileData, RefType } from "./types"; diff --git a/packages/lib/src/file-input/FileItem.tsx b/packages/lib/src/file-input/FileItem.tsx index 20039f6d7e..cd3bda581b 100644 --- a/packages/lib/src/file-input/FileItem.tsx +++ b/packages/lib/src/file-input/FileItem.tsx @@ -1,8 +1,8 @@ import { memo } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcFlex from "../flex/Flex"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import { FileItemProps } from "./types"; import DxcIcon from "../icon/Icon"; import DxcActionIcon from "../action-icon/ActionIcon"; diff --git a/packages/lib/src/footer/Footer.tsx b/packages/lib/src/footer/Footer.tsx index 67ae36107c..5304f8a4e3 100644 --- a/packages/lib/src/footer/Footer.tsx +++ b/packages/lib/src/footer/Footer.tsx @@ -4,8 +4,8 @@ import { responsiveSizes, spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import { dxcLogo, dxcSmallLogo } from "./Icons"; import FooterPropsType from "./types"; import { CoreSpacingTokensType } from "../common/coreTokens"; diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx index 07ad2274be..19da4eb9c9 100644 --- a/packages/lib/src/header/Header.tsx +++ b/packages/lib/src/header/Header.tsx @@ -3,8 +3,8 @@ import styled, { ThemeProvider } from "styled-components"; import { responsiveSizes, spaces } from "../common/variables"; import DxcDropdown from "../dropdown/Dropdown"; import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import HeaderPropsType from "./types"; import { Tooltip } from "../tooltip/Tooltip"; import DxcFlex from "../flex/Flex"; diff --git a/packages/lib/src/heading/Heading.tsx b/packages/lib/src/heading/Heading.tsx index 066ce77f96..ace3b49b59 100644 --- a/packages/lib/src/heading/Heading.tsx +++ b/packages/lib/src/heading/Heading.tsx @@ -1,6 +1,6 @@ import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import HeadingPropsType from "./types"; const DxcHeading = ({ level = 1, text = "", as, weight, margin }: HeadingPropsType): JSX.Element => { diff --git a/packages/lib/src/image/Image.tsx b/packages/lib/src/image/Image.tsx index 2d7c506ff6..2ef5881891 100644 --- a/packages/lib/src/image/Image.tsx +++ b/packages/lib/src/image/Image.tsx @@ -1,6 +1,6 @@ import styled, { ThemeProvider } from "styled-components"; import { ReactNode, useCallback } from "react"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import ImagePropsType, { CaptionWrapperProps } from "./types"; const Figure = styled.figure` diff --git a/packages/lib/src/layout/ApplicationLayout.tsx b/packages/lib/src/layout/ApplicationLayout.tsx index c474b371b9..b14de75279 100644 --- a/packages/lib/src/layout/ApplicationLayout.tsx +++ b/packages/lib/src/layout/ApplicationLayout.tsx @@ -6,7 +6,7 @@ import DxcHeader from "../header/Header"; import DxcIcon from "../icon/Icon"; import DxcSidenav from "../sidenav/Sidenav"; import { SidenavContextProvider, useResponsiveSidenavVisibility } from "../sidenav/SidenavContext"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import { Tooltip } from "../tooltip/Tooltip"; import ApplicationLayoutPropsType, { AppLayoutMainPropsType } from "./types"; import { bottomLinks, findChildType, socialLinks, useResponsive, year } from "./utils"; diff --git a/packages/lib/src/link/Link.tsx b/packages/lib/src/link/Link.tsx index 49f5939296..56b1fced6f 100644 --- a/packages/lib/src/link/Link.tsx +++ b/packages/lib/src/link/Link.tsx @@ -2,7 +2,7 @@ import { forwardRef } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import { LinkProps } from "./types"; import CoreTokens from "../common/coreTokens"; diff --git a/packages/lib/src/nav-tabs/NavTabs.tsx b/packages/lib/src/nav-tabs/NavTabs.tsx index f1116dd034..cad7999623 100644 --- a/packages/lib/src/nav-tabs/NavTabs.tsx +++ b/packages/lib/src/nav-tabs/NavTabs.tsx @@ -1,6 +1,6 @@ import { Children, KeyboardEvent, ReactElement, ReactNode, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import NavTabsPropsType from "./types"; import DxcTab from "./Tab"; import NavTabsContext from "./NavTabsContext"; diff --git a/packages/lib/src/nav-tabs/Tab.tsx b/packages/lib/src/nav-tabs/Tab.tsx index 0f108628a9..7145e26669 100644 --- a/packages/lib/src/nav-tabs/Tab.tsx +++ b/packages/lib/src/nav-tabs/Tab.tsx @@ -3,7 +3,7 @@ import styled from "styled-components"; import DxcBadge from "../badge/Badge"; import DxcFlex from "../flex/Flex"; import NavTabsPropsType, { TabProps } from "./types"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import NavTabsContext from "./NavTabsContext"; import DxcIcon from "../icon/Icon"; diff --git a/packages/lib/src/paginator/Paginator.tsx b/packages/lib/src/paginator/Paginator.tsx index 0b4ddb34a8..951c47af14 100644 --- a/packages/lib/src/paginator/Paginator.tsx +++ b/packages/lib/src/paginator/Paginator.tsx @@ -1,8 +1,8 @@ import styled, { ThemeProvider } from "styled-components"; import DxcButton from "../button/Button"; import DxcSelect from "../select/Select"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import PaginatorPropsType from "./types"; const DxcPaginator = ({ diff --git a/packages/lib/src/paragraph/Paragraph.tsx b/packages/lib/src/paragraph/Paragraph.tsx index 7a5768b935..e512dca324 100644 --- a/packages/lib/src/paragraph/Paragraph.tsx +++ b/packages/lib/src/paragraph/Paragraph.tsx @@ -1,6 +1,6 @@ import { ReactNode } from "react"; import styled, { ThemeProvider } from "styled-components"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; const Paragraph = styled.p` display: ${(props) => props.theme.display}; diff --git a/packages/lib/src/password-input/PasswordInput.tsx b/packages/lib/src/password-input/PasswordInput.tsx index f9f79dfe38..7d7c33ad37 100644 --- a/packages/lib/src/password-input/PasswordInput.tsx +++ b/packages/lib/src/password-input/PasswordInput.tsx @@ -1,7 +1,7 @@ import { forwardRef, useEffect, useRef, useState } from "react"; import styled from "styled-components"; import DxcTextInput from "../text-input/TextInput"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import PasswordInputPropsType, { RefType } from "./types"; const setInputType = (type: string, element: HTMLDivElement | null) => { diff --git a/packages/lib/src/progress-bar/ProgressBar.tsx b/packages/lib/src/progress-bar/ProgressBar.tsx index 3210df1f5e..49f7d84441 100644 --- a/packages/lib/src/progress-bar/ProgressBar.tsx +++ b/packages/lib/src/progress-bar/ProgressBar.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import ProgressBarPropsType from "./types"; const DxcProgressBar = ({ diff --git a/packages/lib/src/quick-nav/QuickNav.tsx b/packages/lib/src/quick-nav/QuickNav.tsx index d3cc3cdabb..794c5b189c 100644 --- a/packages/lib/src/quick-nav/QuickNav.tsx +++ b/packages/lib/src/quick-nav/QuickNav.tsx @@ -4,8 +4,8 @@ import DxcFlex from "../flex/Flex"; import DxcHeading from "../heading/Heading"; import DxcInset from "../inset/Inset"; import DxcTypography from "../typography/Typography"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import QuickNavTypes from "./types"; const DxcQuickNav = ({ title, links }: QuickNavTypes): JSX.Element => { diff --git a/packages/lib/src/radio-group/Radio.tsx b/packages/lib/src/radio-group/Radio.tsx index 2a41372b2d..0913d43426 100644 --- a/packages/lib/src/radio-group/Radio.tsx +++ b/packages/lib/src/radio-group/Radio.tsx @@ -2,7 +2,7 @@ import { memo, useEffect, useId, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme } from "../common/variables"; import DxcFlex from "../flex/Flex"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import { RadioProps } from "./types"; const DxcRadio = ({ diff --git a/packages/lib/src/radio-group/RadioGroup.tsx b/packages/lib/src/radio-group/RadioGroup.tsx index b85d9280f1..39b1ed9e49 100644 --- a/packages/lib/src/radio-group/RadioGroup.tsx +++ b/packages/lib/src/radio-group/RadioGroup.tsx @@ -1,7 +1,7 @@ import { FocusEvent, forwardRef, KeyboardEvent, useCallback, useId, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import DxcRadio from "./Radio"; import RadioGroupPropsType, { RadioOption, RefType } from "./types"; diff --git a/packages/lib/src/resultset-table/ResultsetTable.tsx b/packages/lib/src/resultset-table/ResultsetTable.tsx index 59d2b022c4..9e77591d3a 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.tsx @@ -5,7 +5,7 @@ import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcPaginator from "../paginator/Paginator"; import DxcTable, { DxcActionsCell } from "../table/Table"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import icons from "./Icons"; import ResultsetTablePropsType, { Column, Row } from "./types"; diff --git a/packages/lib/src/select/Listbox.tsx b/packages/lib/src/select/Listbox.tsx index 49eb26d350..991ce64e94 100644 --- a/packages/lib/src/select/Listbox.tsx +++ b/packages/lib/src/select/Listbox.tsx @@ -1,7 +1,7 @@ import { useLayoutEffect, useRef } from "react"; import styled from "styled-components"; import DxcIcon from "../icon/Icon"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import ListOption from "./ListOption"; import { groupsHaveOptions } from "./selectUtils"; import { ListboxProps, ListOptionGroupType, ListOptionType } from "./types"; diff --git a/packages/lib/src/select/Select.stories.tsx b/packages/lib/src/select/Select.stories.tsx index 738945ce82..10ef505083 100644 --- a/packages/lib/src/select/Select.stories.tsx +++ b/packages/lib/src/select/Select.stories.tsx @@ -6,7 +6,7 @@ import preview from "../../.storybook/preview"; import { disabledRules } from "../../test/accessibility/rules/specific/select/disabledRules"; import DxcFlex from "../flex/Flex"; import { HalstackProvider } from "../HalstackContext"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import Listbox from "./Listbox"; import DxcSelect from "./Select"; diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index 3ddc741f06..63f0354433 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -5,8 +5,8 @@ import { spaces } from "../common/variables"; import { getMargin } from "../common/utils"; import DxcIcon from "../icon/Icon"; import { Tooltip, TooltipWrapper } from "../tooltip/Tooltip"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import useWidth from "../utils/useWidth"; import Listbox from "./Listbox"; import { diff --git a/packages/lib/src/sidenav/Sidenav.tsx b/packages/lib/src/sidenav/Sidenav.tsx index 309789b581..5472c307a8 100644 --- a/packages/lib/src/sidenav/Sidenav.tsx +++ b/packages/lib/src/sidenav/Sidenav.tsx @@ -5,7 +5,7 @@ import CoreTokens from "../common/coreTokens"; import { responsiveSizes } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import { useResponsiveSidenavVisibility } from "./SidenavContext"; import SidenavPropsType, { SidenavGroupPropsType, diff --git a/packages/lib/src/slider/Slider.tsx b/packages/lib/src/slider/Slider.tsx index 5469ad1a87..a02678fdab 100644 --- a/packages/lib/src/slider/Slider.tsx +++ b/packages/lib/src/slider/Slider.tsx @@ -3,7 +3,7 @@ import styled, { ThemeProvider } from "styled-components"; import DxcTextInput from "../text-input/TextInput"; import { spaces } from "../common/variables"; import { getMargin } from "../common/utils"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import SliderPropsType, { RefType } from "./types"; const DxcSlider = forwardRef<RefType, SliderPropsType>( diff --git a/packages/lib/src/spinner/Spinner.tsx b/packages/lib/src/spinner/Spinner.tsx index 1ca76618a1..93ecc94dc8 100644 --- a/packages/lib/src/spinner/Spinner.tsx +++ b/packages/lib/src/spinner/Spinner.tsx @@ -1,7 +1,7 @@ import { useId, useMemo } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import SpinnerPropsType from "./types"; const DxcSpinner = ({ label, value, showValue = false, mode = "large", margin }: SpinnerPropsType): JSX.Element => { diff --git a/packages/lib/src/switch/Switch.tsx b/packages/lib/src/switch/Switch.tsx index 90e2b8418a..e52c25f890 100644 --- a/packages/lib/src/switch/Switch.tsx +++ b/packages/lib/src/switch/Switch.tsx @@ -2,8 +2,8 @@ import { forwardRef, KeyboardEvent, useId, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; import { getMargin } from "../common/utils"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import SwitchPropsType, { RefType } from "./types"; const DxcSwitch = forwardRef<RefType, SwitchPropsType>( diff --git a/packages/lib/src/table/Table.tsx b/packages/lib/src/table/Table.tsx index 62f1aae2c6..314605e765 100644 --- a/packages/lib/src/table/Table.tsx +++ b/packages/lib/src/table/Table.tsx @@ -4,7 +4,7 @@ import { getMargin } from "../common/utils"; import DxcDropdown from "../dropdown/Dropdown"; import DxcFlex from "../flex/Flex"; import { DeepPartial, HalstackProvider } from "../HalstackContext"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import dropdownTheme from "./dropdownTheme"; import DxcActionIcon from "../action-icon/ActionIcon"; import TablePropsType, { ActionCellsPropsType } from "./types"; diff --git a/packages/lib/src/tabs/Tabs.tsx b/packages/lib/src/tabs/Tabs.tsx index e617d244bd..71d9694af4 100644 --- a/packages/lib/src/tabs/Tabs.tsx +++ b/packages/lib/src/tabs/Tabs.tsx @@ -10,13 +10,13 @@ import { useState, } from "react"; import styled, { ThemeProvider } from "styled-components"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import TabsContext from "./TabsContext"; import DxcTab from "./Tab"; import TabsPropsType, { TabProps } from "./types"; import DxcTabsLegacy from "./TabsLegacy"; import { spaces } from "../common/variables"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import DxcIcon from "../icon/Icon"; const useResize = (refTabList: MutableRefObject<HTMLDivElement | null>) => { diff --git a/packages/lib/src/tabs/TabsLegacy.tsx b/packages/lib/src/tabs/TabsLegacy.tsx index 9635fbe8ff..5ffd215e14 100644 --- a/packages/lib/src/tabs/TabsLegacy.tsx +++ b/packages/lib/src/tabs/TabsLegacy.tsx @@ -2,8 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import Tab from "./TabLegacy"; import TabsPropsType from "./types"; diff --git a/packages/lib/src/tag/Tag.tsx b/packages/lib/src/tag/Tag.tsx index e1bd13065c..407fc508e1 100644 --- a/packages/lib/src/tag/Tag.tsx +++ b/packages/lib/src/tag/Tag.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import DxcIcon from "../icon/Icon"; import TagPropsType from "./types"; import CoreTokens from "../common/coreTokens"; diff --git a/packages/lib/src/text-input/Suggestions.tsx b/packages/lib/src/text-input/Suggestions.tsx index c772dc7965..946023f0bd 100644 --- a/packages/lib/src/text-input/Suggestions.tsx +++ b/packages/lib/src/text-input/Suggestions.tsx @@ -1,6 +1,6 @@ import { memo, useEffect, useRef } from "react"; import styled from "styled-components"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import Suggestion from "./Suggestion"; import { SuggestionsProps } from "./types"; import DxcIcon from "../icon/Icon"; diff --git a/packages/lib/src/text-input/TextInput.stories.tsx b/packages/lib/src/text-input/TextInput.stories.tsx index ce5ba8e25c..6f177967b7 100644 --- a/packages/lib/src/text-input/TextInput.stories.tsx +++ b/packages/lib/src/text-input/TextInput.stories.tsx @@ -4,7 +4,7 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcFlex from "../flex/Flex"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import Suggestions from "./Suggestions"; import DxcTextInput from "./TextInput"; diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx index ef30062e09..b1c4329aa3 100644 --- a/packages/lib/src/text-input/TextInput.tsx +++ b/packages/lib/src/text-input/TextInput.tsx @@ -20,8 +20,8 @@ import { spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import NumberInputContext from "../number-input/NumberInputContext"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import useWidth from "../utils/useWidth"; import Suggestions from "./Suggestions"; import TextInputPropsType, { AutosuggestWrapperProps, RefType } from "./types"; diff --git a/packages/lib/src/textarea/Textarea.tsx b/packages/lib/src/textarea/Textarea.tsx index 7d9c20d290..ed631c58dd 100644 --- a/packages/lib/src/textarea/Textarea.tsx +++ b/packages/lib/src/textarea/Textarea.tsx @@ -2,8 +2,8 @@ import { ChangeEvent, FocusEvent, forwardRef, useEffect, useId, useRef, useState import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; -import useTheme from "../useTheme"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTheme from "../utils/useTheme"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import TextareaPropsType, { RefType } from "./types"; const patternMatch = (pattern: string, value: string) => new RegExp(pattern).test(value); diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx index 3d76aea535..6df2d40fad 100644 --- a/packages/lib/src/toast/Toast.tsx +++ b/packages/lib/src/toast/Toast.tsx @@ -9,7 +9,7 @@ import DxcSpinner from "../spinner/Spinner"; import { HalstackProvider } from "../HalstackContext"; import ToastPropsType from "./types"; import useTimeout from "../utils/useTimeout"; -import useTranslatedLabels from "../useTranslatedLabels"; +import useTranslatedLabels from "../utils/useTranslatedLabels"; import { responsiveSizes } from "../common/variables"; const fadeInUp = keyframes` diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 512fd24e46..0b80b299a3 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -4,7 +4,7 @@ import { spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import ToggleGroupPropsType, { OptionLabel } from "./types"; const DxcToggleGroup = ({ diff --git a/packages/lib/src/useTheme.tsx b/packages/lib/src/useTheme.tsx deleted file mode 100644 index 9d3abbc0c2..0000000000 --- a/packages/lib/src/useTheme.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useContext } from "react"; -import { componentTokens } from "./common/variables"; -import HalstackContext from "./HalstackContext"; - -const useTheme = () => { - const colorsTheme = useContext(HalstackContext); - return colorsTheme || componentTokens; -}; - -export default useTheme; diff --git a/packages/lib/src/useTranslatedLabels.tsx b/packages/lib/src/useTranslatedLabels.tsx deleted file mode 100644 index 95e1b488c4..0000000000 --- a/packages/lib/src/useTranslatedLabels.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useContext } from "react"; -import { defaultTranslatedComponentLabels } from "./common/variables"; -import { HalstackLanguageContext } from "./HalstackContext"; - -const useTranslatedLabels = () => { - const labels = useContext(HalstackLanguageContext); - return labels || defaultTranslatedComponentLabels; -}; - -export default useTranslatedLabels; diff --git a/packages/lib/src/utils/FocusLock.tsx b/packages/lib/src/utils/FocusLock.tsx index 8079a112cc..eef084f00b 100644 --- a/packages/lib/src/utils/FocusLock.tsx +++ b/packages/lib/src/utils/FocusLock.tsx @@ -59,8 +59,8 @@ const radixPortalContains = (activeElement: Node): boolean => { * @param ref: React.MutableRefObject<HTMLDivElement> * @returns */ -const useFocusableElements = (ref: React.MutableRefObject<HTMLDivElement | null>): HTMLElement[] | null => { - const [focusableElements, setFocusableElements] = useState<HTMLElement[] | null>(null); +const useFocusableElements = (ref: React.MutableRefObject<HTMLDivElement | null>): HTMLElement[] => { + const [focusableElements, setFocusableElements] = useState<HTMLElement[]>([]); useEffect(() => { if (ref?.current != null) { @@ -93,24 +93,23 @@ const FocusLock = ({ children }: { children: React.ReactNode }): JSX.Element => const initialFocus = useRef(false); const focusFirst = useCallback(() => { - if (focusableElements?.length === 0) childrenContainerRef.current?.focus(); - else if (focusableElements && 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]); const focusLast = () => { focusableElements - ?.slice() + .slice() .reverse() ?.some((element) => attemptFocus(element)); }; const focusLock = (event: React.KeyboardEvent<HTMLDivElement>) => { - if (event.key === "Tab" && focusableElements && focusableElements.length === 0) event.preventDefault(); + if (event.key === "Tab" && focusableElements.length === 0) event.preventDefault(); }; useEffect(() => { - if (focusableElements != null && !initialFocus.current) { + if (focusableElements.length > 0 && !initialFocus.current) { initialFocus.current = true; focusFirst(); } diff --git a/packages/lib/src/utils/useTheme.tsx b/packages/lib/src/utils/useTheme.tsx new file mode 100644 index 0000000000..f9ef4f03fc --- /dev/null +++ b/packages/lib/src/utils/useTheme.tsx @@ -0,0 +1,10 @@ +import { useContext } from "react"; +import { componentTokens } from "../common/variables"; +import HalstackContext from "../HalstackContext"; + +const useTheme = () => { + const colorsTheme = useContext(HalstackContext); + return colorsTheme ?? componentTokens; +}; + +export default useTheme; diff --git a/packages/lib/src/utils/useTranslatedLabels.tsx b/packages/lib/src/utils/useTranslatedLabels.tsx new file mode 100644 index 0000000000..446e2b6486 --- /dev/null +++ b/packages/lib/src/utils/useTranslatedLabels.tsx @@ -0,0 +1,10 @@ +import { useContext } from "react"; +import { defaultTranslatedComponentLabels } from "../common/variables"; +import { HalstackLanguageContext } from "../HalstackContext"; + +const useTranslatedLabels = () => { + const labels = useContext(HalstackLanguageContext); + return labels ?? defaultTranslatedComponentLabels; +}; + +export default useTranslatedLabels; diff --git a/packages/lib/src/wizard/Wizard.tsx b/packages/lib/src/wizard/Wizard.tsx index 8dcdff91a0..a0cf132f41 100644 --- a/packages/lib/src/wizard/Wizard.tsx +++ b/packages/lib/src/wizard/Wizard.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../useTheme"; +import useTheme from "../utils/useTheme"; import WizardPropsType, { StepProps } from "./types"; const icons = { @@ -53,24 +53,19 @@ const DxcWizard = ({ tabIndex = 0, }: WizardPropsType): JSX.Element => { const [innerCurrent, setInnerCurrentStep] = useState(currentStep ?? defaultCurrentStep ?? 0); - const renderedCurrent = currentStep == null ? innerCurrent : currentStep; + const renderedCurrent = currentStep ?? innerCurrent; const colorsTheme = useTheme(); const handleStepClick = (newValue: number) => { - if (currentStep == null) { - setInnerCurrentStep(newValue); - } - - if (onStepClick) { - onStepClick(newValue); - } + setInnerCurrentStep(newValue); + onStepClick?.(newValue); }; return ( - <ThemeProvider theme={colorsTheme?.wizard}> + <ThemeProvider theme={colorsTheme.wizard}> <StepsContainer mode={mode} margin={margin} role="group"> - {steps?.map((step, i) => ( - <StepContainer key={`step${i}`} mode={mode} lastStep={steps && i === (steps?.length || 0) - 1}> + {steps.map((step, i) => ( + <StepContainer key={`step${i}`} mode={mode} lastStep={i === steps.length - 1}> <Step onClick={() => { handleStepClick(i); @@ -78,7 +73,7 @@ const DxcWizard = ({ disabled={step.disabled} mode={mode} first={i === 0} - last={i === (steps?.length || 0) - 1} + last={i === steps.length - 1} aria-current={renderedCurrent === i ? "step" : "false"} tabIndex={tabIndex} > From b5f163d657b5560d3a35b50089ab806afca10d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:17:40 +0100 Subject: [PATCH 17/41] Small updated --- packages/lib/src/HalstackContext.tsx | 408 +++++++++++++-------------- 1 file changed, 204 insertions(+), 204 deletions(-) diff --git a/packages/lib/src/HalstackContext.tsx b/packages/lib/src/HalstackContext.tsx index 6dd32a0223..69693bf199 100644 --- a/packages/lib/src/HalstackContext.tsx +++ b/packages/lib/src/HalstackContext.tsx @@ -52,328 +52,328 @@ const parseTheme = (theme: DeepPartial<OpinionatedTheme>): AdvancedTheme => { const accordionTokens = componentTokensCopy.accordion; accordionTokens.assistiveTextFontColor = - theme?.accordion?.assistiveTextFontColor ?? accordionTokens.assistiveTextFontColor; - accordionTokens.titleLabelFontColor = theme?.accordion?.titleFontColor ?? accordionTokens.titleLabelFontColor; - accordionTokens.arrowColor = theme?.accordion?.accentColor ?? accordionTokens.arrowColor; - accordionTokens.iconColor = theme?.accordion?.accentColor ?? accordionTokens.iconColor; + theme.accordion?.assistiveTextFontColor ?? accordionTokens.assistiveTextFontColor; + accordionTokens.titleLabelFontColor = theme.accordion?.titleFontColor ?? accordionTokens.titleLabelFontColor; + accordionTokens.arrowColor = theme.accordion?.accentColor ?? accordionTokens.arrowColor; + accordionTokens.iconColor = theme.accordion?.accentColor ?? accordionTokens.iconColor; accordionTokens.hoverBackgroundColor = - addLightness(57, theme?.accordion?.accentColor) ?? accordionTokens.hoverBackgroundColor; + addLightness(57, theme.accordion?.accentColor) ?? accordionTokens.hoverBackgroundColor; const buttonTokens = componentTokensCopy.button; - buttonTokens.primaryDefaultFontColor = theme?.button?.primaryFontColor ?? buttonTokens.primaryDefaultFontColor; - buttonTokens.primaryDefaultBackgroundColor = theme?.button?.baseColor ?? buttonTokens.primaryDefaultBackgroundColor; - buttonTokens.secondaryDefaultFontColor = theme?.button?.baseColor ?? buttonTokens.secondaryDefaultFontColor; + buttonTokens.primaryDefaultFontColor = theme.button?.primaryFontColor ?? buttonTokens.primaryDefaultFontColor; + buttonTokens.primaryDefaultBackgroundColor = theme.button?.baseColor ?? buttonTokens.primaryDefaultBackgroundColor; + buttonTokens.secondaryDefaultFontColor = theme.button?.baseColor ?? buttonTokens.secondaryDefaultFontColor; buttonTokens.secondaryHoverDefaultFontColor = - theme?.button?.secondaryHoverFontColor ?? buttonTokens.secondaryHoverDefaultFontColor; - buttonTokens.secondaryDefaultBorderColor = theme?.button?.baseColor ?? buttonTokens.secondaryDefaultBorderColor; + theme.button?.secondaryHoverFontColor ?? buttonTokens.secondaryHoverDefaultFontColor; + buttonTokens.secondaryDefaultBorderColor = theme.button?.baseColor ?? buttonTokens.secondaryDefaultBorderColor; buttonTokens.secondaryHoverDefaultBackgroundColor = - theme?.button?.baseColor ?? buttonTokens.secondaryHoverDefaultBackgroundColor; - buttonTokens.tertiaryDefaultFontColor = theme?.button?.baseColor ?? buttonTokens.tertiaryDefaultFontColor; + theme.button?.baseColor ?? buttonTokens.secondaryHoverDefaultBackgroundColor; + buttonTokens.tertiaryDefaultFontColor = theme.button?.baseColor ?? buttonTokens.tertiaryDefaultFontColor; buttonTokens.primaryHoverDefaultBackgroundColor = - subLightness(8, theme?.button?.baseColor) ?? buttonTokens.primaryHoverDefaultBackgroundColor; + subLightness(8, theme.button?.baseColor) ?? buttonTokens.primaryHoverDefaultBackgroundColor; buttonTokens.primaryActiveDefaultBackgroundColor = - subLightness(18, theme?.button?.baseColor) ?? buttonTokens.primaryActiveDefaultBackgroundColor; + subLightness(18, theme.button?.baseColor) ?? buttonTokens.primaryActiveDefaultBackgroundColor; buttonTokens.secondaryActiveDefaultBackgroundColor = - subLightness(18, theme?.button?.baseColor) ?? buttonTokens.secondaryActiveDefaultBackgroundColor; + subLightness(18, theme.button?.baseColor) ?? buttonTokens.secondaryActiveDefaultBackgroundColor; buttonTokens.tertiaryHoverDefaultBackgroundColor = - addLightness(57, theme?.button?.baseColor) ?? buttonTokens.tertiaryHoverDefaultBackgroundColor; + addLightness(57, theme.button?.baseColor) ?? buttonTokens.tertiaryHoverDefaultBackgroundColor; buttonTokens.tertiaryActiveDefaultBackgroundColor = - addLightness(52, theme?.button?.baseColor) ?? buttonTokens.tertiaryActiveDefaultBackgroundColor; + addLightness(52, theme.button?.baseColor) ?? buttonTokens.tertiaryActiveDefaultBackgroundColor; buttonTokens.primaryDisabledDefaultBackgroundColor = - addLightness(42, theme?.button?.baseColor) ?? buttonTokens.primaryDisabledDefaultBackgroundColor; + addLightness(42, theme.button?.baseColor) ?? buttonTokens.primaryDisabledDefaultBackgroundColor; buttonTokens.primaryDisabledDefaultFontColor = - addLightness(42, theme?.button?.primaryFontColor) ?? buttonTokens.primaryDisabledDefaultFontColor; + addLightness(42, theme.button?.primaryFontColor) ?? buttonTokens.primaryDisabledDefaultFontColor; buttonTokens.secondaryDisabledDefaultBorderColor = - addLightness(42, theme?.button?.baseColor) ?? buttonTokens.secondaryDisabledDefaultBorderColor; + addLightness(42, theme.button?.baseColor) ?? buttonTokens.secondaryDisabledDefaultBorderColor; buttonTokens.secondaryDisabledDefaultFontColor = - addLightness(42, theme?.button?.baseColor) ?? buttonTokens.secondaryDisabledDefaultFontColor; + addLightness(42, theme.button?.baseColor) ?? buttonTokens.secondaryDisabledDefaultFontColor; buttonTokens.tertiaryDisabledDefaultFontColor = - addLightness(42, theme?.button?.baseColor) ?? buttonTokens.tertiaryDisabledDefaultFontColor; + addLightness(42, theme.button?.baseColor) ?? buttonTokens.tertiaryDisabledDefaultFontColor; const checkboxTokens = componentTokensCopy.checkbox; - checkboxTokens.backgroundColorChecked = theme?.checkbox?.baseColor ?? checkboxTokens.backgroundColorChecked; - checkboxTokens.borderColor = theme?.checkbox?.baseColor ?? checkboxTokens.borderColor; - checkboxTokens.checkColor = theme?.checkbox?.checkColor ?? checkboxTokens.checkColor; - checkboxTokens.fontColor = theme?.checkbox?.fontColor ?? checkboxTokens.fontColor; + checkboxTokens.backgroundColorChecked = theme.checkbox?.baseColor ?? checkboxTokens.backgroundColorChecked; + checkboxTokens.borderColor = theme.checkbox?.baseColor ?? checkboxTokens.borderColor; + checkboxTokens.checkColor = theme.checkbox?.checkColor ?? checkboxTokens.checkColor; + checkboxTokens.fontColor = theme.checkbox?.fontColor ?? checkboxTokens.fontColor; checkboxTokens.hoverBackgroundColorChecked = - subLightness(15, theme?.checkbox?.baseColor) ?? checkboxTokens.hoverBackgroundColorChecked; - checkboxTokens.hoverBorderColor = subLightness(15, theme?.checkbox?.baseColor) ?? checkboxTokens.hoverBorderColor; + subLightness(15, theme.checkbox?.baseColor) ?? checkboxTokens.hoverBackgroundColorChecked; + checkboxTokens.hoverBorderColor = subLightness(15, theme.checkbox?.baseColor) ?? checkboxTokens.hoverBorderColor; const chipTokens = componentTokensCopy.chip; - chipTokens.backgroundColor = theme?.chip?.baseColor ?? chipTokens.backgroundColor; - chipTokens.fontColor = theme?.chip?.fontColor ?? chipTokens.fontColor; - chipTokens.iconColor = theme?.chip?.iconColor ?? chipTokens.iconColor; - chipTokens.hoverIconColor = subLightness(10, theme?.chip?.iconColor) ?? chipTokens.hoverIconColor; - chipTokens.activeIconColor = subLightness(30, theme?.chip?.iconColor) ?? chipTokens.activeIconColor; + chipTokens.backgroundColor = theme.chip?.baseColor ?? chipTokens.backgroundColor; + chipTokens.fontColor = theme.chip?.fontColor ?? chipTokens.fontColor; + chipTokens.iconColor = theme.chip?.iconColor ?? chipTokens.iconColor; + chipTokens.hoverIconColor = subLightness(10, theme.chip?.iconColor) ?? chipTokens.hoverIconColor; + chipTokens.activeIconColor = subLightness(30, theme.chip?.iconColor) ?? chipTokens.activeIconColor; const contextualMenuTokens = componentTokensCopy.contextualMenu; contextualMenuTokens.selectedMenuItemBackgroundColor = - theme?.contextualMenu?.accentColor ?? contextualMenuTokens.selectedMenuItemBackgroundColor; + theme.contextualMenu?.accentColor ?? contextualMenuTokens.selectedMenuItemBackgroundColor; contextualMenuTokens.hoverSelectedMenuItemBackgroundColor = - subLightness(5, theme?.contextualMenu?.accentColor) ?? contextualMenuTokens.hoverSelectedMenuItemBackgroundColor; + subLightness(5, theme.contextualMenu?.accentColor) ?? contextualMenuTokens.hoverSelectedMenuItemBackgroundColor; contextualMenuTokens.activeSelectedMenuItemBackgroundColor = - subLightness(5, theme?.contextualMenu?.accentColor) ?? contextualMenuTokens.activeSelectedMenuItemBackgroundColor; - contextualMenuTokens.backgroundColor = theme?.contextualMenu?.baseColor ?? contextualMenuTokens.backgroundColor; + subLightness(5, theme.contextualMenu?.accentColor) ?? contextualMenuTokens.activeSelectedMenuItemBackgroundColor; + contextualMenuTokens.backgroundColor = theme.contextualMenu?.baseColor ?? contextualMenuTokens.backgroundColor; contextualMenuTokens.hoverMenuItemBackgroundColor = - subLightness(5, theme?.contextualMenu?.baseColor) ?? contextualMenuTokens.hoverMenuItemBackgroundColor; + subLightness(5, theme.contextualMenu?.baseColor) ?? contextualMenuTokens.hoverMenuItemBackgroundColor; contextualMenuTokens.activeMenuItemBackgroundColor = - subLightness(5, theme?.contextualMenu?.baseColor) ?? contextualMenuTokens.activeMenuItemBackgroundColor; - contextualMenuTokens.menuItemFontColor = theme?.contextualMenu?.fontColor ?? contextualMenuTokens.menuItemFontColor; + subLightness(5, theme.contextualMenu?.baseColor) ?? contextualMenuTokens.activeMenuItemBackgroundColor; + contextualMenuTokens.menuItemFontColor = theme.contextualMenu?.fontColor ?? contextualMenuTokens.menuItemFontColor; contextualMenuTokens.sectionTitleFontColor = - theme?.contextualMenu?.fontColor ?? contextualMenuTokens.sectionTitleFontColor; - contextualMenuTokens.iconColor = theme?.contextualMenu?.iconColor ?? contextualMenuTokens.iconColor; + theme.contextualMenu?.fontColor ?? contextualMenuTokens.sectionTitleFontColor; + contextualMenuTokens.iconColor = theme.contextualMenu?.iconColor ?? contextualMenuTokens.iconColor; const dataGridTokens = componentTokensCopy.dataGrid; - dataGridTokens.headerBackgroundColor = theme?.dataGrid?.baseColor ?? dataGridTokens.headerBackgroundColor; - dataGridTokens.headerFontColor = theme?.dataGrid?.headerFontColor ?? dataGridTokens.headerFontColor; - dataGridTokens.dataFontColor = theme?.dataGrid?.cellFontColor ?? dataGridTokens.dataFontColor; - dataGridTokens.headerCheckboxCheckColor = theme?.dataGrid?.baseColor ?? dataGridTokens.headerCheckboxCheckColor; - dataGridTokens.actionIconColor = theme?.dataGrid?.baseColor ?? dataGridTokens.actionIconColor; - dataGridTokens.hoverActionIconColor = theme?.dataGrid?.baseColor ?? dataGridTokens.hoverActionIconColor; - dataGridTokens.focusActionIconColor = theme?.dataGrid?.baseColor ?? dataGridTokens.focusActionIconColor; - dataGridTokens.activeActionIconColor = theme?.dataGrid?.baseColor ?? dataGridTokens.activeActionIconColor; + dataGridTokens.headerBackgroundColor = theme.dataGrid?.baseColor ?? dataGridTokens.headerBackgroundColor; + dataGridTokens.headerFontColor = theme.dataGrid?.headerFontColor ?? dataGridTokens.headerFontColor; + dataGridTokens.dataFontColor = theme.dataGrid?.cellFontColor ?? dataGridTokens.dataFontColor; + dataGridTokens.headerCheckboxCheckColor = theme.dataGrid?.baseColor ?? dataGridTokens.headerCheckboxCheckColor; + dataGridTokens.actionIconColor = theme.dataGrid?.baseColor ?? dataGridTokens.actionIconColor; + dataGridTokens.hoverActionIconColor = theme.dataGrid?.baseColor ?? dataGridTokens.hoverActionIconColor; + dataGridTokens.focusActionIconColor = theme.dataGrid?.baseColor ?? dataGridTokens.focusActionIconColor; + dataGridTokens.activeActionIconColor = theme.dataGrid?.baseColor ?? dataGridTokens.activeActionIconColor; const dateTokens = componentTokensCopy.dateInput; - dateTokens.pickerSelectedBackgroundColor = theme?.dateInput?.baseColor ?? dateTokens.pickerSelectedBackgroundColor; - dateTokens.pickerSelectedFontColor = theme?.dateInput?.selectedFontColor ?? dateTokens.pickerSelectedFontColor; + dateTokens.pickerSelectedBackgroundColor = theme.dateInput?.baseColor ?? dateTokens.pickerSelectedBackgroundColor; + dateTokens.pickerSelectedFontColor = theme.dateInput?.selectedFontColor ?? dateTokens.pickerSelectedFontColor; dateTokens.pickerActiveBackgroundColor = - subLightness(8, theme?.dateInput?.baseColor) ?? dateTokens.pickerActiveBackgroundColor; - dateTokens.pickerActiveFontColor = theme?.dateInput?.selectedFontColor ?? dateTokens.pickerActiveFontColor; - dateTokens.pickerCurrentYearFontColor = theme?.dateInput?.baseColor ?? dateTokens.pickerCurrentYearFontColor; + subLightness(8, theme.dateInput?.baseColor) ?? dateTokens.pickerActiveBackgroundColor; + dateTokens.pickerActiveFontColor = theme.dateInput?.selectedFontColor ?? dateTokens.pickerActiveFontColor; + dateTokens.pickerCurrentYearFontColor = theme.dateInput?.baseColor ?? dateTokens.pickerCurrentYearFontColor; dateTokens.pickerHeaderActiveBackgroundColor = - subLightness(8, theme?.dateInput?.baseColor) ?? dateTokens.pickerHeaderActiveBackgroundColor; + subLightness(8, theme.dateInput?.baseColor) ?? dateTokens.pickerHeaderActiveBackgroundColor; dateTokens.pickerHeaderActiveFontColor = - theme?.dateInput?.selectedFontColor ?? dateTokens.pickerHeaderActiveFontColor; + theme.dateInput?.selectedFontColor ?? dateTokens.pickerHeaderActiveFontColor; dateTokens.pickerHoverBackgroundColor = - addLightness(52, theme?.dateInput?.baseColor) ?? dateTokens.pickerHoverBackgroundColor; + addLightness(52, theme.dateInput?.baseColor) ?? dateTokens.pickerHoverBackgroundColor; dateTokens.pickerCurrentDateBorderColor = - addLightness(42, theme?.dateInput?.baseColor) ?? dateTokens.pickerCurrentDateBorderColor; + addLightness(42, theme.dateInput?.baseColor) ?? dateTokens.pickerCurrentDateBorderColor; dateTokens.pickerHeaderHoverBackgroundColor = - addLightness(52, theme?.dateInput?.baseColor) ?? dateTokens.pickerHeaderHoverBackgroundColor; + addLightness(52, theme.dateInput?.baseColor) ?? dateTokens.pickerHeaderHoverBackgroundColor; const dialogTokens = componentTokensCopy.dialog; - dialogTokens.backgroundColor = theme?.dialog?.baseColor ?? dialogTokens.backgroundColor; - dialogTokens.closeIconColor = theme?.dialog?.closeIconColor ?? dialogTokens.closeIconColor; - dialogTokens.overlayColor = theme?.dialog?.overlayColor ?? dialogTokens.overlayColor; + dialogTokens.backgroundColor = theme.dialog?.baseColor ?? dialogTokens.backgroundColor; + dialogTokens.closeIconColor = theme.dialog?.closeIconColor ?? dialogTokens.closeIconColor; + dialogTokens.overlayColor = theme.dialog?.overlayColor ?? dialogTokens.overlayColor; const dropdownTokens = componentTokensCopy.dropdown; - dropdownTokens.buttonBackgroundColor = theme?.dropdown?.baseColor ?? dropdownTokens.buttonBackgroundColor; - dropdownTokens.buttonFontColor = theme?.dropdown?.fontColor ?? dropdownTokens.buttonFontColor; - dropdownTokens.buttonIconColor = theme?.dropdown?.fontColor ?? dropdownTokens.caretIconColor; - dropdownTokens.caretIconColor = theme?.dropdown?.fontColor ?? dropdownTokens.caretIconColor; - dropdownTokens.optionFontColor = theme?.dropdown?.optionFontColor ?? dropdownTokens.optionFontColor; - dropdownTokens.optionIconColor = theme?.dropdown?.optionFontColor ?? dropdownTokens.optionIconColor; + dropdownTokens.buttonBackgroundColor = theme.dropdown?.baseColor ?? dropdownTokens.buttonBackgroundColor; + dropdownTokens.buttonFontColor = theme.dropdown?.fontColor ?? dropdownTokens.buttonFontColor; + dropdownTokens.buttonIconColor = theme.dropdown?.fontColor ?? dropdownTokens.caretIconColor; + dropdownTokens.caretIconColor = theme.dropdown?.fontColor ?? dropdownTokens.caretIconColor; + dropdownTokens.optionFontColor = theme.dropdown?.optionFontColor ?? dropdownTokens.optionFontColor; + dropdownTokens.optionIconColor = theme.dropdown?.optionFontColor ?? dropdownTokens.optionIconColor; dropdownTokens.hoverButtonBackgroundColor = - subLightness(5, theme?.dropdown?.baseColor) ?? dropdownTokens.hoverButtonBackgroundColor; + subLightness(5, theme.dropdown?.baseColor) ?? dropdownTokens.hoverButtonBackgroundColor; dropdownTokens.activeButtonBackgroundColor = - subLightness(12, theme?.dropdown?.baseColor) ?? dropdownTokens.activeButtonBackgroundColor; + subLightness(12, theme.dropdown?.baseColor) ?? dropdownTokens.activeButtonBackgroundColor; dropdownTokens.hoverOptionBackgroundColor = - subLightness(5, theme?.dropdown?.baseColor) ?? dropdownTokens.hoverOptionBackgroundColor; + subLightness(5, theme.dropdown?.baseColor) ?? dropdownTokens.hoverOptionBackgroundColor; dropdownTokens.activeOptionBackgroundColor = - subLightness(20, theme?.dropdown?.baseColor) ?? dropdownTokens.activeOptionBackgroundColor; + subLightness(20, theme.dropdown?.baseColor) ?? dropdownTokens.activeOptionBackgroundColor; const fileInputTokens = componentTokensCopy.fileInput; - fileInputTokens.labelFontColor = theme?.fileInput?.fontColor ?? fileInputTokens.labelFontColor; - fileInputTokens.helperTextFontColor = theme?.fileInput?.fontColor ?? fileInputTokens.helperTextFontColor; - fileInputTokens.dropLabelFontColor = theme?.fileInput?.fontColor ?? fileInputTokens.dropLabelFontColor; - fileInputTokens.fileNameFontColor = theme?.fileInput?.fontColor ?? fileInputTokens.fileNameFontColor; + fileInputTokens.labelFontColor = theme.fileInput?.fontColor ?? fileInputTokens.labelFontColor; + fileInputTokens.helperTextFontColor = theme.fileInput?.fontColor ?? fileInputTokens.helperTextFontColor; + fileInputTokens.dropLabelFontColor = theme.fileInput?.fontColor ?? fileInputTokens.dropLabelFontColor; + fileInputTokens.fileNameFontColor = theme.fileInput?.fontColor ?? fileInputTokens.fileNameFontColor; const footerTokens = componentTokensCopy.footer; - footerTokens.backgroundColor = theme?.footer?.baseColor ?? footerTokens.backgroundColor; - footerTokens.bottomLinksFontColor = theme?.footer?.fontColor ?? footerTokens.bottomLinksFontColor; - footerTokens.copyrightFontColor = theme?.footer?.fontColor ?? footerTokens.copyrightFontColor; - footerTokens.socialLinksColor = theme?.footer?.fontColor ?? footerTokens.socialLinksColor; - footerTokens.bottomLinksDividerColor = theme?.footer?.accentColor ?? footerTokens.bottomLinksDividerColor; - footerTokens.logo = theme?.footer?.logo ?? footerTokens.logo; + footerTokens.backgroundColor = theme.footer?.baseColor ?? footerTokens.backgroundColor; + footerTokens.bottomLinksFontColor = theme.footer?.fontColor ?? footerTokens.bottomLinksFontColor; + footerTokens.copyrightFontColor = theme.footer?.fontColor ?? footerTokens.copyrightFontColor; + footerTokens.socialLinksColor = theme.footer?.fontColor ?? footerTokens.socialLinksColor; + footerTokens.bottomLinksDividerColor = theme.footer?.accentColor ?? footerTokens.bottomLinksDividerColor; + footerTokens.logo = theme.footer?.logo ?? footerTokens.logo; const headerTokens = componentTokensCopy.header; - headerTokens.backgroundColor = theme?.header?.baseColor ?? headerTokens.backgroundColor; - headerTokens.underlinedColor = theme?.header?.accentColor ?? headerTokens.underlinedColor; - headerTokens.menuBackgroundColor = theme?.header?.menuBaseColor ?? headerTokens.menuBackgroundColor; - headerTokens.hamburguerFontColor = theme?.header?.fontColor ?? headerTokens.hamburguerFontColor; - headerTokens.hamburguerIconColor = theme?.header?.hamburguerColor ?? headerTokens.hamburguerIconColor; + headerTokens.backgroundColor = theme.header?.baseColor ?? headerTokens.backgroundColor; + headerTokens.underlinedColor = theme.header?.accentColor ?? headerTokens.underlinedColor; + headerTokens.menuBackgroundColor = theme.header?.menuBaseColor ?? headerTokens.menuBackgroundColor; + headerTokens.hamburguerFontColor = theme.header?.fontColor ?? headerTokens.hamburguerFontColor; + headerTokens.hamburguerIconColor = theme.header?.hamburguerColor ?? headerTokens.hamburguerIconColor; headerTokens.hamburguerHoverColor = - addLightness(90, theme?.header?.hamburguerColor) ?? headerTokens.hamburguerHoverColor; - headerTokens.logo = theme?.header?.logo ?? headerTokens.logo; - headerTokens.logoResponsive = theme?.header?.logoResponsive ?? headerTokens.logoResponsive; - headerTokens.contentColor = theme?.header?.contentColor ?? headerTokens.contentColor; - headerTokens.overlayColor = theme?.header?.overlayColor ?? headerTokens.overlayColor; + addLightness(90, theme.header?.hamburguerColor) ?? headerTokens.hamburguerHoverColor; + headerTokens.logo = theme.header?.logo ?? headerTokens.logo; + headerTokens.logoResponsive = theme.header?.logoResponsive ?? headerTokens.logoResponsive; + headerTokens.contentColor = theme.header?.contentColor ?? headerTokens.contentColor; + headerTokens.overlayColor = theme.header?.overlayColor ?? headerTokens.overlayColor; const linkTokens = componentTokensCopy.link; - linkTokens.visitedFontColor = theme?.link?.baseColor ?? linkTokens.visitedFontColor; - linkTokens.visitedUnderlineColor = theme?.link?.baseColor ?? linkTokens.visitedUnderlineColor; + linkTokens.visitedFontColor = theme.link?.baseColor ?? linkTokens.visitedFontColor; + linkTokens.visitedUnderlineColor = theme.link?.baseColor ?? linkTokens.visitedUnderlineColor; const navTabsTokens = componentTokensCopy.navTabs; - navTabsTokens.selectedFontColor = theme?.navTabs?.baseColor ?? navTabsTokens.selectedFontColor; - navTabsTokens.unselectedFontColor = theme?.navTabs?.baseColor ?? navTabsTokens.selectedFontColor; - navTabsTokens.selectedIconColor = theme?.navTabs?.baseColor ?? navTabsTokens.selectedIconColor; - navTabsTokens.unselectedIconColor = theme?.navTabs?.baseColor ?? navTabsTokens.selectedIconColor; - navTabsTokens.selectedUnderlineColor = theme?.navTabs?.accentColor ?? navTabsTokens.selectedUnderlineColor; + navTabsTokens.selectedFontColor = theme.navTabs?.baseColor ?? navTabsTokens.selectedFontColor; + navTabsTokens.unselectedFontColor = theme.navTabs?.baseColor ?? navTabsTokens.selectedFontColor; + navTabsTokens.selectedIconColor = theme.navTabs?.baseColor ?? navTabsTokens.selectedIconColor; + navTabsTokens.unselectedIconColor = theme.navTabs?.baseColor ?? navTabsTokens.selectedIconColor; + navTabsTokens.selectedUnderlineColor = theme.navTabs?.accentColor ?? navTabsTokens.selectedUnderlineColor; navTabsTokens.hoverBackgroundColor = - addLightness(55, theme?.navTabs?.baseColor) ?? navTabsTokens.hoverBackgroundColor; + addLightness(55, theme.navTabs?.baseColor) ?? navTabsTokens.hoverBackgroundColor; navTabsTokens.pressedBackgroundColor = - addLightness(50, theme?.navTabs?.baseColor) ?? navTabsTokens.pressedBackgroundColor; + addLightness(50, theme.navTabs?.baseColor) ?? navTabsTokens.pressedBackgroundColor; const paginatorTokens = componentTokensCopy.paginator; - paginatorTokens.backgroundColor = theme?.paginator?.baseColor ?? paginatorTokens.backgroundColor; - paginatorTokens.fontColor = theme?.paginator?.fontColor ?? paginatorTokens.fontColor; + paginatorTokens.backgroundColor = theme.paginator?.baseColor ?? paginatorTokens.backgroundColor; + paginatorTokens.fontColor = theme.paginator?.fontColor ?? paginatorTokens.fontColor; const progressBarTokens = componentTokensCopy.progressBar; - progressBarTokens.trackLineColor = theme?.progressBar?.accentColor ?? progressBarTokens.trackLineColor; - progressBarTokens.totalLineColor = theme?.progressBar?.baseColor ?? progressBarTokens.totalLineColor; - progressBarTokens.labelFontColor = theme?.progressBar?.fontColor ?? progressBarTokens.labelFontColor; - progressBarTokens.valueFontColor = theme?.progressBar?.fontColor ?? progressBarTokens.valueFontColor; - progressBarTokens.helperTextFontColor = theme?.progressBar?.fontColor ?? progressBarTokens.helperTextFontColor; - progressBarTokens.overlayColor = theme?.progressBar?.overlayColor ?? progressBarTokens.overlayColor; - progressBarTokens.overlayFontColor = theme?.progressBar?.overlayFontColor ?? progressBarTokens.overlayFontColor; + progressBarTokens.trackLineColor = theme.progressBar?.accentColor ?? progressBarTokens.trackLineColor; + progressBarTokens.totalLineColor = theme.progressBar?.baseColor ?? progressBarTokens.totalLineColor; + progressBarTokens.labelFontColor = theme.progressBar?.fontColor ?? progressBarTokens.labelFontColor; + progressBarTokens.valueFontColor = theme.progressBar?.fontColor ?? progressBarTokens.valueFontColor; + progressBarTokens.helperTextFontColor = theme.progressBar?.fontColor ?? progressBarTokens.helperTextFontColor; + progressBarTokens.overlayColor = theme.progressBar?.overlayColor ?? progressBarTokens.overlayColor; + progressBarTokens.overlayFontColor = theme.progressBar?.overlayFontColor ?? progressBarTokens.overlayFontColor; const quickNavTokens = componentTokensCopy.quickNav; - quickNavTokens.fontColor = theme?.quickNav?.fontColor ?? quickNavTokens.fontColor; - quickNavTokens.hoverFontColor = theme?.quickNav?.accentColor ?? quickNavTokens.hoverFontColor; + quickNavTokens.fontColor = theme.quickNav?.fontColor ?? quickNavTokens.fontColor; + quickNavTokens.hoverFontColor = theme.quickNav?.accentColor ?? quickNavTokens.hoverFontColor; const radioGroupTokens = componentTokensCopy.radioGroup; - radioGroupTokens.radioInputColor = theme?.radioGroup?.baseColor ?? radioGroupTokens.radioInputColor; - radioGroupTokens.labelFontColor = theme?.radioGroup?.fontColor ?? radioGroupTokens.labelFontColor; - radioGroupTokens.helperTextFontColor = theme?.radioGroup?.fontColor ?? radioGroupTokens.helperTextFontColor; - radioGroupTokens.radioInputLabelFontColor = theme?.radioGroup?.fontColor ?? radioGroupTokens.radioInputLabelFontColor; + radioGroupTokens.radioInputColor = theme.radioGroup?.baseColor ?? radioGroupTokens.radioInputColor; + radioGroupTokens.labelFontColor = theme.radioGroup?.fontColor ?? radioGroupTokens.labelFontColor; + radioGroupTokens.helperTextFontColor = theme.radioGroup?.fontColor ?? radioGroupTokens.helperTextFontColor; + radioGroupTokens.radioInputLabelFontColor = theme.radioGroup?.fontColor ?? radioGroupTokens.radioInputLabelFontColor; radioGroupTokens.hoverRadioInputColor = - subLightness(10, theme?.radioGroup?.baseColor) ?? radioGroupTokens.radioInputColor; + subLightness(10, theme.radioGroup?.baseColor) ?? radioGroupTokens.radioInputColor; radioGroupTokens.activeRadioInputColor = - subLightness(25, theme?.radioGroup?.baseColor) ?? radioGroupTokens.radioInputColor; + subLightness(25, theme.radioGroup?.baseColor) ?? radioGroupTokens.radioInputColor; const selectTokens = componentTokensCopy.select; selectTokens.selectedListOptionBackgroundColor = - theme?.select?.selectedOptionBackgroundColor ?? selectTokens.selectedListOptionBackgroundColor; - selectTokens.valueFontColor = theme?.select?.fontColor ?? selectTokens.valueFontColor; - selectTokens.labelFontColor = theme?.select?.fontColor ?? selectTokens.labelFontColor; - selectTokens.helperTextFontColor = theme?.select?.fontColor ?? selectTokens.helperTextFontColor; - selectTokens.listOptionFontColor = theme?.select?.optionFontColor ?? selectTokens.listOptionFontColor; - selectTokens.listOptionIconColor = theme?.select?.optionFontColor ?? selectTokens.listOptionIconColor; - selectTokens.placeholderFontColor = addLightness(30, theme?.select?.fontColor) ?? selectTokens.placeholderFontColor; - selectTokens.collapseIndicatorColor = theme?.select?.fontColor ?? selectTokens.collapseIndicatorColor; - selectTokens.hoverInputBorderColor = theme?.select?.hoverBorderColor ?? selectTokens.hoverInputBorderColor; + theme.select?.selectedOptionBackgroundColor ?? selectTokens.selectedListOptionBackgroundColor; + selectTokens.valueFontColor = theme.select?.fontColor ?? selectTokens.valueFontColor; + selectTokens.labelFontColor = theme.select?.fontColor ?? selectTokens.labelFontColor; + selectTokens.helperTextFontColor = theme.select?.fontColor ?? selectTokens.helperTextFontColor; + selectTokens.listOptionFontColor = theme.select?.optionFontColor ?? selectTokens.listOptionFontColor; + selectTokens.listOptionIconColor = theme.select?.optionFontColor ?? selectTokens.listOptionIconColor; + selectTokens.placeholderFontColor = addLightness(30, theme.select?.fontColor) ?? selectTokens.placeholderFontColor; + selectTokens.collapseIndicatorColor = theme.select?.fontColor ?? selectTokens.collapseIndicatorColor; + selectTokens.hoverInputBorderColor = theme.select?.hoverBorderColor ?? selectTokens.hoverInputBorderColor; selectTokens.selectedHoverListOptionBackgroundColor = - subLightness(5, theme?.select?.selectedOptionBackgroundColor) ?? + subLightness(5, theme.select?.selectedOptionBackgroundColor) ?? selectTokens.selectedHoverListOptionBackgroundColor; selectTokens.selectedActiveListOptionBackgroundColor = - subLightness(15, theme?.select?.selectedOptionBackgroundColor) ?? + subLightness(15, theme.select?.selectedOptionBackgroundColor) ?? selectTokens.selectedActiveListOptionBackgroundColor; const sideNavTokens = componentTokensCopy.sidenav; - sideNavTokens.backgroundColor = theme?.sidenav?.baseColor ?? sideNavTokens.backgroundColor; + sideNavTokens.backgroundColor = theme.sidenav?.baseColor ?? sideNavTokens.backgroundColor; const sliderTokens = componentTokensCopy.slider; - sliderTokens.labelFontColor = theme?.slider?.fontColor ?? sliderTokens.labelFontColor; - sliderTokens.helperTextFontColor = theme?.slider?.fontColor ?? sliderTokens.helperTextFontColor; - sliderTokens.limitValuesFontColor = theme?.slider?.fontColor ?? sliderTokens.limitValuesFontColor; - sliderTokens.thumbBackgroundColor = theme?.slider?.baseColor ?? sliderTokens.thumbBackgroundColor; - sliderTokens.focusThumbBackgroundColor = theme?.slider?.baseColor ?? sliderTokens.focusThumbBackgroundColor; - sliderTokens.tickBackgroundColor = theme?.slider?.baseColor ?? sliderTokens.tickBackgroundColor; - sliderTokens.trackLineColor = theme?.slider?.baseColor ?? sliderTokens.trackLineColor; - sliderTokens.totalLineColor = theme?.slider?.totalLineColor ?? sliderTokens.totalLineColor; + sliderTokens.labelFontColor = theme.slider?.fontColor ?? sliderTokens.labelFontColor; + sliderTokens.helperTextFontColor = theme.slider?.fontColor ?? sliderTokens.helperTextFontColor; + sliderTokens.limitValuesFontColor = theme.slider?.fontColor ?? sliderTokens.limitValuesFontColor; + sliderTokens.thumbBackgroundColor = theme.slider?.baseColor ?? sliderTokens.thumbBackgroundColor; + sliderTokens.focusThumbBackgroundColor = theme.slider?.baseColor ?? sliderTokens.focusThumbBackgroundColor; + sliderTokens.tickBackgroundColor = theme.slider?.baseColor ?? sliderTokens.tickBackgroundColor; + sliderTokens.trackLineColor = theme.slider?.baseColor ?? sliderTokens.trackLineColor; + sliderTokens.totalLineColor = theme.slider?.totalLineColor ?? sliderTokens.totalLineColor; sliderTokens.hoverThumbBackgroundColor = - subLightness(15, theme?.slider?.baseColor) ?? sliderTokens.thumbBackgroundColor; + subLightness(15, theme.slider?.baseColor) ?? sliderTokens.thumbBackgroundColor; sliderTokens.activeThumbBackgroundColor = - subLightness(15, theme?.slider?.baseColor) ?? sliderTokens.thumbBackgroundColor; + subLightness(15, theme.slider?.baseColor) ?? sliderTokens.thumbBackgroundColor; const spinnerTokens = componentTokensCopy.spinner; - spinnerTokens.trackCircleColor = theme?.spinner?.accentColor ?? spinnerTokens.trackCircleColor; - spinnerTokens.totalCircleColor = theme?.spinner?.baseColor ?? spinnerTokens.totalCircleColor; - spinnerTokens.trackCircleColorOverlay = theme?.spinner?.overlayColor ?? spinnerTokens.trackCircleColorOverlay; - spinnerTokens.labelFontColor = theme?.spinner?.fontColor ?? spinnerTokens.labelFontColor; - spinnerTokens.progressValueFontColor = theme?.spinner?.fontColor ?? spinnerTokens.progressValueFontColor; - spinnerTokens.overlayLabelFontColor = theme?.spinner?.overlayFontColor ?? spinnerTokens.overlayLabelFontColor; + spinnerTokens.trackCircleColor = theme.spinner?.accentColor ?? spinnerTokens.trackCircleColor; + spinnerTokens.totalCircleColor = theme.spinner?.baseColor ?? spinnerTokens.totalCircleColor; + spinnerTokens.trackCircleColorOverlay = theme.spinner?.overlayColor ?? spinnerTokens.trackCircleColorOverlay; + spinnerTokens.labelFontColor = theme.spinner?.fontColor ?? spinnerTokens.labelFontColor; + spinnerTokens.progressValueFontColor = theme.spinner?.fontColor ?? spinnerTokens.progressValueFontColor; + spinnerTokens.overlayLabelFontColor = theme.spinner?.overlayFontColor ?? spinnerTokens.overlayLabelFontColor; spinnerTokens.overlayProgressValueFontColor = - theme?.spinner?.overlayFontColor ?? spinnerTokens.overlayProgressValueFontColor; + theme.spinner?.overlayFontColor ?? spinnerTokens.overlayProgressValueFontColor; const switchTokens = componentTokensCopy.switch; switchTokens.checkedTrackBackgroundColor = - theme?.switch?.checkedBaseColor ?? switchTokens.checkedTrackBackgroundColor; - switchTokens.labelFontColor = theme?.switch?.fontColor ?? switchTokens.labelFontColor; + theme.switch?.checkedBaseColor ?? switchTokens.checkedTrackBackgroundColor; + switchTokens.labelFontColor = theme.switch?.fontColor ?? switchTokens.labelFontColor; switchTokens.disabledCheckedTrackBackgroundColor = - addLightness(57, theme?.switch?.checkedBaseColor) ?? switchTokens.disabledCheckedTrackBackgroundColor; + addLightness(57, theme.switch?.checkedBaseColor) ?? switchTokens.disabledCheckedTrackBackgroundColor; const tableTokens = componentTokensCopy.table; - tableTokens.headerBackgroundColor = theme?.table?.baseColor ?? tableTokens.headerBackgroundColor; - tableTokens.headerFontColor = theme?.table?.headerFontColor ?? tableTokens.headerFontColor; - tableTokens.dataFontColor = theme?.table?.cellFontColor ?? tableTokens.dataFontColor; - tableTokens.sortIconColor = theme?.table?.headerFontColor ?? tableTokens.sortIconColor; - tableTokens.actionIconColor = theme?.table?.baseColor ?? tableTokens.actionIconColor; - tableTokens.hoverActionIconColor = theme?.table?.baseColor ?? tableTokens.hoverActionIconColor; - tableTokens.focusActionIconColor = theme?.table?.baseColor ?? tableTokens.focusActionIconColor; - tableTokens.activeActionIconColor = theme?.table?.baseColor ?? tableTokens.activeActionIconColor; + tableTokens.headerBackgroundColor = theme.table?.baseColor ?? tableTokens.headerBackgroundColor; + tableTokens.headerFontColor = theme.table?.headerFontColor ?? tableTokens.headerFontColor; + tableTokens.dataFontColor = theme.table?.cellFontColor ?? tableTokens.dataFontColor; + tableTokens.sortIconColor = theme.table?.headerFontColor ?? tableTokens.sortIconColor; + tableTokens.actionIconColor = theme.table?.baseColor ?? tableTokens.actionIconColor; + tableTokens.hoverActionIconColor = theme.table?.baseColor ?? tableTokens.hoverActionIconColor; + tableTokens.focusActionIconColor = theme.table?.baseColor ?? tableTokens.focusActionIconColor; + tableTokens.activeActionIconColor = theme.table?.baseColor ?? tableTokens.activeActionIconColor; const tabsTokens = componentTokensCopy.tabs; - tabsTokens.selectedFontColor = theme?.tabs?.baseColor ?? tabsTokens.selectedFontColor; - tabsTokens.selectedIconColor = theme?.tabs?.baseColor ?? tabsTokens.selectedIconColor; - tabsTokens.selectedUnderlineColor = theme?.tabs?.baseColor ?? tabsTokens.selectedUnderlineColor; - tabsTokens.focusOutline = theme?.tabs?.baseColor ?? tabsTokens.focusOutline; - tabsTokens.hoverBackgroundColor = addLightness(57, theme?.tabs?.baseColor) ?? tabsTokens.hoverBackgroundColor; - tabsTokens.pressedBackgroundColor = addLightness(52, theme?.tabs?.baseColor) ?? tabsTokens.pressedBackgroundColor; + tabsTokens.selectedFontColor = theme.tabs?.baseColor ?? tabsTokens.selectedFontColor; + tabsTokens.selectedIconColor = theme.tabs?.baseColor ?? tabsTokens.selectedIconColor; + tabsTokens.selectedUnderlineColor = theme.tabs?.baseColor ?? tabsTokens.selectedUnderlineColor; + tabsTokens.focusOutline = theme.tabs?.baseColor ?? tabsTokens.focusOutline; + tabsTokens.hoverBackgroundColor = addLightness(57, theme.tabs?.baseColor) ?? tabsTokens.hoverBackgroundColor; + tabsTokens.pressedBackgroundColor = addLightness(52, theme.tabs?.baseColor) ?? tabsTokens.pressedBackgroundColor; const tagTokens = componentTokensCopy.tag; - tagTokens.fontColor = theme?.tag?.fontColor ?? tagTokens.fontColor; - tagTokens.iconColor = theme?.tag?.iconColor ?? tagTokens.iconColor; + tagTokens.fontColor = theme.tag?.fontColor ?? tagTokens.fontColor; + tagTokens.iconColor = theme.tag?.iconColor ?? tagTokens.iconColor; const textInputTokens = componentTokensCopy.textInput; - textInputTokens.labelFontColor = theme?.textInput?.fontColor ?? textInputTokens.labelFontColor; - textInputTokens.helperTextFontColor = theme?.textInput?.fontColor ?? textInputTokens.helperTextFontColor; - textInputTokens.valueFontColor = theme?.textInput?.fontColor ?? textInputTokens.valueFontColor; - textInputTokens.actionIconColor = theme?.textInput?.fontColor ?? textInputTokens.actionIconColor; - textInputTokens.hoverActionIconColor = theme?.textInput?.fontColor ?? textInputTokens.hoverActionIconColor; - textInputTokens.focusActionIconColor = theme?.textInput?.fontColor ?? textInputTokens.focusActionIconColor; - textInputTokens.activeActionIconColor = theme?.textInput?.fontColor ?? textInputTokens.activeActionIconColor; - textInputTokens.hoverBorderColor = theme?.textInput?.hoverBorderColor ?? textInputTokens.hoverBorderColor; - textInputTokens.suffixColor = addLightness(40, theme?.textInput?.fontColor) ?? textInputTokens.suffixColor; - textInputTokens.prefixColor = addLightness(40, theme?.textInput?.fontColor) ?? textInputTokens.prefixColor; + textInputTokens.labelFontColor = theme.textInput?.fontColor ?? textInputTokens.labelFontColor; + textInputTokens.helperTextFontColor = theme.textInput?.fontColor ?? textInputTokens.helperTextFontColor; + textInputTokens.valueFontColor = theme.textInput?.fontColor ?? textInputTokens.valueFontColor; + textInputTokens.actionIconColor = theme.textInput?.fontColor ?? textInputTokens.actionIconColor; + textInputTokens.hoverActionIconColor = theme.textInput?.fontColor ?? textInputTokens.hoverActionIconColor; + textInputTokens.focusActionIconColor = theme.textInput?.fontColor ?? textInputTokens.focusActionIconColor; + textInputTokens.activeActionIconColor = theme.textInput?.fontColor ?? textInputTokens.activeActionIconColor; + textInputTokens.hoverBorderColor = theme.textInput?.hoverBorderColor ?? textInputTokens.hoverBorderColor; + textInputTokens.suffixColor = addLightness(40, theme.textInput?.fontColor) ?? textInputTokens.suffixColor; + textInputTokens.prefixColor = addLightness(40, theme.textInput?.fontColor) ?? textInputTokens.prefixColor; textInputTokens.placeholderFontColor = - addLightness(30, theme?.textInput?.fontColor) ?? textInputTokens.placeholderFontColor; + addLightness(30, theme.textInput?.fontColor) ?? textInputTokens.placeholderFontColor; const textareaTokens = componentTokensCopy.textarea; - textareaTokens.labelFontColor = theme?.textarea?.fontColor ?? textareaTokens.labelFontColor; - textareaTokens.helperTextFontColor = theme?.textarea?.fontColor ?? textareaTokens.helperTextFontColor; - textareaTokens.valueFontColor = theme?.textarea?.fontColor ?? textareaTokens.valueFontColor; - textareaTokens.hoverBorderColor = theme?.textarea?.hoverBorderColor ?? textareaTokens.hoverBorderColor; + textareaTokens.labelFontColor = theme.textarea?.fontColor ?? textareaTokens.labelFontColor; + textareaTokens.helperTextFontColor = theme.textarea?.fontColor ?? textareaTokens.helperTextFontColor; + textareaTokens.valueFontColor = theme.textarea?.fontColor ?? textareaTokens.valueFontColor; + textareaTokens.hoverBorderColor = theme.textarea?.hoverBorderColor ?? textareaTokens.hoverBorderColor; textareaTokens.placeholderFontColor = - addLightness(30, theme?.textarea?.fontColor) ?? textareaTokens.placeholderFontColor; + addLightness(30, theme.textarea?.fontColor) ?? textareaTokens.placeholderFontColor; const toggleGroupTokens = componentTokensCopy.toggleGroup; toggleGroupTokens.selectedBackgroundColor = - theme?.toggleGroup?.selectedBaseColor ?? toggleGroupTokens.selectedBackgroundColor; - toggleGroupTokens.selectedFontColor = theme?.toggleGroup?.selectedFontColor ?? toggleGroupTokens.selectedFontColor; + theme.toggleGroup?.selectedBaseColor ?? toggleGroupTokens.selectedBackgroundColor; + toggleGroupTokens.selectedFontColor = theme.toggleGroup?.selectedFontColor ?? toggleGroupTokens.selectedFontColor; toggleGroupTokens.unselectedBackgroundColor = - theme?.toggleGroup?.unselectedBaseColor ?? toggleGroupTokens.unselectedBackgroundColor; + theme.toggleGroup?.unselectedBaseColor ?? toggleGroupTokens.unselectedBackgroundColor; toggleGroupTokens.unselectedActiveBackgroundColor = - theme?.toggleGroup?.selectedBaseColor ?? toggleGroupTokens.unselectedActiveBackgroundColor; + theme.toggleGroup?.selectedBaseColor ?? toggleGroupTokens.unselectedActiveBackgroundColor; toggleGroupTokens.unselectedFontColor = - theme?.toggleGroup?.unselectedFontColor ?? toggleGroupTokens.unselectedFontColor; + theme.toggleGroup?.unselectedFontColor ?? toggleGroupTokens.unselectedFontColor; toggleGroupTokens.selectedHoverBackgroundColor = - subLightness(8, theme?.toggleGroup?.selectedBaseColor) ?? toggleGroupTokens.selectedHoverBackgroundColor; + subLightness(8, theme.toggleGroup?.selectedBaseColor) ?? toggleGroupTokens.selectedHoverBackgroundColor; toggleGroupTokens.selectedActiveBackgroundColor = - subLightness(18, theme?.toggleGroup?.selectedBaseColor) ?? toggleGroupTokens.selectedActiveBackgroundColor; + subLightness(18, theme.toggleGroup?.selectedBaseColor) ?? toggleGroupTokens.selectedActiveBackgroundColor; toggleGroupTokens.selectedDisabledBackgroundColor = - addLightness(57, theme?.toggleGroup?.selectedBaseColor) ?? toggleGroupTokens.selectedDisabledBackgroundColor; + addLightness(57, theme.toggleGroup?.selectedBaseColor) ?? toggleGroupTokens.selectedDisabledBackgroundColor; toggleGroupTokens.selectedDisabledFontColor = - addLightness(42, theme?.toggleGroup?.selectedBaseColor) ?? toggleGroupTokens.selectedDisabledFontColor; + addLightness(42, theme.toggleGroup?.selectedBaseColor) ?? toggleGroupTokens.selectedDisabledFontColor; toggleGroupTokens.unselectedHoverBackgroundColor = - subLightness(10, theme?.toggleGroup?.unselectedBaseColor) ?? toggleGroupTokens.unselectedHoverBackgroundColor; + subLightness(10, theme.toggleGroup?.unselectedBaseColor) ?? toggleGroupTokens.unselectedHoverBackgroundColor; const wizardTokens = componentTokensCopy.wizard; - wizardTokens.selectedStepBackgroundColor = theme?.wizard?.baseColor ?? wizardTokens.selectedStepBackgroundColor; - wizardTokens.selectedStepFontColor = theme?.wizard?.selectedStepFontColor ?? wizardTokens.selectedStepFontColor; - wizardTokens.selectedStepBorderColor = theme?.wizard?.baseColor ?? wizardTokens.selectedStepBorderColor; - wizardTokens.visitedLabelFontColor = theme?.wizard?.fontColor ?? wizardTokens.visitedLabelFontColor; - wizardTokens.selectedLabelFontColor = theme?.wizard?.fontColor ?? wizardTokens.selectedLabelFontColor; - wizardTokens.visitedHelperTextFontColor = theme?.wizard?.fontColor ?? wizardTokens.visitedHelperTextFontColor; - wizardTokens.selectedHelperTextFontColor = theme?.wizard?.fontColor ?? wizardTokens.selectedHelperTextFontColor; + wizardTokens.selectedStepBackgroundColor = theme.wizard?.baseColor ?? wizardTokens.selectedStepBackgroundColor; + wizardTokens.selectedStepFontColor = theme.wizard?.selectedStepFontColor ?? wizardTokens.selectedStepFontColor; + wizardTokens.selectedStepBorderColor = theme.wizard?.baseColor ?? wizardTokens.selectedStepBorderColor; + wizardTokens.visitedLabelFontColor = theme.wizard?.fontColor ?? wizardTokens.visitedLabelFontColor; + wizardTokens.selectedLabelFontColor = theme.wizard?.fontColor ?? wizardTokens.selectedLabelFontColor; + wizardTokens.visitedHelperTextFontColor = theme.wizard?.fontColor ?? wizardTokens.visitedHelperTextFontColor; + wizardTokens.selectedHelperTextFontColor = theme.wizard?.fontColor ?? wizardTokens.selectedHelperTextFontColor; wizardTokens.unvisitedStepBorderColor = - addLightness(40, theme?.wizard?.fontColor) ?? wizardTokens.unvisitedStepBorderColor; + addLightness(40, theme.wizard?.fontColor) ?? wizardTokens.unvisitedStepBorderColor; wizardTokens.unvisitedStepFontColor = - addLightness(40, theme?.wizard?.fontColor) ?? wizardTokens.unvisitedStepFontColor; + addLightness(40, theme.wizard?.fontColor) ?? wizardTokens.unvisitedStepFontColor; wizardTokens.unvisitedLabelFontColor = - addLightness(40, theme?.wizard?.fontColor) ?? wizardTokens.unvisitedLabelFontColor; + addLightness(40, theme.wizard?.fontColor) ?? wizardTokens.unvisitedLabelFontColor; wizardTokens.unvisitedHelperTextFontColor = - addLightness(40, theme?.wizard?.fontColor) ?? wizardTokens.unvisitedHelperTextFontColor; + addLightness(40, theme.wizard?.fontColor) ?? wizardTokens.unvisitedHelperTextFontColor; return componentTokensCopy; }; From 8392956df4f221eda64a30fb64b3136b1d98a687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:00:36 +0100 Subject: [PATCH 18/41] Optional chaining fixed --- packages/lib/src/accordion/Accordion.tsx | 2 +- .../lib/src/bulleted-list/BulletedList.tsx | 6 ++-- packages/lib/src/checkbox/Checkbox.tsx | 4 +-- packages/lib/src/data-grid/DataGrid.tsx | 21 +++++------ packages/lib/src/date-input/Calendar.tsx | 9 +++-- packages/lib/src/date-input/DateInput.tsx | 6 ++-- packages/lib/src/date-input/DatePicker.tsx | 12 +++---- packages/lib/src/dialog/Dialog.tsx | 2 +- packages/lib/src/dropdown/Dropdown.tsx | 2 +- packages/lib/src/file-input/FileInput.tsx | 22 ++++++------ packages/lib/src/file-input/FileItem.tsx | 4 +-- packages/lib/src/footer/Footer.tsx | 16 ++++----- packages/lib/src/header/Header.tsx | 35 ++++++++----------- packages/lib/src/image/Image.tsx | 2 +- packages/lib/src/layout/ApplicationLayout.tsx | 6 ++-- 15 files changed, 68 insertions(+), 81 deletions(-) diff --git a/packages/lib/src/accordion/Accordion.tsx b/packages/lib/src/accordion/Accordion.tsx index 11154a6250..085657de5e 100644 --- a/packages/lib/src/accordion/Accordion.tsx +++ b/packages/lib/src/accordion/Accordion.tsx @@ -30,7 +30,7 @@ const DxcAccordion = ({ }; return ( - <ThemeProvider theme={colorsTheme?.accordion}> + <ThemeProvider theme={colorsTheme.accordion}> <AccordionContainer isExpanded={isExpanded ?? innerIsExpanded} margin={margin}> <AccordionHeader> <AccordionTrigger diff --git a/packages/lib/src/bulleted-list/BulletedList.tsx b/packages/lib/src/bulleted-list/BulletedList.tsx index 14c563b54a..3dc377ea3d 100644 --- a/packages/lib/src/bulleted-list/BulletedList.tsx +++ b/packages/lib/src/bulleted-list/BulletedList.tsx @@ -12,7 +12,7 @@ const DxcBulletedList = ({ children, type = "disc", icon = "" }: BulletedListPro const colorsTheme = useTheme(); return ( - <ThemeProvider theme={colorsTheme?.bulletedList}> + <ThemeProvider theme={colorsTheme.bulletedList}> <ListContainer> <DxcFlex direction="column" as={type === "number" ? "ol" : "ul"} gap="0.125rem"> {Children.map(children, (child, index) => ( @@ -20,7 +20,7 @@ const DxcBulletedList = ({ children, type = "disc", icon = "" }: BulletedListPro <GeneralContent> {type === "number" ? ( <Number> - <DxcTypography color={colorsTheme?.bulletedList?.fontColor}>{index + 1}.</DxcTypography> + <DxcTypography color={colorsTheme.bulletedList.fontColor}>{index + 1}.</DxcTypography> </Number> ) : type === "square" ? ( <Bullet> @@ -39,7 +39,7 @@ const DxcBulletedList = ({ children, type = "disc", icon = "" }: BulletedListPro <Disc /> </Bullet> )} - <DxcTypography color={colorsTheme?.bulletedList?.fontColor}>{child}</DxcTypography> + <DxcTypography color={colorsTheme.bulletedList.fontColor}>{child}</DxcTypography> </GeneralContent> </ListItem> ))} diff --git a/packages/lib/src/checkbox/Checkbox.tsx b/packages/lib/src/checkbox/Checkbox.tsx index 22500473f7..283ba8c973 100644 --- a/packages/lib/src/checkbox/Checkbox.tsx +++ b/packages/lib/src/checkbox/Checkbox.tsx @@ -62,7 +62,7 @@ const DxcCheckbox = forwardRef<RefType, CheckboxPropsType>( }; return ( - <ThemeProvider theme={colorsTheme?.checkbox}> + <ThemeProvider theme={colorsTheme.checkbox}> <MainContainer disabled={disabled} readOnly={readOnly} @@ -75,7 +75,7 @@ const DxcCheckbox = forwardRef<RefType, CheckboxPropsType>( {label && ( <LabelContainer id={labelId} disabled={disabled} labelPosition={labelPosition} aria-label={label}> {label} - {optional && ` ${translatedLabels?.formFields?.optionalLabel}`} + {optional && ` ${translatedLabels.formFields.optionalLabel}`} </LabelContainer> )} <ValueInput diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index fa226e199f..d12a013e1b 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -66,7 +66,7 @@ const DxcDataGrid = ({ setSortColumns(newSortColumns); }; - // Proccess columns prop into usable columns based on other props + // Process columns prop into usable columns based on other props const columnsToRender = useMemo(() => { let expectedColumns = columns.map((column) => convertToRDGColumns(column, summaryRow)); if (expandable) { @@ -250,20 +250,15 @@ const DxcDataGrid = ({ sortColumns={sortColumns} onSortColumnsChange={handleSortChange} rowKeyGetter={(row) => (uniqueRowId ? rowKeyGetter(row, uniqueRowId) : "")} - rowHeight={(row) => { - if ( - row.isExpandedChildContent && - typeof row.expandedContentHeight === "number" && - row.expandedContentHeight > 0 - ) { - return row.expandedContentHeight; - } - return colorsTheme?.dataGrid?.dataRowHeight ?? 0; - }} + rowHeight={(row) => + row.isExpandedChildContent && typeof row.expandedContentHeight === "number" && row.expandedContentHeight > 0 + ? row.expandedContentHeight + : (colorsTheme.dataGrid?.dataRowHeight ?? 0) + } selectedRows={selectedRows} bottomSummaryRows={summaryRow ? [summaryRow] : undefined} - headerRowHeight={colorsTheme?.dataGrid?.headerRowHeight} - summaryRowHeight={colorsTheme?.dataGrid?.summaryRowHeight} + headerRowHeight={colorsTheme.dataGrid.headerRowHeight} + summaryRowHeight={colorsTheme.dataGrid.summaryRowHeight} className="fill-grid" /> {showPaginator && ( diff --git a/packages/lib/src/date-input/Calendar.tsx b/packages/lib/src/date-input/Calendar.tsx index fde9b0dd23..a2013ffad9 100644 --- a/packages/lib/src/date-input/Calendar.tsx +++ b/packages/lib/src/date-input/Calendar.tsx @@ -58,12 +58,11 @@ const Calendar = ({ onDaySelect, today, }: CalendarPropsType): JSX.Element => { - const id = useId(); const [dateToFocus, setDateToFocus] = useState(getDateToFocus(selectedDate, innerDate, today)); const [isFocusable, setIsFocusable] = useState(false); - const dayCells = useMemo(() => getDays(innerDate), [innerDate]); + const id = useId(); const translatedLabels = useTranslatedLabels(); - const weekDays = translatedLabels?.calendar?.daysShort ?? []; + const dayCells = useMemo(() => getDays(innerDate), [innerDate]); const onDateClickHandler = (date: DateType) => { const newDate = innerDate.set("month", date.month).set("date", date.day); @@ -170,14 +169,14 @@ const Calendar = ({ return ( <CalendarContainer role="grid"> <CalendarHeaderRow role="row"> - {weekDays.map((weekDay) => ( + {translatedLabels.calendar.daysShort.map((weekDay) => ( <WeekHeaderCell key={weekDay} role="columnheader"> {weekDay} </WeekHeaderCell> ))} </CalendarHeaderRow> <MonthContainer onBlur={handleOnBlur} role="rowgroup"> - {divideDaysIntoWeeks(dayCells, weekDays.length).map((week, rowIndex) => ( + {divideDaysIntoWeeks(dayCells, translatedLabels.calendar.daysShort.length).map((week, rowIndex) => ( <WeekContainer key={`${id}_week_${rowIndex}`} role="row"> {week.map((date) => ( <DayCellButton diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index 93eb0515ba..12bf534bc6 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -118,7 +118,7 @@ const DxcDateInput = forwardRef<RefType, DateInputPropsType>( } const newDate = getDate(newValue, format, lastValidYear, setLastValidYear); const invalidDateMessage = - newValue !== "" && !newDate.isValid() && translatedLabels?.dateInput?.invalidDateErrorMessage; + newValue !== "" && !newDate.isValid() && translatedLabels.dateInput.invalidDateErrorMessage; const callbackParams = { value: newValue, error: inputError || invalidDateMessage || undefined, @@ -138,7 +138,7 @@ const DxcDateInput = forwardRef<RefType, DateInputPropsType>( const handleOnBlur = ({ value: blurValue, error: inputError }: { value: string; error?: string }) => { const date = getDate(blurValue, format, lastValidYear, setLastValidYear); const invalidDateMessage = - blurValue !== "" && !date.isValid() && translatedLabels?.dateInput?.invalidDateErrorMessage; + blurValue !== "" && !date.isValid() && translatedLabels.dateInput.invalidDateErrorMessage; const callbackParams = { value: blurValue, error: inputError || invalidDateMessage || undefined, @@ -233,7 +233,7 @@ const DxcDateInput = forwardRef<RefType, DateInputPropsType>( disabled={disabled} hasHelperText={!!helperText} > - {label} {optional && <OptionalLabel>{translatedLabels?.formFields?.optionalLabel}</OptionalLabel>} + {label} {optional && <OptionalLabel>{translatedLabels.formFields.optionalLabel}</OptionalLabel>} </Label> )} {helperText && <HelperText disabled={disabled}>{helperText}</HelperText>} diff --git a/packages/lib/src/date-input/DatePicker.tsx b/packages/lib/src/date-input/DatePicker.tsx index 8050d135c1..b4d3bec3df 100644 --- a/packages/lib/src/date-input/DatePicker.tsx +++ b/packages/lib/src/date-input/DatePicker.tsx @@ -6,7 +6,7 @@ import Calendar from "./Calendar"; import YearPicker from "./YearPicker"; import useTranslatedLabels from "../utils/useTranslatedLabels"; import DxcIcon from "../icon/Icon"; -import {Tooltip} from "../tooltip/Tooltip"; +import { Tooltip } from "../tooltip/Tooltip"; const today = dayjs(); @@ -33,9 +33,9 @@ const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Elemen return ( <DatePickerContainer id={id}> <PickerHeader> - <Tooltip label={translatedLabels?.calendar?.previousMonthTitle}> + <Tooltip label={translatedLabels.calendar.previousMonthTitle}> <HeaderButton - aria-label={translatedLabels?.calendar?.previousMonthTitle} + aria-label={translatedLabels.calendar.previousMonthTitle} onClick={() => handleMonthChange(innerDate.set("month", innerDate.get("month") - 1))} > <DxcIcon icon="keyboard_arrow_left" /> @@ -46,13 +46,13 @@ const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Elemen onClick={() => setContent((currentContent) => (currentContent === "yearPicker" ? "calendar" : "yearPicker"))} > <HeaderYearTriggerLabel> - {translatedLabels?.calendar?.months?.[innerDate.get("month")]} {innerDate.format("YYYY")} + {translatedLabels.calendar.months[innerDate.get("month")]} {innerDate.format("YYYY")} </HeaderYearTriggerLabel> <DxcIcon icon={content === "yearPicker" ? "arrow_drop_up" : "arrow_drop_down"} /> </HeaderYearTrigger> - <Tooltip label={translatedLabels?.calendar?.nextMonthTitle}> + <Tooltip label={translatedLabels.calendar.nextMonthTitle}> <HeaderButton - aria-label={translatedLabels?.calendar?.nextMonthTitle} + aria-label={translatedLabels.calendar.nextMonthTitle} onClick={() => handleMonthChange(innerDate.set("month", innerDate.get("month") + 1))} > <DxcIcon icon="keyboard_arrow_right" /> diff --git a/packages/lib/src/dialog/Dialog.tsx b/packages/lib/src/dialog/Dialog.tsx index be2d8ea88a..2c1fc670f5 100644 --- a/packages/lib/src/dialog/Dialog.tsx +++ b/packages/lib/src/dialog/Dialog.tsx @@ -127,7 +127,7 @@ const DxcDialog = ({ onClick={() => { onCloseClick?.(); }} - aria-label={translatedLabels?.dialog?.closeIconAriaLabel} + aria-label={translatedLabels.dialog.closeIconAriaLabel} tabIndex={tabIndex} > <DxcIcon icon="close" /> diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index 6d52957299..23969b1489 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -151,7 +151,7 @@ const DxcDropdown = ({ }, [visualFocusIndex]); return ( - <ThemeProvider theme={colorsTheme?.dropdown}> + <ThemeProvider theme={colorsTheme.dropdown}> <DropdownContainer onMouseEnter={!disabled && expandOnHover ? handleOnOpenMenu : undefined} onMouseLeave={!disabled && expandOnHover ? handleOnCloseMenu : undefined} diff --git a/packages/lib/src/file-input/FileInput.tsx b/packages/lib/src/file-input/FileInput.tsx index c935004603..29bc7402f0 100644 --- a/packages/lib/src/file-input/FileInput.tsx +++ b/packages/lib/src/file-input/FileInput.tsx @@ -61,8 +61,8 @@ const DxcFileInput = forwardRef<RefType, FileInputPropsType>( const translatedLabels = useTranslatedLabels(); const checkFileSize = (file: File) => { - if (minSize && file.size < minSize) return translatedLabels?.fileInput?.fileSizeGreaterThanErrorMessage; - else if (maxSize && file.size > maxSize) return translatedLabels?.fileInput?.fileSizeLessThanErrorMessage; + if (minSize && file.size < minSize) return translatedLabels.fileInput.fileSizeGreaterThanErrorMessage; + else if (maxSize && file.size > maxSize) return translatedLabels.fileInput.fileSizeLessThanErrorMessage; }; const getFilesToAdd = async (selectedFiles: File[]) => { @@ -123,7 +123,7 @@ const DxcFileInput = forwardRef<RefType, FileInputPropsType>( } }; const handleDragOut = (e: DragEvent<HTMLDivElement>) => { - // only if dragged items leave container (outside, not to childs) + // only if dragged items leave container (outside, not to children) if (!e.currentTarget?.contains(e.relatedTarget as HTMLDivElement)) { setIsDragging(false); } @@ -158,7 +158,7 @@ const DxcFileInput = forwardRef<RefType, FileInputPropsType>( }, [value]); return ( - <ThemeProvider theme={colorsTheme?.fileInput}> + <ThemeProvider theme={colorsTheme.fileInput}> <FileInputContainer margin={margin} ref={ref}> <Label htmlFor={fileInputId} disabled={disabled}> {label} @@ -180,8 +180,8 @@ const DxcFileInput = forwardRef<RefType, FileInputPropsType>( label={ buttonLabel ?? (multiple - ? translatedLabels?.fileInput?.multipleButtonLabelDefault - : translatedLabels?.fileInput?.singleButtonLabelDefault) + ? translatedLabels.fileInput.multipleButtonLabelDefault + : translatedLabels.fileInput.singleButtonLabelDefault) } onClick={handleClick} disabled={disabled} @@ -228,7 +228,7 @@ const DxcFileInput = forwardRef<RefType, FileInputPropsType>( > <DxcButton mode="secondary" - label={buttonLabel ?? translatedLabels?.fileInput?.dropAreaButtonLabelDefault} + label={buttonLabel ?? translatedLabels.fileInput.dropAreaButtonLabelDefault} onClick={handleClick} disabled={disabled} size={{ width: "fitContent" }} @@ -237,15 +237,15 @@ const DxcFileInput = forwardRef<RefType, FileInputPropsType>( <DropzoneLabel disabled={disabled}> {dropAreaLabel ?? (multiple - ? translatedLabels?.fileInput?.multipleDropAreaLabelDefault - : translatedLabels?.fileInput?.singleDropAreaLabelDefault)} + ? translatedLabels.fileInput.multipleDropAreaLabelDefault + : translatedLabels.fileInput.singleDropAreaLabelDefault)} </DropzoneLabel> ) : ( <FiledropLabel disabled={disabled}> {dropAreaLabel ?? (multiple - ? translatedLabels?.fileInput?.multipleDropAreaLabelDefault - : translatedLabels?.fileInput?.singleDropAreaLabelDefault)} + ? translatedLabels.fileInput.multipleDropAreaLabelDefault + : translatedLabels.fileInput.singleDropAreaLabelDefault)} </FiledropLabel> )} </DragDropArea> diff --git a/packages/lib/src/file-input/FileItem.tsx b/packages/lib/src/file-input/FileItem.tsx index cd3bda581b..19b9364eb6 100644 --- a/packages/lib/src/file-input/FileItem.tsx +++ b/packages/lib/src/file-input/FileItem.tsx @@ -23,7 +23,7 @@ const FileItem = ({ const getIconAriaLabel = () => (type?.includes("video") ? "video" : type?.includes("audio") ? "audio" : "file"); return ( - <ThemeProvider theme={colorsTheme?.fileInput}> + <ThemeProvider theme={colorsTheme.fileInput}> <MainContainer error={error} singleFileMode={singleFileMode} showPreview={showPreview}> {showPreview && (type?.includes("image") ? ( @@ -45,7 +45,7 @@ const FileItem = ({ onClick={() => onDelete(fileName)} icon="close" tabIndex={tabIndex} - title={translatedLabels?.fileInput?.deleteFileActionTitle ?? ""} + title={translatedLabels.fileInput.deleteFileActionTitle} /> </DxcFlex> {error && !singleFileMode && ( diff --git a/packages/lib/src/footer/Footer.tsx b/packages/lib/src/footer/Footer.tsx index 5304f8a4e3..b944204bb6 100644 --- a/packages/lib/src/footer/Footer.tsx +++ b/packages/lib/src/footer/Footer.tsx @@ -24,27 +24,27 @@ const DxcFooter = ({ const footerLogo = useMemo( () => - !colorsTheme?.footer?.logo ? ( + !colorsTheme.footer.logo ? ( mode === "default" ? ( dxcLogo ) : ( dxcSmallLogo ) - ) : typeof colorsTheme?.footer?.logo === "string" ? ( - <LogoImg mode={mode} alt={translatedLabels?.formFields?.logoAlternativeText} src={colorsTheme?.footer?.logo} /> + ) : typeof colorsTheme.footer.logo === "string" ? ( + <LogoImg mode={mode} alt={translatedLabels.formFields.logoAlternativeText} src={colorsTheme.footer.logo} /> ) : ( - colorsTheme?.footer?.logo + colorsTheme.footer.logo ), - [colorsTheme] + [colorsTheme, translatedLabels] ); return ( - <ThemeProvider theme={colorsTheme?.footer}> + <ThemeProvider theme={colorsTheme.footer}> <FooterContainer margin={margin} mode={mode}> <DxcFlex justifyContent="space-between" alignItems="center" wrap="wrap" gap="1.5rem"> <LogoContainer mode={mode}>{footerLogo}</LogoContainer> {mode === "default" && ( - <DxcFlex gap={colorsTheme?.footer?.socialLinksGutter as CoreSpacingTokensType}> + <DxcFlex gap={colorsTheme.footer.socialLinksGutter as CoreSpacingTokensType}> {socialLinks?.map((link, index) => ( <Tooltip label={link.title} key={`social${index}${link.href}`}> <SocialAnchor @@ -75,7 +75,7 @@ const DxcFooter = ({ </span> ))} </BottomLinks> - <Copyright>{copyright || translatedLabels?.footer?.copyrightText?.(new Date().getFullYear())}</Copyright> + <Copyright>{copyright ?? translatedLabels.footer.copyrightText(new Date().getFullYear())}</Copyright> </BottomContainer> )} </FooterContainer> diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx index 19da4eb9c9..7008400762 100644 --- a/packages/lib/src/header/Header.tsx +++ b/packages/lib/src/header/Header.tsx @@ -62,16 +62,11 @@ const DxcHeader = ({ margin, tabIndex = 0, }: HeaderPropsType): JSX.Element => { + const [isResponsive, setIsResponsive] = useState(false); + const [isMenuVisible, setIsMenuVisible] = useState(false); const colorsTheme = useTheme(); const translatedLabels = useTranslatedLabels(); - const ref = useRef(null); - const [isResponsive, setIsResponsive] = useState(false); - const [isMenuVisible, setIsMenuVisible] = useState(false); - - const handleResize = useCallback(() => { - setIsResponsive(window.matchMedia(`(max-width: ${responsiveSizes.medium}rem)`).matches); - }, []); const handleMenu = () => { if (isResponsive && !isMenuVisible) { @@ -82,22 +77,26 @@ const DxcHeader = ({ }; const headerLogo = useMemo( - () => getLogoElement(colorsTheme?.header?.logo, translatedLabels?.formFields?.logoAlternativeText), - [colorsTheme?.header?.logo] + () => getLogoElement(colorsTheme.header.logo, translatedLabels.formFields.logoAlternativeText), + [colorsTheme, translatedLabels] ); const headerResponsiveLogo = useMemo( - () => getLogoElement(colorsTheme?.header?.logoResponsive, translatedLabels?.formFields?.logoAlternativeText), - [colorsTheme?.header?.logoResponsive] + () => getLogoElement(colorsTheme.header?.logoResponsive, translatedLabels.formFields.logoAlternativeText), + [colorsTheme, translatedLabels] ); useEffect(() => { + const handleResize = () => { + setIsResponsive(window.matchMedia(`(max-width: ${responsiveSizes.medium}rem)`).matches); + }; + handleResize(); window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; - }, [handleResize]); + }, []); useEffect(() => { if (!isResponsive) { @@ -106,7 +105,7 @@ const DxcHeader = ({ }, [isResponsive]); return ( - <ThemeProvider theme={colorsTheme?.header}> + <ThemeProvider theme={colorsTheme.header}> <HeaderContainer underlined={underlined} margin={margin} ref={ref}> <LogoAnchor tabIndex={onClick ? tabIndex : -1} interactive={!!onClick} onClick={onClick}> <LogoContainer>{headerLogo}</LogoContainer> @@ -116,18 +115,14 @@ const DxcHeader = ({ <ChildContainer> <HamburgerTrigger tabIndex={tabIndex} onClick={handleMenu} aria-label="Show options"> <DxcIcon icon="menu" /> - {translatedLabels?.header?.hamburguerTitle} + {translatedLabels.header.hamburguerTitle} </HamburgerTrigger> </ChildContainer> <ResponsiveMenu hasVisibility={isMenuVisible}> <DxcFlex justifyContent="space-between" alignItems="center"> <ResponsiveLogoContainer>{headerResponsiveLogo}</ResponsiveLogoContainer> - <Tooltip label={translatedLabels?.header?.closeIcon}> - <CloseAction - tabIndex={tabIndex} - onClick={handleMenu} - aria-label={translatedLabels?.header?.closeIcon} - > + <Tooltip label={translatedLabels.header.closeIcon}> + <CloseAction tabIndex={tabIndex} onClick={handleMenu} aria-label={translatedLabels.header.closeIcon}> <DxcIcon icon="close" /> </CloseAction> </Tooltip> diff --git a/packages/lib/src/image/Image.tsx b/packages/lib/src/image/Image.tsx index 2ef5881891..07c592b279 100644 --- a/packages/lib/src/image/Image.tsx +++ b/packages/lib/src/image/Image.tsx @@ -52,7 +52,7 @@ export default function DxcImage({ ); return ( - <ThemeProvider theme={colorsTheme?.image}> + <ThemeProvider theme={colorsTheme.image}> <CaptionWrapper condition={caption !== undefined} wrapper={figureWrapper}> <img alt={alt} diff --git a/packages/lib/src/layout/ApplicationLayout.tsx b/packages/lib/src/layout/ApplicationLayout.tsx index b14de75279..a4856932bf 100644 --- a/packages/lib/src/layout/ApplicationLayout.tsx +++ b/packages/lib/src/layout/ApplicationLayout.tsx @@ -142,12 +142,10 @@ const DxcApplicationLayout = ({ <HeaderContainer>{header ?? <DxcHeader underlined />}</HeaderContainer> {sidenav && isResponsive && ( <VisibilityToggle> - <Tooltip label={translatedLabels?.applicationLayout?.visibilityToggleTitle}> + <Tooltip label={translatedLabels.applicationLayout.visibilityToggleTitle}> <HamburgerTrigger onClick={handleSidenavVisibility} - aria-label={ - visibilityToggleLabel ? undefined : translatedLabels?.applicationLayout?.visibilityToggleTitle - } + aria-label={visibilityToggleLabel ? undefined : translatedLabels.applicationLayout.visibilityToggleTitle} > <DxcIcon icon="Menu" /> {visibilityToggleLabel} From 84f34e0291d44f8cef3b0f08d1a3c0d25c0c05cd Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Tue, 17 Dec 2024 17:03:00 +0100 Subject: [PATCH 19/41] Refactorized some of the stories --- .../components/link/code/examples/nextLink.ts | 6 +- .../nav-tabs/code/examples/nextLink.ts | 6 +- .../AccordionGroup.stories.tsx | 11 ++- .../lib/src/accordion/Accordion.stories.tsx | 11 ++- .../src/action-icon/ActionIcon.stories.tsx | 29 +++--- packages/lib/src/alert/Alert.stories.tsx | 35 +++++-- packages/lib/src/badge/Badge.stories.tsx | 35 ++++--- packages/lib/src/bleed/Bleed.stories.tsx | 35 ++++--- .../src/breadcrumbs/Breadcrumbs.stories.tsx | 17 ++-- .../bulleted-list/BulletedList.stories.tsx | 17 +++- packages/lib/src/button/Button.stories.tsx | 35 ++++--- packages/lib/src/card/Card.stories.tsx | 29 +++--- .../lib/src/checkbox/Checkbox.stories.tsx | 52 +++++----- packages/lib/src/chip/Chip.stories.tsx | 29 ++++-- .../lib/src/container/Container.stories.tsx | 95 ++++++++++--------- .../ContextualMenu.stories.tsx | 27 ++++-- .../lib/src/date-input/DateInput.stories.tsx | 2 - packages/lib/src/nav-tabs/Tab.tsx | 3 +- packages/lib/src/typography/Typography.tsx | 2 +- 19 files changed, 301 insertions(+), 175 deletions(-) diff --git a/apps/website/screens/components/link/code/examples/nextLink.ts b/apps/website/screens/components/link/code/examples/nextLink.ts index 48b41b6a50..24bf29fc86 100644 --- a/apps/website/screens/components/link/code/examples/nextLink.ts +++ b/apps/website/screens/components/link/code/examples/nextLink.ts @@ -1,9 +1,9 @@ import { DxcLink, DxcInset } from "@dxc-technology/halstack-react"; import Link from "next/link"; -import React from "react"; +import { forwardRef } from "react"; const code = `() => { - const CustomLink = React.forwardRef( + const CustomLink = forwardRef( ({ onClick, href, children, ...other }, ref) => { return ( <DxcLink {...other} href={href} onClick={onClick} ref={ref}> @@ -26,7 +26,7 @@ const code = `() => { const scope = { DxcLink, Link, - React, + forwardRef, DxcInset, }; diff --git a/apps/website/screens/components/nav-tabs/code/examples/nextLink.ts b/apps/website/screens/components/nav-tabs/code/examples/nextLink.ts index 3aacd3eb17..c14749fc86 100644 --- a/apps/website/screens/components/nav-tabs/code/examples/nextLink.ts +++ b/apps/website/screens/components/nav-tabs/code/examples/nextLink.ts @@ -1,9 +1,9 @@ import { DxcNavTabs, DxcInset } from "@dxc-technology/halstack-react"; import Link from "next/link"; -import React from "react"; +import { forwardRef } from "react"; const code = `() => { - const CustomNavTab = React.forwardRef(({ href, children, ...other }, ref) => { + const CustomNavTab = forwardRef(({ href, children, ...other }, ref) => { return ( <DxcNavTabs.Tab {...other} href={href}> {children} @@ -32,7 +32,7 @@ const scope = { DxcNavTabs, DxcInset, Link, - React, + forwardRef, }; export default { code, scope }; diff --git a/packages/lib/src/accordion-group/AccordionGroup.stories.tsx b/packages/lib/src/accordion-group/AccordionGroup.stories.tsx index 220babc058..2f69892fb2 100644 --- a/packages/lib/src/accordion-group/AccordionGroup.stories.tsx +++ b/packages/lib/src/accordion-group/AccordionGroup.stories.tsx @@ -2,13 +2,14 @@ import DxcAccordionGroup from "./AccordionGroup"; import DxcInset from "../inset/Inset"; import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; +import { Meta, StoryObj } from "@storybook/react/*"; export default { title: "Accordion Group", component: DxcAccordionGroup, -}; +} as Meta<typeof DxcAccordionGroup>; -export const Chromatic = () => ( +const AccordionGroup = () => ( <> <Title title="States" theme="light" level={2} /> <ExampleContainer> @@ -249,3 +250,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcAccordionGroup>; + +export const Chromatic: Story = { + render: AccordionGroup, +}; diff --git a/packages/lib/src/accordion/Accordion.stories.tsx b/packages/lib/src/accordion/Accordion.stories.tsx index 9afb46dc17..4e340b1138 100644 --- a/packages/lib/src/accordion/Accordion.stories.tsx +++ b/packages/lib/src/accordion/Accordion.stories.tsx @@ -2,11 +2,12 @@ import DxcAccordion from "./Accordion"; import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import { HalstackProvider } from "../HalstackContext"; +import { Meta, StoryObj } from "@storybook/react/*"; export default { title: "Accordion", component: DxcAccordion, -}; +} as Meta<typeof DxcAccordion>; const smallIcon = ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" height="20" width="20"> @@ -45,7 +46,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Accordion = () => ( <> <Title title="Component anatomy" theme="light" level={2} /> <ExampleContainer> @@ -238,3 +239,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcAccordion>; + +export const Chromatic: Story = { + render: Accordion, +}; diff --git a/packages/lib/src/action-icon/ActionIcon.stories.tsx b/packages/lib/src/action-icon/ActionIcon.stories.tsx index 6442585a17..c8d7731ac4 100644 --- a/packages/lib/src/action-icon/ActionIcon.stories.tsx +++ b/packages/lib/src/action-icon/ActionIcon.stories.tsx @@ -4,11 +4,12 @@ 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 ", component: DxcActionIcon, -}; +} as Meta<typeof DxcActionIcon>; const iconSVG = ( <svg width="24px" height="24px" viewBox="0 0 24 24" fill="currentColor"> @@ -64,16 +65,22 @@ const NestedTooltip = () => ( </> ); -export const ActionIconTooltip = Tooltip.bind({}); -ActionIconTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +type Story = StoryObj<typeof DxcActionIcon>; + +export const ActionIconTooltip: Story = { + render: Tooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; -export const NestedActionIconTooltip = NestedTooltip.bind({}); -NestedActionIconTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +export const NestedActionIconTooltip: Story = { + render: NestedTooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; diff --git a/packages/lib/src/alert/Alert.stories.tsx b/packages/lib/src/alert/Alert.stories.tsx index c37ea092b3..0c2f2a2810 100644 --- a/packages/lib/src/alert/Alert.stories.tsx +++ b/packages/lib/src/alert/Alert.stories.tsx @@ -2,11 +2,12 @@ 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", component: DxcAlert, -}; +} as Meta<typeof DxcAlert>; const messages = [ { text: "Message 1", onClose: () => {} }, @@ -46,7 +47,7 @@ const message = { onClose: () => {}, }; -export const Chromatic = () => ( +const Alert = () => ( <> <Title title="Banner" theme="light" level={2} /> <ExampleContainer> @@ -287,7 +288,7 @@ export const Chromatic = () => ( </> ); -export const InformationModal = () => ( +const AlertInfo = () => ( <ExampleContainer> <DxcAlert title="Info" @@ -299,7 +300,7 @@ export const InformationModal = () => ( </ExampleContainer> ); -export const SuccessModal = () => ( +const AlertSuccess = () => ( <ExampleContainer> <DxcAlert title="Success" @@ -313,7 +314,7 @@ export const SuccessModal = () => ( </ExampleContainer> ); -export const WarningModal = () => ( +const AlertWarning = () => ( <ExampleContainer> <DxcAlert title="Warning" @@ -326,7 +327,7 @@ export const WarningModal = () => ( </ExampleContainer> ); -export const ErrorModal = () => ( +const AlertError = () => ( <ExampleContainer> <DxcAlert title="Error" @@ -338,3 +339,25 @@ export const ErrorModal = () => ( /> </ExampleContainer> ); + +type Story = StoryObj<typeof DxcAlert>; + +export const Chromatic: Story = { + render: Alert, +}; + +export const InformationModal: Story = { + render: AlertInfo, +}; + +export const SuccessModal: Story = { + render: AlertSuccess, +}; + +export const WarningModal: Story = { + render: AlertWarning, +}; + +export const ErrorModal: Story = { + render: AlertError, +}; diff --git a/packages/lib/src/badge/Badge.stories.tsx b/packages/lib/src/badge/Badge.stories.tsx index 7c36f3451c..a2cd402b59 100644 --- a/packages/lib/src/badge/Badge.stories.tsx +++ b/packages/lib/src/badge/Badge.stories.tsx @@ -5,11 +5,12 @@ 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", component: DxcBadge, -}; +} as Meta<typeof DxcBadge>; const icon = ( <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor"> @@ -19,7 +20,7 @@ const icon = ( </svg> ); -export const Chromatic = () => ( +const Badge = () => ( <> <Title title="Notification" theme="light" level={2} /> <ExampleContainer> @@ -233,16 +234,26 @@ const NestedTooltip = () => ( </> ); -export const BadgeTooltip = Tooltip.bind({}); -BadgeTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const div = canvas.getByText("Tooltip label"); - await userEvent.hover(div); +type Story = StoryObj<typeof DxcBadge>; + +export const Chromatic: Story = { + render: Badge, +}; + +export const BadgeTooltip: Story = { + render: Tooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const div = canvas.getByText("Tooltip label"); + await userEvent.hover(div); + }, }; -export const NestedBadgeTooltip = NestedTooltip.bind({}); -NestedBadgeTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const div = canvas.getByText("Tooltip label"); - await userEvent.hover(div); +export const NestedBadgeTooltip: Story = { + render: NestedTooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const div = canvas.getByText("Tooltip label"); + await userEvent.hover(div); + }, }; diff --git a/packages/lib/src/bleed/Bleed.stories.tsx b/packages/lib/src/bleed/Bleed.stories.tsx index e39d19f004..9e41eeea91 100644 --- a/packages/lib/src/bleed/Bleed.stories.tsx +++ b/packages/lib/src/bleed/Bleed.stories.tsx @@ -2,13 +2,28 @@ import styled from "styled-components"; import Title from "../../.storybook/components/Title"; import DxcBleed from "./Bleed"; import DxcFlex from "../flex/Flex"; +import { Meta, StoryObj } from "@storybook/react/*"; export default { title: "Bleed", component: DxcBleed, -}; +} as Meta<typeof DxcBleed>; + +const Container = styled.div` + background: #f2eafa; + padding: 5rem; + margin: 2.5rem; +`; + +const Placeholder = styled.div` + min-height: 40px; + min-width: 120px; + border: 1px solid #a46ede; + border-radius: 0.5rem; + background-color: #e5d5f6; +`; -export const Chromatic = () => ( +const Bleed = () => ( <> <Title title="Space = none" theme="light" level={4} /> <Container> @@ -325,16 +340,8 @@ export const Chromatic = () => ( </> ); -const Container = styled.div` - background: #f2eafa; - padding: 5rem; - margin: 2.5rem; -`; +type Story = StoryObj<typeof DxcBleed>; -const Placeholder = styled.div` - min-height: 40px; - min-width: 120px; - border: 1px solid #a46ede; - border-radius: 0.5rem; - background-color: #e5d5f6; -`; +export const Chromatic: Story = { + render: Bleed, +}; diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index 046652b0c7..a7d29ebd0a 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -6,6 +6,7 @@ import { HalstackProvider } from "../HalstackContext"; import { userEvent, within } from "@storybook/test"; import { disabledRules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules"; import preview from "../../.storybook/preview"; +import { Meta, StoryObj } from "@storybook/react/*"; export default { title: "Breadcrumbs", @@ -20,7 +21,7 @@ export default { }, }, }, -}; +} as Meta<typeof DxcBreadcrumbs>; const items = [ { @@ -185,9 +186,13 @@ const Breadcrumbs = () => ( </> ); -export const Chromatic = Breadcrumbs.bind({}); -Chromatic.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const dropdowns = canvas.getAllByRole("button"); - await userEvent.click(dropdowns[2]); +type Story = StoryObj<typeof DxcBreadcrumbs>; + +export const Chromatic: Story = { + render: Breadcrumbs, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const dropdowns = canvas.getAllByRole("button"); + await userEvent.click(dropdowns[2]); + }, }; diff --git a/packages/lib/src/bulleted-list/BulletedList.stories.tsx b/packages/lib/src/bulleted-list/BulletedList.stories.tsx index f15879af9f..d07508fcf3 100644 --- a/packages/lib/src/bulleted-list/BulletedList.stories.tsx +++ b/packages/lib/src/bulleted-list/BulletedList.stories.tsx @@ -2,11 +2,12 @@ import styled from "styled-components"; 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", component: DxcBulletedList, -}; +} as Meta<typeof DxcBulletedList>; const icon = ( <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor"> @@ -15,7 +16,11 @@ const icon = ( </svg> ); -export const Chromatic = () => ( +const Container = styled.div` + width: 400px; +`; + +const BulletedList = () => ( <> <ExampleContainer> <Title title="Icon list (SVG)" level={4} /> @@ -109,6 +114,8 @@ export const Chromatic = () => ( </> ); -const Container = styled.div` - width: 400px; -`; +type Story = StoryObj<typeof DxcBulletedList>; + +export const Chromatic: Story = { + render: BulletedList, +}; diff --git a/packages/lib/src/button/Button.stories.tsx b/packages/lib/src/button/Button.stories.tsx index 0067952c5d..4b9283563e 100644 --- a/packages/lib/src/button/Button.stories.tsx +++ b/packages/lib/src/button/Button.stories.tsx @@ -6,11 +6,12 @@ import { HalstackProvider } from "../HalstackContext"; 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", component: DxcButton, -}; +} as Meta<typeof DxcButton>; const facebookIcon = ( <svg @@ -43,7 +44,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Button = () => ( <> <> <Title title="Default" theme="light" level={2} /> @@ -4988,16 +4989,26 @@ const NestedTooltip = () => ( </> ); -export const ButtonTooltip = Tooltip.bind({}); -ButtonTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +type Story = StoryObj<typeof DxcButton>; + +export const Chromatic: Story = { + render: Button, +}; + +export const ButtonTooltip: Story = { + render: Tooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; -export const NestedButtonTooltip = NestedTooltip.bind({}); -NestedButtonTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +export const NestedButtonTooltip: Story = { + render: NestedTooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; diff --git a/packages/lib/src/card/Card.stories.tsx b/packages/lib/src/card/Card.stories.tsx index 433f6dbdd7..599b89b0c1 100644 --- a/packages/lib/src/card/Card.stories.tsx +++ b/packages/lib/src/card/Card.stories.tsx @@ -2,11 +2,12 @@ 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", component: DxcCard, -}; +} as Meta<typeof DxcCard>; const Card = () => ( <> @@ -155,16 +156,22 @@ const linkStates = async (focusCard, hoverCard) => { await userEvent.hover(hoverCard); }; -export const ActionCardStates = actionCard.bind({}); -ActionCardStates.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.tab(); - await userEvent.hover(canvas.getAllByText("Hovered default with action")[1]); +type Story = StoryObj<typeof DxcCard>; + +export const ActionCardStates: Story = { + render: actionCard, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.tab(); + await userEvent.hover(canvas.getAllByText("Hovered default with action")[1]); + }, }; -export const Chromatic = Card.bind({}); -Chromatic.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const linkCards = canvas.getAllByRole("link"); - await linkStates(linkCards[1], linkCards[2]); +export const Chromatic: Story = { + render: Card, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const linkCards = canvas.getAllByRole("link"); + await linkStates(linkCards[1], linkCards[2]); + }, }; diff --git a/packages/lib/src/checkbox/Checkbox.stories.tsx b/packages/lib/src/checkbox/Checkbox.stories.tsx index 70d3cdaebe..4cf3b80897 100644 --- a/packages/lib/src/checkbox/Checkbox.stories.tsx +++ b/packages/lib/src/checkbox/Checkbox.stories.tsx @@ -3,11 +3,12 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcCheckbox from "./Checkbox"; +import { Meta, StoryObj } from "@storybook/react/*"; export default { title: "Checkbox", component: DxcCheckbox, -}; +} as Meta<typeof DxcCheckbox>;; const opinionatedTheme = { checkbox: { @@ -17,6 +18,25 @@ const opinionatedTheme = { }, }; +const ScrollableContainer = styled.div` + display: flex; + flex-direction: column; + gap: 14px; + width: 200px; + height: 200px; + border: 1px solid #000; + padding: 14px; + overflow: auto; +`; + +const SmallContainer = styled.div` + display: flex; + flex-direction: column; + gap: 14px; + width: 150px; + height: 150px; +`; + const Checkbox = () => ( <> <ExampleContainer> @@ -194,28 +214,12 @@ const Checkbox = () => ( </> ); -export const Chromatic = Checkbox.bind({}); +type Story = StoryObj<typeof DxcCheckbox>; -Chromatic.play = async () => { - const listEl = document.getElementById("scroll-container"); - listEl?.scrollTo?.({ top: 50 }); +export const Chromatic: Story = { + render: Checkbox, + play: async () => { + const listEl = document.getElementById("scroll-container"); + listEl?.scrollTo?.({ top: 50 }); + }, }; - -const ScrollableContainer = styled.div` - display: flex; - flex-direction: column; - gap: 14px; - width: 200px; - height: 200px; - border: 1px solid #000; - padding: 14px; - overflow: auto; -`; - -const SmallContainer = styled.div` - display: flex; - flex-direction: column; - gap: 14px; - width: 150px; - height: 150px; -`; diff --git a/packages/lib/src/chip/Chip.stories.tsx b/packages/lib/src/chip/Chip.stories.tsx index 31bd92dbe9..e209fc9a1c 100644 --- a/packages/lib/src/chip/Chip.stories.tsx +++ b/packages/lib/src/chip/Chip.stories.tsx @@ -3,11 +3,12 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcChip from "./Chip"; +import { Meta, StoryObj } from "@storybook/react/*"; export default { title: "Chip", component: DxcChip, -}; +} as Meta<typeof DxcChip>; const iconSVG = ( <svg @@ -46,7 +47,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Chip = () => ( <> <ExampleContainer> <Title title="Basic chip" theme="light" level={4} /> @@ -170,12 +171,14 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + const ChipPrefixFocused = () => ( <ExampleContainer> <Title title="Chip with prefix" theme="light" level={4} /> <DxcChip label="Chip with prefix" prefixIcon={iconSVG} onClickPrefix={() => {}} /> </ExampleContainer> ); + const ChipSuffixFocused = () => ( <ExampleContainer> <Title title="Chip with suffix" theme="light" level={4} /> @@ -183,12 +186,22 @@ const ChipSuffixFocused = () => ( </ExampleContainer> ); -export const PrefixFocused = ChipPrefixFocused.bind({}); -PrefixFocused.play = async () => { - await userEvent.tab(); +type Story = StoryObj<typeof DxcChip>; + +export const Chromatic: Story = { + render: Chip, }; -export const SuffixFocused = ChipSuffixFocused.bind({}); -SuffixFocused.play = async () => { - await userEvent.tab(); +export const PrefixFocused: Story = { + render: ChipPrefixFocused, + play: async () => { + await userEvent.tab(); + }, +}; + +export const SuffixFocused: Story = { + render: ChipSuffixFocused, + play: async () => { + await userEvent.tab(); + }, }; diff --git a/packages/lib/src/container/Container.stories.tsx b/packages/lib/src/container/Container.stories.tsx index 68e56c2122..69f4dfe0c3 100644 --- a/packages/lib/src/container/Container.stories.tsx +++ b/packages/lib/src/container/Container.stories.tsx @@ -2,13 +2,57 @@ 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", component: DxcContainer, -}; +} as Meta<typeof DxcContainer>; + +const Listbox = ({ suggestions = [] }: { suggestions: string[] }): JSX.Element => ( + <DxcContainer + boxSizing="border-box" + boxShadow="0 4px 6px -1px rgba(0, 0, 0, 0.1)" + border={{ width: "1px", style: "solid", color: "color_grey_400" }} + borderRadius="0.25rem" + background={{ color: "color_white" }} + padding={{ top: "xxsmall", bottom: "xxsmall" }} + maxHeight="304px" + width="250px" + overflow={{ x: "hidden", y: "auto" }} + > + {suggestions.map((suggestion, index) => ( + <DxcContainer padding={{ left: "xsmall", right: "xsmall" }}> + <DxcContainer + border={ + index !== suggestions.length - 1 + ? { + bottom: { + width: "1px", + style: "solid", + color: "color_grey_200", + }, + } + : undefined + } + padding={{ + top: "xxsmall", + bottom: "xxsmall", + left: "xxsmall", + right: "xxsmall", + }} + overflow="hidden" + > + <DxcTypography whiteSpace="nowrap" textOverflow="ellipsis" lineHeight="1.715em"> + {suggestion} + </DxcTypography> + </DxcContainer> + </DxcContainer> + ))} + </DxcContainer> +); -export const Chromatic = () => ( +const Container = () => ( <> <Title title="Box sizing border box" level={4} /> <ExampleContainer> @@ -180,45 +224,8 @@ export const Chromatic = () => ( </> ); -const Listbox = ({ suggestions = [] }: { suggestions: string[] }): JSX.Element => ( - <DxcContainer - boxSizing="border-box" - boxShadow="0 4px 6px -1px rgba(0, 0, 0, 0.1)" - border={{ width: "1px", style: "solid", color: "color_grey_400" }} - borderRadius="0.25rem" - background={{ color: "color_white" }} - padding={{ top: "xxsmall", bottom: "xxsmall" }} - maxHeight="304px" - width="250px" - overflow={{ x: "hidden", y: "auto" }} - > - {suggestions.map((suggestion, index) => ( - <DxcContainer padding={{ left: "xsmall", right: "xsmall" }}> - <DxcContainer - border={ - index !== suggestions.length - 1 - ? { - bottom: { - width: "1px", - style: "solid", - color: "color_grey_200", - }, - } - : undefined - } - padding={{ - top: "xxsmall", - bottom: "xxsmall", - left: "xxsmall", - right: "xxsmall", - }} - overflow="hidden" - > - <DxcTypography whiteSpace="nowrap" textOverflow="ellipsis" lineHeight="1.715em"> - {suggestion} - </DxcTypography> - </DxcContainer> - </DxcContainer> - ))} - </DxcContainer> -); +type Story = StoryObj<typeof DxcContainer>; + +export const Chromatic: Story = { + render: Container, +}; diff --git a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx index 43cb920d5e..5b50345d6c 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx @@ -8,6 +8,7 @@ import DxcContextualMenu from "./ContextualMenu"; import SingleItem from "./SingleItem"; import { userEvent, within } from "@storybook/test"; import ContextualMenuContext from "./ContextualMenuContext"; +import { StoryObj } from "@storybook/react/*"; export default { title: "Contextual Menu", @@ -124,7 +125,7 @@ const itemsWithTruncatedText = [ }, ]; -export const Chromatic = () => ( +const ContextualMenu = () => ( <> <Title title="Default" theme="light" level={3} /> <ExampleContainer> @@ -175,7 +176,7 @@ export const Chromatic = () => ( </> ); -export const SingleItemStates = () => { +const Single = () => { const colorsTheme = useTheme(); return ( @@ -227,9 +228,21 @@ const ItemWithEllipsis = () => ( </ExampleContainer> ); -export const ContextualMenuTooltip = ItemWithEllipsis.bind({}); -ContextualMenuTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.hover(canvas.getByText("Item with a very long label that should be truncated")); - await userEvent.hover(canvas.getByText("Item with a very long label that should be truncated")); +type Story = StoryObj<typeof DxcContextualMenu>; + +export const Chromatic: Story = { + render: ContextualMenu, +}; + +export const SingleItemStates: Story = { + render: Single, +}; + +export const ContextualMenuTooltip: Story = { + render: ItemWithEllipsis, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.hover(canvas.getByText("Item with a very long label that should be truncated")); + await userEvent.hover(canvas.getByText("Item with a very long label that should be truncated")); + }, }; diff --git a/packages/lib/src/date-input/DateInput.stories.tsx b/packages/lib/src/date-input/DateInput.stories.tsx index fce54ba071..ceb717bfe2 100644 --- a/packages/lib/src/date-input/DateInput.stories.tsx +++ b/packages/lib/src/date-input/DateInput.stories.tsx @@ -12,8 +12,6 @@ import Calendar from "./Calendar"; import DxcDateInput from "./DateInput"; import DxcDatePicker from "./DatePicker"; import YearPicker from "./YearPicker"; -import DxcTooltip from "../tooltip/Tooltip"; -import DxcInset from "../inset/Inset"; export default { title: "Date Input", diff --git a/packages/lib/src/nav-tabs/Tab.tsx b/packages/lib/src/nav-tabs/Tab.tsx index 0f108628a9..076c0f5d49 100644 --- a/packages/lib/src/nav-tabs/Tab.tsx +++ b/packages/lib/src/nav-tabs/Tab.tsx @@ -1,9 +1,8 @@ -import React, { useEffect, forwardRef, Ref, useContext, useRef, useImperativeHandle, KeyboardEvent } from "react"; +import { useEffect, forwardRef, Ref, useContext, useRef, useImperativeHandle, KeyboardEvent } from "react"; import styled from "styled-components"; import DxcBadge from "../badge/Badge"; import DxcFlex from "../flex/Flex"; import NavTabsPropsType, { TabProps } from "./types"; -import useTheme from "../useTheme"; import NavTabsContext from "./NavTabsContext"; import DxcIcon from "../icon/Icon"; diff --git a/packages/lib/src/typography/Typography.tsx b/packages/lib/src/typography/Typography.tsx index 3271ff698f..0679f0d8b7 100644 --- a/packages/lib/src/typography/Typography.tsx +++ b/packages/lib/src/typography/Typography.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useMemo } from "react"; +import { useContext, useMemo } from "react"; import styled from "styled-components"; import TypographyPropsTypes, { TypographyContextProps } from "./types"; From 3e8ec1c1ccfd1e58ff4ca7ab4b67bb92a541a20f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:46:06 +0100 Subject: [PATCH 20/41] Updating components --- .../src/accordion-group/AccordionGroup.tsx | 6 +-- packages/lib/src/accordion/Accordion.tsx | 6 +-- packages/lib/src/alert/Alert.tsx | 9 ++-- .../lib/src/bulleted-list/BulletedList.tsx | 6 +-- packages/lib/src/button/Button.tsx | 45 +++++++++---------- packages/lib/src/card/Card.tsx | 6 +-- packages/lib/src/checkbox/Checkbox.tsx | 16 +++---- packages/lib/src/chip/Chip.tsx | 5 ++- packages/lib/src/container/Container.tsx | 29 ++++++------ .../ContextualMenu.stories.tsx | 2 +- .../src/contextual-menu/ContextualMenu.tsx | 6 +-- packages/lib/src/data-grid/DataGrid.tsx | 7 ++- packages/lib/src/date-input/Calendar.tsx | 6 +-- packages/lib/src/date-input/DateInput.tsx | 8 ++-- packages/lib/src/date-input/DatePicker.tsx | 6 +-- packages/lib/src/dialog/Dialog.test.tsx | 4 +- packages/lib/src/dialog/Dialog.tsx | 9 ++-- packages/lib/src/dropdown/Dropdown.tsx | 6 +-- packages/lib/src/file-input/FileInput.tsx | 9 ++-- packages/lib/src/file-input/FileItem.tsx | 9 ++-- packages/lib/src/footer/Footer.tsx | 9 ++-- packages/lib/src/header/Header.tsx | 10 ++--- packages/lib/src/heading/Heading.tsx | 5 ++- packages/lib/src/image/Image.tsx | 6 +-- packages/lib/src/layout/ApplicationLayout.tsx | 6 +-- packages/lib/src/switch/Switch.tsx | 4 +- packages/lib/src/utils/FocusLock.tsx | 30 +++++++------ packages/lib/src/utils/useWidth.tsx | 1 - 28 files changed, 130 insertions(+), 141 deletions(-) diff --git a/packages/lib/src/accordion-group/AccordionGroup.tsx b/packages/lib/src/accordion-group/AccordionGroup.tsx index f98707630b..89c7def565 100644 --- a/packages/lib/src/accordion-group/AccordionGroup.tsx +++ b/packages/lib/src/accordion-group/AccordionGroup.tsx @@ -1,11 +1,11 @@ -import { Children, useCallback, useMemo, useState } from "react"; +import { Children, useCallback, useContext, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; import AccordionGroupAccordion from "./AccordionGroupAccordion"; import AccordionGroupPropsType from "./types"; import AccordionGroupAccordionContext from "./AccordionGroupContext"; +import HalstackContext from "../HalstackContext"; const DxcAccordionGroup = ({ defaultIndexActive, @@ -15,8 +15,8 @@ const DxcAccordionGroup = ({ margin, children, }: AccordionGroupPropsType): JSX.Element => { - const colorsTheme = useTheme(); const [innerIndexActive, setInnerIndexActive] = useState(defaultIndexActive ?? -1); + const colorsTheme = useContext(HalstackContext); const handlerActiveChange = useCallback( (index: number) => { diff --git a/packages/lib/src/accordion/Accordion.tsx b/packages/lib/src/accordion/Accordion.tsx index 085657de5e..5d576f6b94 100644 --- a/packages/lib/src/accordion/Accordion.tsx +++ b/packages/lib/src/accordion/Accordion.tsx @@ -1,8 +1,8 @@ -import { useId, useState } from "react"; +import { useContext, useId, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import AccordionPropsType from "./types"; import DxcIcon from "../icon/Icon"; @@ -20,7 +20,7 @@ const DxcAccordion = ({ }: AccordionPropsType): JSX.Element => { const id = useId(); const [innerIsExpanded, setInnerIsExpanded] = useState(defaultIsExpanded ?? false); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const handleAccordionState = () => { if (isExpanded == null) { diff --git a/packages/lib/src/alert/Alert.tsx b/packages/lib/src/alert/Alert.tsx index 485764f290..e2ed56bc3f 100644 --- a/packages/lib/src/alert/Alert.tsx +++ b/packages/lib/src/alert/Alert.tsx @@ -1,7 +1,5 @@ import styled, { css, ThemeProvider } from "styled-components"; -import { useState, memo, useId, useEffect, useCallback } from "react"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import { useState, memo, useId, useEffect, useCallback, useContext } from "react"; import AlertPropsType from "./types"; import DxcIcon from "../icon/Icon"; import DxcButton from "../button/Button"; @@ -10,6 +8,7 @@ import DxcActionIcon from "../action-icon/ActionIcon"; import DxcFlex from "../flex/Flex"; import ModalAlertWrapper from "./ModalAlertWrapper"; import CoreTokens from "../common/coreTokens"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; const AlertContainer = styled.div<{ semantic: AlertPropsType["semantic"]; @@ -138,8 +137,8 @@ export default function DxcAlert({ const [currentIndex, setCurrentIndex] = useState(0); const id = useId(); - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const handleNextOnClick = () => { setCurrentIndex((prevIndex) => (prevIndex < messages.length ? prevIndex + 1 : prevIndex)); diff --git a/packages/lib/src/bulleted-list/BulletedList.tsx b/packages/lib/src/bulleted-list/BulletedList.tsx index 3dc377ea3d..1a8139358c 100644 --- a/packages/lib/src/bulleted-list/BulletedList.tsx +++ b/packages/lib/src/bulleted-list/BulletedList.tsx @@ -1,15 +1,15 @@ -import { Children } from "react"; +import { Children, useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcFlex from "../flex/Flex"; import DxcTypography from "../typography/Typography"; import BulletedListPropsType, { BulletedListItemPropsType } from "./types"; -import useTheme from "../utils/useTheme"; import DxcIcon from "../icon/Icon"; +import HalstackContext from "../HalstackContext"; const BulletedListItem = ({ children }: BulletedListItemPropsType): JSX.Element => <>{children}</>; const DxcBulletedList = ({ children, type = "disc", icon = "" }: BulletedListPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.bulletedList}> diff --git a/packages/lib/src/button/Button.tsx b/packages/lib/src/button/Button.tsx index b5a70ce65f..c9a40f280f 100644 --- a/packages/lib/src/button/Button.tsx +++ b/packages/lib/src/button/Button.tsx @@ -1,10 +1,11 @@ +import { useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; import { getMargin } from "../common/utils"; -import useTheme from "../utils/useTheme"; import type ButtonPropsType from "./types"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; +import HalstackContext from "../HalstackContext"; const DxcButton = ({ label = "", @@ -20,7 +21,7 @@ const DxcButton = ({ size = { height: "large", width: "fitContent" }, tabIndex = 0, }: ButtonPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.button}> @@ -195,7 +196,6 @@ const getButtonStyles = ( &:disabled { ${disabled} }`; - case "secondary": switch (semantic) { case "default": @@ -303,7 +303,6 @@ const getButtonStyles = ( &:disabled { ${disabled} }`; - case "tertiary": switch (semantic) { case "default": @@ -418,50 +417,50 @@ const Button = styled.button<{ props.margin && typeof props.margin === "object" && props.margin.left ? spaces[props.margin.left] : ""}; padding-top: ${(props) => props.hasIcon && !props.hasLabel - ? props?.size?.height === "small" + ? props.size?.height === "small" ? props.theme.paddingSmallOnlyIconTop - : props?.size?.height === "medium" + : props.size?.height === "medium" ? props.theme.paddingMediumOnlyIconTop : props.theme.paddingLargeOnlyIconTop - : props?.size?.height === "small" + : props.size?.height === "small" ? props.theme.paddingSmallTop - : props?.size?.height === "medium" + : props.size?.height === "medium" ? props.theme.paddingMediumTop : props.theme.paddingLargeTop}; padding-bottom: ${(props) => props.hasIcon && !props.hasLabel - ? props?.size?.height === "small" + ? props.size?.height === "small" ? props.theme.paddingSmallOnlyIconBottom - : props?.size?.height === "medium" + : props.size?.height === "medium" ? props.theme.paddingMediumOnlyIconBottom : props.theme.paddingLargeOnlyIconBottom - : props?.size?.height === "small" + : props.size?.height === "small" ? props.theme.paddingSmallBottom - : props?.size?.height === "medium" + : props.size?.height === "medium" ? props.theme.paddingMediumBottom : props.theme.paddingLargeBottom}; padding-left: ${(props) => props.hasIcon && !props.hasLabel - ? props?.size?.height === "small" + ? props.size?.height === "small" ? props.theme.paddingSmallOnlyIconLeft - : props?.size?.height === "medium" + : props.size?.height === "medium" ? props.theme.paddingMediumOnlyIconLeft : props.theme.paddingLargeOnlyIconLeft - : props?.size?.height === "small" + : props.size?.height === "small" ? props.theme.paddingSmallLeft - : props?.size?.height === "medium" + : props.size?.height === "medium" ? props.theme.paddingMediumLeft : props.theme.paddingLargeLeft}; padding-right: ${(props) => props.hasIcon && !props.hasLabel - ? props?.size?.height === "small" + ? props.size?.height === "small" ? props.theme.paddingSmallOnlyIconRight - : props?.size?.height === "medium" + : props.size?.height === "medium" ? props.theme.paddingMediumOnlyIconRight : props.theme.paddingLargeOnlyIconRight - : props?.size?.height === "small" + : props.size?.height === "small" ? props.theme.paddingSmallRight - : props?.size?.height === "medium" + : props.size?.height === "medium" ? props.theme.paddingMediumRight : props.theme.paddingLargeRight}; @@ -493,10 +492,10 @@ const IconContainer = styled.div<{ size: ButtonPropsType["size"]; }>` display: flex; - font-size: ${(props) => (props?.size?.height === "small" ? "16" : props?.size?.height === "medium" ? "16" : "24")}px; + font-size: ${(props) => (props.size?.height === "small" ? "16" : props.size?.height === "medium" ? "16" : "24")}px; svg { - height: ${(props) => (props?.size?.height === "small" ? "16" : props?.size?.height === "medium" ? "16" : "24")}px; - width: ${(props) => (props?.size?.height === "small" ? "16" : props?.size?.height === "medium" ? "16" : "24")}px; + height: ${(props) => (props.size?.height === "small" ? "16" : props.size?.height === "medium" ? "16" : "24")}px; + width: ${(props) => (props.size?.height === "small" ? "16" : props.size?.height === "medium" ? "16" : "24")}px; } `; diff --git a/packages/lib/src/card/Card.tsx b/packages/lib/src/card/Card.tsx index c9a977f567..be94105c4c 100644 --- a/packages/lib/src/card/Card.tsx +++ b/packages/lib/src/card/Card.tsx @@ -1,9 +1,9 @@ -import { useState } from "react"; +import { useContext, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; import CardPropsType from "./types"; import CoreTokens from "../common/coreTokens"; +import HalstackContext from "../HalstackContext"; const DxcCard = ({ imageSrc, @@ -18,7 +18,7 @@ const DxcCard = ({ outlined = true, children, }: CardPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const [isHovered, changeIsHovered] = useState(false); return ( diff --git a/packages/lib/src/checkbox/Checkbox.tsx b/packages/lib/src/checkbox/Checkbox.tsx index 283ba8c973..d7c22a8b73 100644 --- a/packages/lib/src/checkbox/Checkbox.tsx +++ b/packages/lib/src/checkbox/Checkbox.tsx @@ -1,9 +1,8 @@ -import { useState, useRef, useId, forwardRef, KeyboardEvent } from "react"; +import { useContext, useState, useRef, useId, forwardRef, KeyboardEvent } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; import { getMargin } from "../common/utils"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import CheckboxPropsType, { RefType } from "./types"; const checkedIcon = ( @@ -34,19 +33,18 @@ const DxcCheckbox = forwardRef<RefType, CheckboxPropsType>( const labelId = `label-checkbox-${useId()}`; const [innerChecked, setInnerChecked] = useState(defaultChecked); const checkboxRef = useRef<HTMLSpanElement | null>(null); - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const handleCheckboxChange = () => { if (!disabled && !readOnly) { - if (document.activeElement !== checkboxRef?.current) { - checkboxRef?.current?.focus(); + if (document.activeElement !== checkboxRef.current) { + checkboxRef.current?.focus(); } - const newChecked = checked ?? innerChecked; if (checked == null) { setInnerChecked((innerCurrentlyChecked) => !innerCurrentlyChecked); } - onChange?.(!newChecked); + onChange?.(!(checked ?? innerChecked)); } }; diff --git a/packages/lib/src/chip/Chip.tsx b/packages/lib/src/chip/Chip.tsx index b842ca663c..b5434cef32 100644 --- a/packages/lib/src/chip/Chip.tsx +++ b/packages/lib/src/chip/Chip.tsx @@ -1,8 +1,9 @@ +import { useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import ChipPropsType from "./types"; const DxcChip = ({ @@ -15,7 +16,7 @@ const DxcChip = ({ margin, tabIndex = 0, }: ChipPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.chip}> diff --git a/packages/lib/src/container/Container.tsx b/packages/lib/src/container/Container.tsx index 47ae248b10..72d200bbf9 100644 --- a/packages/lib/src/container/Container.tsx +++ b/packages/lib/src/container/Container.tsx @@ -5,7 +5,7 @@ import { spaces } from "../common/variables"; const getBorderStyles = (direction: "top" | "bottom" | "left" | "right", borderProperties: BorderProperties) => `border-${direction}: ${borderProperties?.width ?? ""} ${borderProperties?.style ?? ""} ${ - borderProperties?.color ? (getCoreColorToken(borderProperties?.color) ?? "") : "" + borderProperties?.color ? getCoreColorToken(borderProperties?.color) : "" };`; const Container = styled.div<StyledProps>` @@ -42,23 +42,20 @@ const Container = styled.div<StyledProps>` border && "color" in border && border?.color ? `${getCoreColorToken(border?.color)}` : ""}; ${({ border }) => { + let styles = ""; if (border != null) { - let styles = ""; - if ("top" in border) { - styles += border?.top ? getBorderStyles("top", border.top) : ""; + 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 ("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; } - return undefined; + return styles; }}; margin: ${({ margin }) => (typeof margin === "string" ? spaces[margin] : "")}; @@ -68,7 +65,7 @@ const Container = styled.div<StyledProps>` margin-left: ${({ margin }) => (typeof margin === "object" && margin.left ? spaces[margin.left] : "")}; outline: ${({ outline }) => - `${outline?.width ?? ""} ${outline?.style ?? ""} ${outline?.color ? (getCoreColorToken(outline?.color) ?? "") : ""}`}; + `${outline?.width ?? ""} ${outline?.style ?? ""} ${outline?.color ? getCoreColorToken(outline?.color) : ""}`}; outline-offset: ${({ outline }) => outline?.offset}; overflow: ${({ $overflow }) => (typeof $overflow === "string" ? $overflow : "")}; diff --git a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx index 606533f745..226e765d62 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx @@ -176,7 +176,7 @@ export const Chromatic = () => ( ); export const SingleItemStates = () => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.contextualMenu}> diff --git a/packages/lib/src/contextual-menu/ContextualMenu.tsx b/packages/lib/src/contextual-menu/ContextualMenu.tsx index cb954efda6..ec064ad1e6 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.tsx @@ -1,7 +1,6 @@ -import { useLayoutEffect, useMemo, useRef, useState } from "react"; +import { useContext, useLayoutEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import CoreTokens from "../common/coreTokens"; -import useTheme from "../utils/useTheme"; import MenuItem from "./MenuItem"; import ContextualMenuPropsType, { GroupItem, @@ -14,6 +13,7 @@ import ContextualMenuPropsType, { } from "./types"; import Section from "./Section"; import ContextualMenuContext from "./ContextualMenuContext"; +import HalstackContext from "../HalstackContext"; const ContextualMenu = styled.div` box-sizing: border-box; @@ -79,7 +79,7 @@ export default function DxcContextualMenu({ items }: ContextualMenuPropsType) { const contextualMenuRef = useRef<HTMLDivElement | null>(null); const itemsWithId = useMemo(() => addIdToItems(items), [items]); const contextValue = useMemo(() => ({ selectedItemId, setSelectedItemId }), [selectedItemId, setSelectedItemId]); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const [firstUpdate, setFirstUpdate] = useState(true); useLayoutEffect(() => { diff --git a/packages/lib/src/data-grid/DataGrid.tsx b/packages/lib/src/data-grid/DataGrid.tsx index d12a013e1b..4caf502637 100644 --- a/packages/lib/src/data-grid/DataGrid.tsx +++ b/packages/lib/src/data-grid/DataGrid.tsx @@ -1,9 +1,8 @@ -import { useEffect, useMemo, useState } from "react"; +import { useContext, useEffect, useMemo, useState } from "react"; import DataGrid, { SortColumn } from "react-data-grid"; import styled, { ThemeProvider } from "styled-components"; import DataGridPropsType, { HierarchyGridRow, GridRow, ExpandableGridRow } from "./types"; import "react-data-grid/lib/styles.css"; - import { convertToRDGColumns, rowKeyGetter, @@ -20,9 +19,9 @@ import { getMinItemsPerPageIndex, getMaxItemsPerPageIndex, } from "./utils"; -import useTheme from "../utils/useTheme"; import DxcPaginator from "../paginator/Paginator"; import { DxcActionsCell } from "../table/Table"; +import HalstackContext from "../HalstackContext"; const DxcDataGrid = ({ columns, @@ -44,7 +43,7 @@ const DxcDataGrid = ({ totalItems, }: DataGridPropsType): JSX.Element => { const [rowsToRender, setRowsToRender] = useState<GridRow[] | HierarchyGridRow[] | ExpandableGridRow[]>(rows); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const [page, changePage] = useState(1); const goToPage = (newPage: number) => { diff --git a/packages/lib/src/date-input/Calendar.tsx b/packages/lib/src/date-input/Calendar.tsx index a2013ffad9..1506fb5e1e 100644 --- a/packages/lib/src/date-input/Calendar.tsx +++ b/packages/lib/src/date-input/Calendar.tsx @@ -1,8 +1,8 @@ import { Dayjs } from "dayjs"; -import { useState, useMemo, useEffect, useId, memo, KeyboardEvent, FocusEvent } from "react"; +import { useState, useMemo, useEffect, useId, memo, KeyboardEvent, FocusEvent, useContext } from "react"; import styled from "styled-components"; import { CalendarPropsType, DateType } from "./types"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import { HalstackLanguageContext } from "../HalstackContext"; const getDays = (innerDate: Dayjs) => { const monthDayCells: DateType[] = []; @@ -61,7 +61,7 @@ const Calendar = ({ const [dateToFocus, setDateToFocus] = useState(getDateToFocus(selectedDate, innerDate, today)); const [isFocusable, setIsFocusable] = useState(false); const id = useId(); - const translatedLabels = useTranslatedLabels(); + const translatedLabels = useContext(HalstackLanguageContext); const dayCells = useMemo(() => getDays(innerDate), [innerDate]); const onDateClickHandler = (date: DateType) => { diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index 12bf534bc6..332de4cbfd 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -4,6 +4,7 @@ import { useEffect, useId, useCallback, + useContext, forwardRef, Dispatch, SetStateAction, @@ -14,8 +15,7 @@ import dayjs, { Dayjs } from "dayjs"; import styled, { ThemeProvider } from "styled-components"; import * as Popover from "@radix-ui/react-popover"; import customParseFormat from "dayjs/plugin/customParseFormat"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import DateInputPropsType, { RefType } from "./types"; import DatePicker from "./DatePicker"; import { getMargin } from "../common/utils"; @@ -88,8 +88,8 @@ const DxcDateInput = forwardRef<RefType, DateInputPropsType>( : null ); const [sideOffset, setSideOffset] = useState(SIDEOFFSET); - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const dateRef = useRef<HTMLDivElement | null>(null); const popoverContentRef = useRef<HTMLDivElement | null>(null); diff --git a/packages/lib/src/date-input/DatePicker.tsx b/packages/lib/src/date-input/DatePicker.tsx index b4d3bec3df..a8716333bf 100644 --- a/packages/lib/src/date-input/DatePicker.tsx +++ b/packages/lib/src/date-input/DatePicker.tsx @@ -1,12 +1,12 @@ -import { memo, useState } from "react"; +import { memo, useContext, useState } from "react"; import dayjs, { Dayjs } from "dayjs"; import styled from "styled-components"; import { DatePickerPropsType } from "./types"; import Calendar from "./Calendar"; import YearPicker from "./YearPicker"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; +import { HalstackLanguageContext } from "../HalstackContext"; const today = dayjs(); @@ -14,7 +14,7 @@ const DatePicker = ({ date, onDateSelect, id }: DatePickerPropsType): JSX.Elemen const [innerDate, setInnerDate] = useState(date?.isValid() ? date : dayjs()); const [content, setContent] = useState("calendar"); const selectedDate = date?.isValid() ? date : dayjs(null); - const translatedLabels = useTranslatedLabels(); + const translatedLabels = useContext(HalstackLanguageContext); const handleDateSelect = (chosenDate: Dayjs) => { setInnerDate(chosenDate); diff --git a/packages/lib/src/dialog/Dialog.test.tsx b/packages/lib/src/dialog/Dialog.test.tsx index bc8587643d..829ac4aa42 100644 --- a/packages/lib/src/dialog/Dialog.test.tsx +++ b/packages/lib/src/dialog/Dialog.test.tsx @@ -267,7 +267,7 @@ describe("Dialog component: Focus lock tests", () => { }); test("Focus gets trapped in the Dialog when there are not focusable elements inside until it is closed", () => { - const { getAllByRole } = render( + const { getAllByRole, getByRole } = render( <> <DxcTextInput label="Name" /> <DxcDialog closable={false}> @@ -278,7 +278,7 @@ describe("Dialog component: Focus lock tests", () => { </> ); const inputs = getAllByRole("textbox"); - const dialog = getAllByRole("dialog")[0]; + const dialog = getByRole("dialog"); userEvent.tab(); userEvent.tab(); expect(document.activeElement).not.toEqual(inputs[1]); diff --git a/packages/lib/src/dialog/Dialog.tsx b/packages/lib/src/dialog/Dialog.tsx index 2c1fc670f5..d86d7f6412 100644 --- a/packages/lib/src/dialog/Dialog.tsx +++ b/packages/lib/src/dialog/Dialog.tsx @@ -1,10 +1,9 @@ -import { useEffect } from "react"; +import { useContext, useEffect } from "react"; import { createPortal } from "react-dom"; import styled, { createGlobalStyle, ThemeProvider } from "styled-components"; import { responsiveSizes } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import FocusLock from "../utils/FocusLock"; import DialogPropsType from "./types"; @@ -90,8 +89,8 @@ const DxcDialog = ({ onBackgroundClick, tabIndex = 0, }: DialogPropsType): JSX.Element => { - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index 23969b1489..cd55a8c4a4 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -1,10 +1,10 @@ import * as Popover from "@radix-ui/react-popover"; -import { FocusEvent, KeyboardEvent, useCallback, useId, useLayoutEffect, useRef, useState } from "react"; +import { FocusEvent, KeyboardEvent, useCallback, useId, useLayoutEffect, useRef, useState, useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import useWidth from "../utils/useWidth"; import DropdownMenu from "./DropdownMenu"; import DropdownPropsType from "./types"; @@ -31,7 +31,7 @@ const DxcDropdown = ({ const [isOpen, changeIsOpen] = useState(false); const [visualFocusIndex, setVisualFocusIndex] = useState(0); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const triggerRef = useRef<HTMLButtonElement | null>(null); const menuRef = useRef<HTMLUListElement | null>(null); const width = useWidth(triggerRef.current); diff --git a/packages/lib/src/file-input/FileInput.tsx b/packages/lib/src/file-input/FileInput.tsx index 29bc7402f0..460b30dd30 100644 --- a/packages/lib/src/file-input/FileInput.tsx +++ b/packages/lib/src/file-input/FileInput.tsx @@ -1,11 +1,10 @@ -import { useCallback, useEffect, useId, useState, forwardRef, DragEvent, ChangeEvent } from "react"; +import { useCallback, useContext, useEffect, useId, useState, forwardRef, DragEvent, ChangeEvent } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcButton from "../button/Button"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; import FileItem from "./FileItem"; import FileInputPropsType, { FileData, RefType } from "./types"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; const getFilePreview = async (file: File): Promise<string> => { if (file?.type?.includes("video")) return "filled_movie"; @@ -57,8 +56,8 @@ const DxcFileInput = forwardRef<RefType, FileInputPropsType>( const [isDragging, setIsDragging] = useState(false); const [files, setFiles] = useState<FileData[]>([]); const fileInputId = `file-input-${useId()}`; - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const checkFileSize = (file: File) => { if (minSize && file.size < minSize) return translatedLabels.fileInput.fileSizeGreaterThanErrorMessage; diff --git a/packages/lib/src/file-input/FileItem.tsx b/packages/lib/src/file-input/FileItem.tsx index 19b9364eb6..18fe6d2a43 100644 --- a/packages/lib/src/file-input/FileItem.tsx +++ b/packages/lib/src/file-input/FileItem.tsx @@ -1,11 +1,10 @@ -import { memo } from "react"; +import { memo, useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcFlex from "../flex/Flex"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; import { FileItemProps } from "./types"; import DxcIcon from "../icon/Icon"; import DxcActionIcon from "../action-icon/ActionIcon"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; const FileItem = ({ fileName = "", @@ -17,8 +16,8 @@ const FileItem = ({ onDelete, tabIndex, }: FileItemProps): JSX.Element => { - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const getIconAriaLabel = () => (type?.includes("video") ? "video" : type?.includes("audio") ? "audio" : "file"); diff --git a/packages/lib/src/footer/Footer.tsx b/packages/lib/src/footer/Footer.tsx index b944204bb6..0c4262114c 100644 --- a/packages/lib/src/footer/Footer.tsx +++ b/packages/lib/src/footer/Footer.tsx @@ -1,14 +1,13 @@ -import { useMemo } from "react"; +import { useMemo, useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import { responsiveSizes, spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; import { dxcLogo, dxcSmallLogo } from "./Icons"; import FooterPropsType from "./types"; import { CoreSpacingTokensType } from "../common/coreTokens"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; const DxcFooter = ({ socialLinks, @@ -19,8 +18,8 @@ const DxcFooter = ({ tabIndex = 0, mode = "default", }: FooterPropsType): JSX.Element => { - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const footerLogo = useMemo( () => diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx index 7008400762..55bf25d3ac 100644 --- a/packages/lib/src/header/Header.tsx +++ b/packages/lib/src/header/Header.tsx @@ -1,13 +1,13 @@ -import { ComponentProps, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { ComponentProps, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { responsiveSizes, spaces } from "../common/variables"; import DxcDropdown from "../dropdown/Dropdown"; import DxcIcon from "../icon/Icon"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; import HeaderPropsType from "./types"; import { Tooltip } from "../tooltip/Tooltip"; import DxcFlex from "../flex/Flex"; +import { useContext } from "react"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; const Dropdown = (props: ComponentProps<typeof DxcDropdown>) => ( <HeaderDropdown> @@ -64,8 +64,8 @@ const DxcHeader = ({ }: HeaderPropsType): JSX.Element => { const [isResponsive, setIsResponsive] = useState(false); const [isMenuVisible, setIsMenuVisible] = useState(false); - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const ref = useRef(null); const handleMenu = () => { diff --git a/packages/lib/src/heading/Heading.tsx b/packages/lib/src/heading/Heading.tsx index ace3b49b59..d3832167d0 100644 --- a/packages/lib/src/heading/Heading.tsx +++ b/packages/lib/src/heading/Heading.tsx @@ -1,10 +1,11 @@ import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; import HeadingPropsType from "./types"; +import { useContext } from "react"; +import HalstackContext from "../HalstackContext"; const DxcHeading = ({ level = 1, text = "", as, weight, margin }: HeadingPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const checkValidAs = () => { if (as === "h1" || as === "h2" || as === "h3" || as === "h4" || as === "h5") return as; diff --git a/packages/lib/src/image/Image.tsx b/packages/lib/src/image/Image.tsx index 07c592b279..1cd0298014 100644 --- a/packages/lib/src/image/Image.tsx +++ b/packages/lib/src/image/Image.tsx @@ -1,7 +1,7 @@ import styled, { ThemeProvider } from "styled-components"; -import { ReactNode, useCallback } from "react"; -import useTheme from "../utils/useTheme"; +import { ReactNode, useCallback, useContext } from "react"; import ImagePropsType, { CaptionWrapperProps } from "./types"; +import HalstackContext from "../HalstackContext"; const Figure = styled.figure` display: flex; @@ -39,7 +39,7 @@ export default function DxcImage({ onLoad, onError, }: ImagePropsType) { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const figureWrapper = useCallback( (children: ReactNode) => ( diff --git a/packages/lib/src/layout/ApplicationLayout.tsx b/packages/lib/src/layout/ApplicationLayout.tsx index a4856932bf..2d81944097 100644 --- a/packages/lib/src/layout/ApplicationLayout.tsx +++ b/packages/lib/src/layout/ApplicationLayout.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useContext, useEffect, useRef, useState } from "react"; import styled from "styled-components"; import { responsiveSizes } from "../common/variables"; import DxcFooter from "../footer/Footer"; @@ -6,10 +6,10 @@ import DxcHeader from "../header/Header"; import DxcIcon from "../icon/Icon"; import DxcSidenav from "../sidenav/Sidenav"; import { SidenavContextProvider, useResponsiveSidenavVisibility } from "../sidenav/SidenavContext"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; import { Tooltip } from "../tooltip/Tooltip"; import ApplicationLayoutPropsType, { AppLayoutMainPropsType } from "./types"; import { bottomLinks, findChildType, socialLinks, useResponsive, year } from "./utils"; +import { HalstackLanguageContext } from "../HalstackContext"; const ApplicationLayoutContainer = styled.div<{ isSidenavVisible: boolean; @@ -125,7 +125,7 @@ const DxcApplicationLayout = ({ const [isSidenavVisibleResponsive, setIsSidenavVisibleResponsive] = useState(false); const isResponsive = useResponsive(responsiveSizes.large); const ref = useRef(null); - const translatedLabels = useTranslatedLabels(); + const translatedLabels = useContext(HalstackLanguageContext); const handleSidenavVisibility = () => { setIsSidenavVisibleResponsive((currentIsSidenavVisibleResponsive) => !currentIsSidenavVisibleResponsive); diff --git a/packages/lib/src/switch/Switch.tsx b/packages/lib/src/switch/Switch.tsx index e52c25f890..a0b1b1fba8 100644 --- a/packages/lib/src/switch/Switch.tsx +++ b/packages/lib/src/switch/Switch.tsx @@ -35,14 +35,12 @@ const DxcSwitch = forwardRef<RefType, SwitchPropsType>( const handleOnKeyDown = (event: KeyboardEvent<HTMLDivElement>) => { switch (event.key) { case "Enter": - case " ": { - // Space + case " ": event.preventDefault(); refTrack?.current?.focus(); setInnerChecked(!(checked ?? innerChecked)); onChange?.(!(checked ?? innerChecked)); break; - } default: break; } diff --git a/packages/lib/src/utils/FocusLock.tsx b/packages/lib/src/utils/FocusLock.tsx index eef084f00b..04f110c616 100644 --- a/packages/lib/src/utils/FocusLock.tsx +++ b/packages/lib/src/utils/FocusLock.tsx @@ -19,9 +19,9 @@ const focusableQuery = [ `[tabindex]${not.negTabIndex}${not.disabled}`, ].join(","); -const getFocusableElements = (container: HTMLElement | null): HTMLElement[] => +const getFocusableElements = (container: HTMLElement): HTMLElement[] => Array.prototype.slice - .call(container?.querySelectorAll(focusableQuery)) + .call(container.querySelectorAll(focusableQuery)) .filter( (element: HTMLElement) => element.getAttribute("aria-hidden") !== "true" && @@ -37,7 +37,7 @@ const getFocusableElements = (container: HTMLElement | null): HTMLElement[] => * @returns */ const attemptFocus = (element: HTMLElement): boolean => { - element?.focus(); + element.focus(); return document.activeElement === element; }; @@ -59,15 +59,15 @@ const radixPortalContains = (activeElement: Node): boolean => { * @param ref: React.MutableRefObject<HTMLDivElement> * @returns */ -const useFocusableElements = (ref: React.MutableRefObject<HTMLDivElement | null>): HTMLElement[] => { - const [focusableElements, setFocusableElements] = useState<HTMLElement[]>([]); +const useFocusableElements = (ref: React.MutableRefObject<HTMLDivElement | null>): HTMLElement[] | null => { + const [focusableElements, setFocusableElements] = useState<HTMLElement[] | null>(null); useEffect(() => { - if (ref?.current != null) { + if (ref.current != null) { setFocusableElements(getFocusableElements(ref.current)); const observer = new MutationObserver(() => { - setFocusableElements(getFocusableElements(ref?.current)); + if (ref.current != null) setFocusableElements(getFocusableElements(ref.current)); }); observer.observe(ref.current, { childList: true, subtree: true }); return () => { @@ -93,23 +93,25 @@ const FocusLock = ({ children }: { children: React.ReactNode }): JSX.Element => const initialFocus = useRef(false); const focusFirst = useCallback(() => { - if (focusableElements.length === 0) childrenContainerRef.current?.focus(); - else if (focusableElements.length > 0) focusableElements.some((element) => attemptFocus(element)); + if (focusableElements != null) { + if (focusableElements.length === 0) childrenContainerRef.current?.focus(); + else if (focusableElements.length > 0) focusableElements.some((element) => attemptFocus(element)); + } }, [focusableElements]); const focusLast = () => { focusableElements - .slice() + ?.slice() .reverse() - ?.some((element) => attemptFocus(element)); + .some((element) => attemptFocus(element)); }; const focusLock = (event: React.KeyboardEvent<HTMLDivElement>) => { - if (event.key === "Tab" && focusableElements.length === 0) event.preventDefault(); + if (event.key === "Tab" && focusableElements?.length === 0) event.preventDefault(); }; useEffect(() => { - if (focusableElements.length > 0 && !initialFocus.current) { + if (focusableElements != null && !initialFocus.current) { initialFocus.current = true; focusFirst(); } @@ -121,7 +123,7 @@ const FocusLock = ({ children }: { children: React.ReactNode }): JSX.Element => const container = childrenContainerRef.current; if ( - target && + target != null && !( container?.contains(target) || container?.nextElementSibling?.contains(target) || diff --git a/packages/lib/src/utils/useWidth.tsx b/packages/lib/src/utils/useWidth.tsx index 0e52d86137..9580a0bab3 100644 --- a/packages/lib/src/utils/useWidth.tsx +++ b/packages/lib/src/utils/useWidth.tsx @@ -18,7 +18,6 @@ const useWidth = <T extends Element>(target: T | null) => { triggerObserver.unobserve(target); }; } - return undefined; }, [target]); return width; From 42e5fdbba694d68f82994ef145f2cfba6f1f91b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:56:34 +0100 Subject: [PATCH 21/41] Updated test to fix typing errors --- .../accordion-group/AccordionGroup.test.tsx | 20 +++--- .../Breadcrumbs.accessibility.test.tsx | 7 +- .../lib/src/breadcrumbs/Breadcrumbs.test.tsx | 2 +- packages/lib/src/checkbox/Checkbox.test.tsx | 12 ++-- .../contextual-menu/ContextualMenu.test.tsx | 68 ++++++++++--------- 5 files changed, 54 insertions(+), 55 deletions(-) diff --git a/packages/lib/src/accordion-group/AccordionGroup.test.tsx b/packages/lib/src/accordion-group/AccordionGroup.test.tsx index 84f0259a49..1dd5ebaa24 100644 --- a/packages/lib/src/accordion-group/AccordionGroup.test.tsx +++ b/packages/lib/src/accordion-group/AccordionGroup.test.tsx @@ -20,12 +20,12 @@ describe("Accordion component tests", () => { </DxcAccordionGroup.Accordion> </DxcAccordionGroup> ); - expect(getAllByRole("button")[0].getAttribute("aria-expanded")).toBe("false"); - expect(getAllByRole("button")[1].getAttribute("aria-expanded")).toBe("false"); + expect(getAllByRole("button")[0]?.getAttribute("aria-expanded")).toBe("false"); + expect(getAllByRole("button")[1]?.getAttribute("aria-expanded")).toBe("false"); fireEvent.click(getByText("Accordion1")); expect(onActiveChange).toHaveBeenCalled(); - expect(getAllByRole("button")[0].getAttribute("aria-expanded")).toBe("true"); - expect(getAllByRole("button")[1].getAttribute("aria-expanded")).toBe("false"); + expect(getAllByRole("button")[0]?.getAttribute("aria-expanded")).toBe("true"); + expect(getAllByRole("button")[1]?.getAttribute("aria-expanded")).toBe("false"); }); test("Uncontrolled accordion group renders initially with an accordion expanded using defaultIndexActive", () => { @@ -39,8 +39,8 @@ describe("Accordion component tests", () => { </DxcAccordionGroup.Accordion> </DxcAccordionGroup> ); - expect(getAllByRole("button")[0].getAttribute("aria-expanded")).toBe("false"); - expect(getAllByRole("button")[1].getAttribute("aria-expanded")).toBe("true"); + expect(getAllByRole("button")[0]?.getAttribute("aria-expanded")).toBe("false"); + expect(getAllByRole("button")[1]?.getAttribute("aria-expanded")).toBe("true"); expect(getByText("Second accordion")).toBeTruthy(); expect(queryByText("First accordion")).toBeFalsy(); }); @@ -63,8 +63,8 @@ describe("Accordion component tests", () => { fireEvent.click(getByText("Accordion2")); expect(onActiveChange.mock.calls[0][0]).toBe(0); expect(onActiveChange.mock.calls[1][0]).toBe(1); - expect(getAllByRole("button")[0].getAttribute("aria-expanded")).toBe("false"); - expect(getAllByRole("button")[1].getAttribute("aria-expanded")).toBe("true"); + expect(getAllByRole("button")[0]?.getAttribute("aria-expanded")).toBe("false"); + expect(getAllByRole("button")[1]?.getAttribute("aria-expanded")).toBe("true"); rerender( <DxcAccordionGroup margin="large" indexActive={0} onActiveChange={onActiveChange}> @@ -76,8 +76,8 @@ describe("Accordion component tests", () => { </DxcAccordionGroup.Accordion> </DxcAccordionGroup> ); - expect(getAllByRole("button")[0].getAttribute("aria-expanded")).toBe("true"); - expect(getAllByRole("button")[1].getAttribute("aria-expanded")).toBe("false"); + expect(getAllByRole("button")[0]?.getAttribute("aria-expanded")).toBe("true"); + expect(getAllByRole("button")[1]?.getAttribute("aria-expanded")).toBe("false"); }); test("Disabled uncontrolled accordion group", () => { diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx index 7ace83c036..dbfe3efb38 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx @@ -1,13 +1,10 @@ import { render } from "@testing-library/react"; -import { axe } from "../../test/accessibility/axe-helper"; +import { axe, formatRules } from "../../test/accessibility/axe-helper"; import DxcBreadcrumbs from "./Breadcrumbs"; import { disabledRules as rules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules"; const disabledRules = { - rules: rules.reduce((rulesObj, rule) => { - rulesObj[rule] = { enabled: false }; - return rulesObj; - }, {}), + rules: formatRules(rules), }; const items = [ diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.test.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.test.tsx index a1e96da287..4ed7419345 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.test.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.test.tsx @@ -32,7 +32,7 @@ describe("Breadcrumbs component tests", () => { const { getByText, getByRole } = render(<DxcBreadcrumbs items={items} ariaLabel="example" />); const breadcrumbs = getByRole("navigation"); expect(breadcrumbs.getAttribute("aria-label")).toBe("example"); - expect(getByText("Dark Mode").parentElement.getAttribute("aria-current")).toBe("page"); + 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 () => { const { queryByText, getByText, getByRole } = render(<DxcBreadcrumbs items={items} itemsBeforeCollapse={3} />); diff --git a/packages/lib/src/checkbox/Checkbox.test.tsx b/packages/lib/src/checkbox/Checkbox.test.tsx index 5871a8f586..2a8563fcb0 100644 --- a/packages/lib/src/checkbox/Checkbox.test.tsx +++ b/packages/lib/src/checkbox/Checkbox.test.tsx @@ -68,12 +68,12 @@ describe("Checkbox component tests", () => { const input = component.getByRole("checkbox"); const submitInput = component.container.querySelector<HTMLInputElement>(`input[name="test"]`); expect(input.getAttribute("aria-checked")).toBe("false"); - expect(submitInput.checked).toBe(false); + expect(submitInput?.checked).toBe(false); fireEvent.click(visibleCheckbox); expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalledWith(true); expect(input.getAttribute("aria-checked")).toBe("true"); - expect(submitInput.checked).toBe(true); + expect(submitInput?.checked).toBe(true); }); test("Controlled checkbox", () => { @@ -86,7 +86,7 @@ describe("Checkbox component tests", () => { expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalledWith(true); expect(input.getAttribute("aria-checked")).toBe("false"); - expect(submitInput.checked).toBe(false); + expect(submitInput?.checked).toBe(false); }); test("Renders with correct initial value and initial state when it is uncontrolled", () => { @@ -95,9 +95,9 @@ describe("Checkbox component tests", () => { ); const checkbox = getByRole("checkbox"); const submitInput = container.querySelector<HTMLInputElement>(`input[name="test"]`); - expect(submitInput.value).toBe("test-defaultChecked"); + expect(submitInput?.value).toBe("test-defaultChecked"); expect(checkbox.getAttribute("aria-checked")).toBe("true"); - expect(submitInput.checked).toBe(true); + expect(submitInput?.checked).toBe(true); }); test("Test disable keyboard and mouse interactions", () => { @@ -112,7 +112,7 @@ describe("Checkbox component tests", () => { expect(onChange).toHaveBeenCalledTimes(0); expect(input.getAttribute("aria-checked")).toBe("false"); expect(input.getAttribute("aria-disabled")).toBe("true"); - expect(submitInput.checked).toBe(false); + expect(submitInput?.checked).toBe(false); userEvent.tab(); expect(document.activeElement === input).toBeFalsy(); }); diff --git a/packages/lib/src/contextual-menu/ContextualMenu.test.tsx b/packages/lib/src/contextual-menu/ContextualMenu.test.tsx index 8cdf2dd1b6..62204900a3 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.test.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.test.tsx @@ -31,12 +31,12 @@ const groups = [ ]; describe("Contextual menu component tests", () => { - test("Single - Renders with correct aria attributes", async () => { + test("Single - Renders with correct aria attributes", () => { const { getAllByRole, getByRole } = render(<DxcContextualMenu items={items} />); expect(getAllByRole("menuitem").length).toBe(4); const actions = getAllByRole("button"); - await userEvent.click(actions[0]); - expect(actions[0].getAttribute("aria-pressed")).toBeTruthy(); + actions[0] != null && userEvent.click(actions[0]); + expect(actions[0]?.getAttribute("aria-pressed")).toBeTruthy(); expect(getByRole("menu")).toBeTruthy(); }); test("Single - An item can appear as selected by default by using the attribute selectedByDefault", () => { @@ -50,31 +50,33 @@ describe("Contextual menu component tests", () => { const item = getByRole("button"); expect(item.getAttribute("aria-pressed")).toBeTruthy(); }); - test("Group - Group items collapse when clicked", async () => { + test("Group - Group items collapse when clicked", () => { const { queryByText, getByText } = render(<DxcContextualMenu items={groups} />); - await userEvent.click(getByText("Grouped Item 1")); + userEvent.click(getByText("Grouped Item 1")); expect(getByText("Item 1")).toBeTruthy(); expect(getByText("Grouped Item 2")).toBeTruthy(); - await userEvent.click(getByText("Grouped Item 2")); + userEvent.click(getByText("Grouped Item 2")); expect(getByText("Item 2")).toBeTruthy(); expect(getByText("Item 3")).toBeTruthy(); - await userEvent.click(getByText("Grouped Item 1")); + userEvent.click(getByText("Grouped Item 1")); expect(queryByText("Item 1")).toBeFalsy(); expect(queryByText("Item 2")).toBeFalsy(); expect(queryByText("Item 3")).toBeFalsy(); }); - test("Group - Renders with correct aria attributes", async () => { + test("Group - Renders with correct aria attributes", () => { const { getAllByRole } = render(<DxcContextualMenu items={groups} />); const group1 = getAllByRole("button")[0]; - await userEvent.click(group1); - expect(group1.getAttribute("aria-expanded")).toBeTruthy(); - expect(group1.getAttribute("aria-controls")).toBe(group1.nextElementSibling.id); - await userEvent.click(getAllByRole("button")[2]); - await userEvent.click(getAllByRole("button")[6]); + group1 != null && userEvent.click(group1); + expect(group1?.getAttribute("aria-expanded")).toBeTruthy(); + expect(group1?.getAttribute("aria-controls")).toBe(group1?.nextElementSibling?.id); + const groupedItem1 = getAllByRole("button")[2]; + const groupedItem2 = getAllByRole("button")[6]; + groupedItem1 != null && userEvent.click(groupedItem1); + groupedItem2 != null && userEvent.click(groupedItem2); expect(getAllByRole("menuitem").length).toBe(10); const optionToBeClicked = getAllByRole("button")[4]; - await userEvent.click(optionToBeClicked); - expect(optionToBeClicked.getAttribute("aria-pressed")).toBeTruthy(); + 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", () => { const test = [ @@ -85,34 +87,34 @@ describe("Contextual menu component tests", () => { ]; const { getByText, getAllByRole } = render(<DxcContextualMenu items={test} />); expect(getByText("Tested item")).toBeTruthy(); - expect(getAllByRole("button")[1].getAttribute("aria-pressed")).toBeTruthy(); + expect(getAllByRole("button")[1]?.getAttribute("aria-pressed")).toBeTruthy(); }); - test("Group - Collapsed groups render as selected when containing a selected item", async () => { + test("Group - Collapsed groups render as selected when containing a selected item", () => { const { getAllByRole } = render(<DxcContextualMenu items={groups} />); const group1 = getAllByRole("button")[0]; - await userEvent.click(group1); + group1 != null && userEvent.click(group1); const group2 = getAllByRole("button")[2]; - await userEvent.click(group2); + group2 != null && userEvent.click(group2); const item = getAllByRole("button")[3]; - await userEvent.click(item); - expect(item.getAttribute("aria-pressed")).toBeTruthy(); - expect(group1.getAttribute("aria-pressed")).toBe("false"); - expect(group2.getAttribute("aria-pressed")).toBe("false"); - await userEvent.click(group2); - expect(group2.getAttribute("aria-pressed")).toBe("true"); - await userEvent.click(group1); - expect(group1.getAttribute("aria-pressed")).toBe("true"); + 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); + expect(group2?.getAttribute("aria-pressed")).toBe("true"); + group1 != null && userEvent.click(group1); + expect(group1?.getAttribute("aria-pressed")).toBe("true"); }); - test("Sections - Renders with correct aria attributes", async () => { + test("Sections - Renders with correct aria attributes", () => { const { getAllByRole, getByText } = render(<DxcContextualMenu items={sections} />); expect(getAllByRole("region").length).toBe(2); expect(getAllByRole("menuitem").length).toBe(6); const actions = getAllByRole("button"); - await userEvent.click(actions[0]); - expect(actions[0].getAttribute("aria-pressed")).toBeTruthy(); + 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); - expect(getAllByRole("region")[1].getAttribute("aria-label")).toBeTruthy(); + expect(getAllByRole("region")[0]?.getAttribute("aria-labelledby")).toBe(getByText("Section title").id); + expect(getAllByRole("region")[1]?.getAttribute("aria-label")).toBeTruthy(); }); test("The onSelect event from each item is called correctly", () => { const test = [ @@ -124,6 +126,6 @@ describe("Contextual menu component tests", () => { const { getByRole } = render(<DxcContextualMenu items={test} />); const item = getByRole("button"); fireEvent.click(item); - expect(test[0].onSelect).toHaveBeenCalled(); + expect(test[0]?.onSelect).toHaveBeenCalled(); }); }); From b9d503151fd71fde391dd58a4c69b5cba9b66ac0 Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Wed, 18 Dec 2024 14:01:24 +0100 Subject: [PATCH 22/41] Refactorized rest of the stories --- .../AccordionGroup.stories.tsx | 2 +- .../lib/src/accordion/Accordion.stories.tsx | 2 +- .../src/action-icon/ActionIcon.stories.tsx | 8 +- packages/lib/src/alert/Alert.stories.tsx | 2 +- packages/lib/src/badge/Badge.stories.tsx | 2 +- packages/lib/src/bleed/Bleed.stories.tsx | 2 +- .../src/breadcrumbs/Breadcrumbs.stories.tsx | 2 +- .../bulleted-list/BulletedList.stories.tsx | 2 +- packages/lib/src/button/Button.stories.tsx | 2 +- packages/lib/src/card/Card.stories.tsx | 2 +- .../lib/src/checkbox/Checkbox.stories.tsx | 2 +- packages/lib/src/chip/Chip.stories.tsx | 2 +- .../lib/src/container/Container.stories.tsx | 2 +- .../ContextualMenu.stories.tsx | 4 +- .../lib/src/data-grid/DataGrid.stories.tsx | 265 ++++++++++-------- .../lib/src/date-input/DateInput.stories.tsx | 103 ++++--- packages/lib/src/dialog/Dialog.stories.tsx | 261 +++++++++-------- packages/lib/src/divider/Divider.stories.tsx | 11 +- .../lib/src/dropdown/Dropdown.stories.tsx | 49 ++-- .../lib/src/file-input/FileInput.stories.tsx | 11 +- packages/lib/src/flex/Flex.stories.tsx | 39 +-- packages/lib/src/footer/Footer.stories.tsx | 35 ++- packages/lib/src/grid/Grid.stories.tsx | 61 ++-- packages/lib/src/header/Header.stories.tsx | 156 ++++++----- packages/lib/src/heading/Heading.stories.tsx | 11 +- packages/lib/src/icon/Icon.stories.tsx | 11 +- packages/lib/src/image/Image.stories.tsx | 11 +- packages/lib/src/inset/Inset.stories.tsx | 33 ++- .../src/layout/ApplicationLayout.stories.tsx | 66 +++-- packages/lib/src/link/Link.stories.tsx | 11 +- packages/lib/src/nav-tabs/NavTabs.stories.tsx | 11 +- .../src/number-input/NumberInput.stories.tsx | 11 +- .../lib/src/paginator/Paginator.stories.tsx | 11 +- .../lib/src/paragraph/Paragraph.stories.tsx | 11 +- .../password-input/PasswordInput.stories.tsx | 25 +- .../src/progress-bar/ProgressBar.stories.tsx | 23 +- .../lib/src/quick-nav/QuickNav.stories.tsx | 53 ++-- .../src/radio-group/RadioGroup.stories.tsx | 11 +- .../ResultsetTable.stories.tsx | 107 ++++--- packages/lib/src/select/Select.stories.tsx | 163 ++++++----- packages/lib/src/sidenav/Sidenav.stories.tsx | 57 ++-- packages/lib/src/slider/Slider.stories.tsx | 11 +- packages/lib/src/spinner/Spinner.stories.tsx | 43 ++- .../src/status-light/StatusLight.stories.tsx | 11 +- packages/lib/src/switch/Switch.stories.tsx | 11 +- packages/lib/src/table/Table.stories.tsx | 23 +- packages/lib/src/tabs/Tabs.stories.tsx | 24 +- packages/lib/src/tabs/TabsLegacy.stories.tsx | 24 +- packages/lib/src/tabs/types.ts | 2 +- packages/lib/src/tag/Tag.stories.tsx | 21 +- .../lib/src/text-input/TextInput.stories.tsx | 24 +- .../lib/src/textarea/Textarea.stories.tsx | 11 +- packages/lib/src/toast/Toast.stories.tsx | 32 ++- .../src/toggle-group/ToggleGroup.stories.tsx | 39 ++- packages/lib/src/tooltip/Tooltip.stories.tsx | 65 +++-- .../lib/src/typography/Typography.stories.tsx | 11 +- packages/lib/src/wizard/Wizard.stories.tsx | 23 +- 57 files changed, 1258 insertions(+), 770 deletions(-) diff --git a/packages/lib/src/accordion-group/AccordionGroup.stories.tsx b/packages/lib/src/accordion-group/AccordionGroup.stories.tsx index 2f69892fb2..e6206b0d58 100644 --- a/packages/lib/src/accordion-group/AccordionGroup.stories.tsx +++ b/packages/lib/src/accordion-group/AccordionGroup.stories.tsx @@ -2,7 +2,7 @@ import DxcAccordionGroup from "./AccordionGroup"; import DxcInset from "../inset/Inset"; import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; -import { Meta, StoryObj } from "@storybook/react/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Accordion Group", diff --git a/packages/lib/src/accordion/Accordion.stories.tsx b/packages/lib/src/accordion/Accordion.stories.tsx index 4e340b1138..4927992ade 100644 --- a/packages/lib/src/accordion/Accordion.stories.tsx +++ b/packages/lib/src/accordion/Accordion.stories.tsx @@ -2,7 +2,7 @@ import DxcAccordion from "./Accordion"; import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import { HalstackProvider } from "../HalstackContext"; -import { Meta, StoryObj } from "@storybook/react/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Accordion", diff --git a/packages/lib/src/action-icon/ActionIcon.stories.tsx b/packages/lib/src/action-icon/ActionIcon.stories.tsx index c8d7731ac4..bd1a035d2a 100644 --- a/packages/lib/src/action-icon/ActionIcon.stories.tsx +++ b/packages/lib/src/action-icon/ActionIcon.stories.tsx @@ -4,7 +4,7 @@ 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/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Action Icon ", @@ -18,7 +18,7 @@ const iconSVG = ( </svg> ); -export const Chromatic = () => ( +const ActionIcon = () => ( <> <Title title="Default" theme="light" level={2} /> <ExampleContainer> @@ -67,6 +67,10 @@ const NestedTooltip = () => ( type Story = StoryObj<typeof DxcActionIcon>; +export const Chromatic: Story = { + render: ActionIcon, +}; + export const ActionIconTooltip: Story = { render: Tooltip, play: async ({ canvasElement }) => { diff --git a/packages/lib/src/alert/Alert.stories.tsx b/packages/lib/src/alert/Alert.stories.tsx index 0c2f2a2810..b67c366eec 100644 --- a/packages/lib/src/alert/Alert.stories.tsx +++ b/packages/lib/src/alert/Alert.stories.tsx @@ -2,7 +2,7 @@ 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/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Alert", diff --git a/packages/lib/src/badge/Badge.stories.tsx b/packages/lib/src/badge/Badge.stories.tsx index a2cd402b59..001e77dfaa 100644 --- a/packages/lib/src/badge/Badge.stories.tsx +++ b/packages/lib/src/badge/Badge.stories.tsx @@ -5,7 +5,7 @@ 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/*"; +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 9e41eeea91..04e3a5a7c3 100644 --- a/packages/lib/src/bleed/Bleed.stories.tsx +++ b/packages/lib/src/bleed/Bleed.stories.tsx @@ -2,7 +2,7 @@ import styled from "styled-components"; import Title from "../../.storybook/components/Title"; import DxcBleed from "./Bleed"; import DxcFlex from "../flex/Flex"; -import { Meta, StoryObj } from "@storybook/react/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Bleed", diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index a7d29ebd0a..8a40e72c10 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -6,7 +6,7 @@ import { HalstackProvider } from "../HalstackContext"; import { userEvent, within } from "@storybook/test"; import { disabledRules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules"; import preview from "../../.storybook/preview"; -import { Meta, StoryObj } from "@storybook/react/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Breadcrumbs", diff --git a/packages/lib/src/bulleted-list/BulletedList.stories.tsx b/packages/lib/src/bulleted-list/BulletedList.stories.tsx index d07508fcf3..33db809d85 100644 --- a/packages/lib/src/bulleted-list/BulletedList.stories.tsx +++ b/packages/lib/src/bulleted-list/BulletedList.stories.tsx @@ -2,7 +2,7 @@ import styled from "styled-components"; import Title from "../../.storybook/components/Title"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import DxcBulletedList from "./BulletedList"; -import { Meta, StoryObj } from "@storybook/react/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Bulleted List", diff --git a/packages/lib/src/button/Button.stories.tsx b/packages/lib/src/button/Button.stories.tsx index 4b9283563e..8e9a8dc92a 100644 --- a/packages/lib/src/button/Button.stories.tsx +++ b/packages/lib/src/button/Button.stories.tsx @@ -6,7 +6,7 @@ import { HalstackProvider } from "../HalstackContext"; import DxcInset from "../inset/Inset"; import DxcTooltip from "../tooltip/Tooltip"; import { userEvent, within } from "@storybook/test"; -import { Meta, StoryObj } from "@storybook/react/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Button", diff --git a/packages/lib/src/card/Card.stories.tsx b/packages/lib/src/card/Card.stories.tsx index 599b89b0c1..ebc08151ad 100644 --- a/packages/lib/src/card/Card.stories.tsx +++ b/packages/lib/src/card/Card.stories.tsx @@ -2,7 +2,7 @@ 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/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Card", diff --git a/packages/lib/src/checkbox/Checkbox.stories.tsx b/packages/lib/src/checkbox/Checkbox.stories.tsx index 4cf3b80897..1f172703ea 100644 --- a/packages/lib/src/checkbox/Checkbox.stories.tsx +++ b/packages/lib/src/checkbox/Checkbox.stories.tsx @@ -3,7 +3,7 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcCheckbox from "./Checkbox"; -import { Meta, StoryObj } from "@storybook/react/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Checkbox", diff --git a/packages/lib/src/chip/Chip.stories.tsx b/packages/lib/src/chip/Chip.stories.tsx index e209fc9a1c..0950b12fe9 100644 --- a/packages/lib/src/chip/Chip.stories.tsx +++ b/packages/lib/src/chip/Chip.stories.tsx @@ -3,7 +3,7 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcChip from "./Chip"; -import { Meta, StoryObj } from "@storybook/react/*"; +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 69f4dfe0c3..2162bfb679 100644 --- a/packages/lib/src/container/Container.stories.tsx +++ b/packages/lib/src/container/Container.stories.tsx @@ -2,7 +2,7 @@ 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/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Container", diff --git a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx index 61b415cfe0..99757aaf29 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.stories.tsx @@ -8,12 +8,12 @@ import DxcContextualMenu from "./ContextualMenu"; import SingleItem from "./SingleItem"; import { userEvent, within } from "@storybook/test"; import ContextualMenuContext from "./ContextualMenuContext"; -import { StoryObj } from "@storybook/react/*"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Contextual Menu", component: DxcContextualMenu, -}; +} as Meta<typeof DxcContextualMenu>; const items = [{ label: "Item 1" }, { label: "Item 2" }, { label: "Item 3" }, { label: "Item 4" }]; diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index 0b7aaae217..89901617ca 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -9,6 +9,7 @@ import preview from "../../.storybook/preview"; import { userEvent, within } from "@storybook/test"; import DxcBadge from "../badge/Badge"; import { ActionsPropsType } from "../table/types"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Data Grid", @@ -23,7 +24,7 @@ export default { }, }, }, -}; +} as Meta<typeof DxcDataGrid>; const actions: ActionsPropsType = [ { @@ -560,7 +561,91 @@ const childRowsPaginated: HierarchyGridRow[] = [ }, ] as HierarchyGridRow[]; -export const Chromatic = () => { +const customSortColumns: GridColumn[] = [ + { + key: "id", + label: "ID", + alignment: "left", + }, + { + key: "task", + label: "Title", + alignment: "left", + }, + { + key: "complete", + label: " % Complete", + alignment: "right", + }, + { + key: "priority", + label: "Priority", + alignment: "center", + }, + { + key: "component", + label: "Component", + alignment: "center", + summaryKey: "total", + sortable: true, + sortFn: (a: JSX.Element, b: JSX.Element) => + a.props.label < b.props.label ? -1 : a.props.label > b.props.label ? 1 : 0, + }, +]; + +const customSortRows = [ + { + id: 1, + task: "Task 1", + complete: 46, + priority: "High", + component: <DxcBadge label={"CCC"} />, + }, + { + id: 2, + task: "Task 2", + complete: 51, + priority: "High", + component: <DxcBadge label={"BBB"} />, + }, + { + id: 3, + task: "Task 3", + complete: 40, + priority: "High", + component: <DxcBadge label={"AAA"} />, + }, + { + id: 4, + task: "Task 4", + complete: 10, + component: <DxcBadge label={"EEE"} />, + priority: "High", + }, + { + id: 5, + task: "Task 5", + complete: 68, + priority: "High", + component: <DxcBadge label={"DDD"} />, + }, + { + id: 6, + task: "Task 6", + complete: 37, + priority: "High", + component: <DxcBadge label={"FFF"} />, + }, + { + id: 7, + task: "Task 7", + complete: 73, + priority: "Medium", + component: <DxcBadge label={"GGG"} />, + }, +]; + +const DataGrid = () => { const [selectedRows, setSelectedRows] = useState((): Set<number | string> => new Set()); const [selectedChildRows, setSelectedChildRows] = useState((): Set<number | string> => new Set()); @@ -676,91 +761,7 @@ export const Chromatic = () => { ); }; -const customSortColumns: GridColumn[] = [ - { - key: "id", - label: "ID", - alignment: "left", - }, - { - key: "task", - label: "Title", - alignment: "left", - }, - { - key: "complete", - label: " % Complete", - alignment: "right", - }, - { - key: "priority", - label: "Priority", - alignment: "center", - }, - { - key: "component", - label: "Component", - alignment: "center", - summaryKey: "total", - sortable: true, - sortFn: (a: JSX.Element, b: JSX.Element) => - a.props.label < b.props.label ? -1 : a.props.label > b.props.label ? 1 : 0, - }, -]; - -const customSortRows = [ - { - id: 1, - task: "Task 1", - complete: 46, - priority: "High", - component: <DxcBadge label={"CCC"} />, - }, - { - id: 2, - task: "Task 2", - complete: 51, - priority: "High", - component: <DxcBadge label={"BBB"} />, - }, - { - id: 3, - task: "Task 3", - complete: 40, - priority: "High", - component: <DxcBadge label={"AAA"} />, - }, - { - id: 4, - task: "Task 4", - complete: 10, - component: <DxcBadge label={"EEE"} />, - priority: "High", - }, - { - id: 5, - task: "Task 5", - complete: 68, - priority: "High", - component: <DxcBadge label={"DDD"} />, - }, - { - id: 6, - task: "Task 6", - complete: 37, - priority: "High", - component: <DxcBadge label={"FFF"} />, - }, - { - id: 7, - task: "Task 7", - complete: 73, - priority: "Medium", - component: <DxcBadge label={"GGG"} />, - }, -]; - -export const CustomSort = () => { +const DataGridSort = () => { return ( <> <ExampleContainer> @@ -771,7 +772,7 @@ export const CustomSort = () => { ); }; -export const Paginator = () => { +const DataGridPaginator = () => { const [selectedRows, setSelectedRows] = useState((): Set<number | string> => new Set()); const [selectedChildRows, setSelectedChildRows] = useState((): Set<number | string> => new Set()); return ( @@ -872,28 +873,6 @@ const DataGridSortedChildren = () => { ); }; -export const DataGridSortedWithChildren = DataGridSortedChildren.bind({}); -DataGridSortedWithChildren.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await userEvent.click(canvas.getAllByRole("checkbox")[0]); - 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")); - await userEvent.click(canvas.getAllByRole("columnheader")[1]); - await userEvent.click(canvas.getAllByRole("columnheader")[1]); - await userEvent.click(canvas.getAllByRole("checkbox")[5]); - - await userEvent.click(canvas.getAllByRole("checkbox")[13]); - 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")); - await userEvent.click(canvas.getAllByRole("columnheader")[4]); - await userEvent.click(canvas.getAllByRole("checkbox")[18]); -}; - const DataGridSortedExpandable = () => { const [selectedRows, setSelectedRows] = useState((): Set<number | string> => new Set()); return ( @@ -928,17 +907,57 @@ const DataGridSortedExpandable = () => { ); }; -export const DataGridSortedExpanded = DataGridSortedExpandable.bind({}); -DataGridSortedExpanded.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("button")[0]); - await userEvent.click(canvas.getAllByRole("button")[1]); - await userEvent.click(canvas.getAllByRole("columnheader")[4]); - await userEvent.click(canvas.getAllByRole("button")[9]); - await userEvent.click(canvas.getAllByRole("button")[10]); - await userEvent.click(canvas.getAllByRole("columnheader")[10]); - await userEvent.click(canvas.getAllByRole("button")[16]); - await userEvent.click(canvas.getAllByRole("button")[43]); - await userEvent.click(canvas.getAllByRole("button")[36]); - await userEvent.click(canvas.getAllByRole("button")[37]); +type Story = StoryObj<typeof DxcDataGrid>; + +export const Chromatic: Story = { + render: DataGrid, +}; + +export const CustomSort: Story = { + render: DataGridSort, +}; + +export const Paginator: Story = { + render: DataGridPaginator, +}; + +export const DataGridSortedWithChildren: Story = { + render: DataGridSortedChildren, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + await userEvent.click(canvas.getAllByRole("checkbox")[0]); + 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")); + await userEvent.click(canvas.getAllByRole("columnheader")[1]); + await userEvent.click(canvas.getAllByRole("columnheader")[1]); + await userEvent.click(canvas.getAllByRole("checkbox")[5]); + + await userEvent.click(canvas.getAllByRole("checkbox")[13]); + 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")); + await userEvent.click(canvas.getAllByRole("columnheader")[4]); + await userEvent.click(canvas.getAllByRole("checkbox")[18]); + }, +}; + +export const DataGridSortedExpanded: Story = { + render: DataGridSortedExpandable, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("button")[0]); + await userEvent.click(canvas.getAllByRole("button")[1]); + await userEvent.click(canvas.getAllByRole("columnheader")[4]); + await userEvent.click(canvas.getAllByRole("button")[9]); + await userEvent.click(canvas.getAllByRole("button")[10]); + await userEvent.click(canvas.getAllByRole("columnheader")[10]); + await userEvent.click(canvas.getAllByRole("button")[16]); + await userEvent.click(canvas.getAllByRole("button")[43]); + await userEvent.click(canvas.getAllByRole("button")[36]); + await userEvent.click(canvas.getAllByRole("button")[37]); + }, }; diff --git a/packages/lib/src/date-input/DateInput.stories.tsx b/packages/lib/src/date-input/DateInput.stories.tsx index dd4e6ef463..2e5afec065 100644 --- a/packages/lib/src/date-input/DateInput.stories.tsx +++ b/packages/lib/src/date-input/DateInput.stories.tsx @@ -12,6 +12,7 @@ import Calendar from "./Calendar"; import DxcDateInput from "./DateInput"; import DxcDatePicker from "./DatePicker"; import YearPicker from "./YearPicker"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Date Input", @@ -26,7 +27,7 @@ export default { }, }, }, -}; +} as Meta<typeof DxcDateInput>; const opinionatedTheme = { dateInput: { @@ -118,13 +119,6 @@ const DateInputChromatic = () => ( </> ); -export const Chromatic = DateInputChromatic.bind({}); -Chromatic.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[0]); - await fireEvent.click(screen.getByText("April 1905")); -}; - const DateInputOpinionatedTheme = () => ( <> <Title title="Opinionated theme" theme="light" level={2} /> @@ -164,12 +158,6 @@ const DateInputOpinionatedTheme = () => ( </> ); -export const DateInputOpinionated = DateInputOpinionatedTheme.bind({}); -DateInputOpinionated.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[3]); -}; - const YearPickerOpinionatedTheme = () => ( <ExampleContainer expanded> <Title title="Year picker" theme="light" level={4} /> @@ -179,13 +167,6 @@ const YearPickerOpinionatedTheme = () => ( </ExampleContainer> ); -export const YearPickerOpinionated = YearPickerOpinionatedTheme.bind({}); -YearPickerOpinionated.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole("combobox")); - await fireEvent.click(screen.getByText("April 1905")); -}; - const DatePickerButtonStates = () => { const colorsTheme: any = useTheme(); return ( @@ -231,14 +212,7 @@ const DatePickerButtonStates = () => { ); }; -export const DatePickerStates = DatePickerButtonStates.bind({}); -DatePickerStates.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const dateBtn = canvas.getAllByRole("combobox")[0]; - await userEvent.click(dateBtn); -}; - -export const YearPickerStates = () => { +const YearPickerButtonStates = () => { const colorsTheme: any = useTheme(); return ( <> @@ -272,7 +246,7 @@ export const YearPickerStates = () => { ); }; -export const DatePickerWithToday = () => { +const DatePickerToday = () => { const colorsTheme: any = useTheme(); return ( <ThemeProvider theme={colorsTheme}> @@ -310,16 +284,65 @@ const Tooltip = () => { ); }; -export const DatePickerTooltipPrevious = Tooltip.bind({}); -DatePickerTooltipPrevious.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const previousMonthButton = canvas.getAllByRole("button")[0]; - await userEvent.hover(previousMonthButton); +type Story = StoryObj<typeof DxcDateInput>; + +export const Chromatic: Story = { + render: DateInputChromatic, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("combobox")[0]); + await fireEvent.click(screen.getByText("April 1905")); + }, +}; + +export const DateInputOpinionated: Story = { + render: DateInputOpinionatedTheme, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("combobox")[3]); + }, +}; + +export const YearPickerOpinionated: Story = { + render: YearPickerOpinionatedTheme, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getByRole("combobox")); + await fireEvent.click(screen.getByText("April 1905")); + }, +}; + +export const DatePickerStates: Story = { + render: DatePickerButtonStates, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const dateBtn = canvas.getAllByRole("combobox")[0]; + await userEvent.click(dateBtn); + }, +}; + +export const YearPickerStates: Story = { + render: YearPickerButtonStates, +}; + +export const DatePickerWithToday: Story = { + render: DatePickerToday, }; -export const DatePickerTooltipAfter = Tooltip.bind({}); -DatePickerTooltipAfter.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const afterMonthButton = canvas.getAllByRole("button")[2]; - await userEvent.hover(afterMonthButton); +export const DatePickerTooltipPrevious: Story = { + render: Tooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const previousMonthButton = canvas.getAllByRole("button")[0]; + await userEvent.hover(previousMonthButton); + }, +}; + +export const DatePickerTooltipAfter: Story = { + render: Tooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const afterMonthButton = canvas.getAllByRole("button")[2]; + await userEvent.hover(afterMonthButton); + }, }; diff --git a/packages/lib/src/dialog/Dialog.stories.tsx b/packages/lib/src/dialog/Dialog.stories.tsx index 182a345ed6..6854d5935b 100644 --- a/packages/lib/src/dialog/Dialog.stories.tsx +++ b/packages/lib/src/dialog/Dialog.stories.tsx @@ -11,6 +11,7 @@ import DxcInset from "../inset/Inset"; import DxcParagraph from "../paragraph/Paragraph"; import DxcTextInput from "../text-input/TextInput"; import DxcDialog from "./Dialog"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Dialog", @@ -20,6 +21,16 @@ export default { viewports: INITIAL_VIEWPORTS, }, }, +} as Meta<typeof DxcDialog>; + +const customViewports = { + resizedScreen: { + name: "Custom viewport", + styles: { + width: "720px", + height: "900px", + }, + }, }; const opinionatedTheme = { @@ -30,7 +41,7 @@ const opinionatedTheme = { }, }; -export const DefaultDialog = () => ( +const Dialog = () => ( <ExampleContainer expanded={true}> <Title title="Default dialog" theme="light" level={4} /> <DxcDialog> @@ -54,7 +65,7 @@ export const DefaultDialog = () => ( </ExampleContainer> ); -export const DefaultDialogOpinionated = () => ( +const DialogOpinionated = () => ( <ExampleContainer expanded={true}> <Title title="Default dialog" theme="light" level={4} /> <HalstackProvider theme={opinionatedTheme}> @@ -80,6 +91,100 @@ export const DefaultDialogOpinionated = () => ( </ExampleContainer> ); +const DialogInput = () => ( + <ExampleContainer expanded={true}> + <Title title="Dialog with inputs" theme="light" level={4} /> + <DxcDialog> + <DxcInset space="1.5rem"> + <DxcFlex gap="2rem" direction="column"> + <DxcHeading level={4} text="Example form" /> + <DxcFlex gap="1rem" direction="column"> + <DxcTextInput size="fillParent" label="Name" /> + <DxcTextInput size="fillParent" label="Surname" /> + </DxcFlex> + <DxcAlert + semantic="error" + title="Error" + message={{ + text: "User: arn:aws:xxx::xxxxxxxxxxxx:assumed-role/assure-sandbox-xxxx-xxxxxxxxxxxxxxxxxxxxxxxxxx/sandbox-xxxx-xxxxxxxxxxxxxxxxxx is not authorized to perform: lambda:xxxxxxxxxxxxxx on resource: arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:sandbox-xxxx-xx-xxxxxxx-xxxxxxx-lambda because no identity-based policy allows the lambda:xxxxxxxxxxxxxx action", + }} + /> + <DxcFlex justifyContent="flex-end" gap="0.5rem"> + <DxcButton label="Cancel" mode="tertiary" /> + <DxcButton label="Save" /> + </DxcFlex> + </DxcFlex> + </DxcInset> + </DxcDialog> + </ExampleContainer> +); + +const DialogNoOverlay = () => ( + <ExampleContainer expanded={true}> + <Title title="Dialog Without Overlay" theme="light" level={4} /> + <DxcDialog overlay={false}> + <DxcInset space="1.5rem"> + <DxcFlex direction="column" gap="1rem"> + <DxcHeading level={4} text="Example title" /> + <DxcParagraph> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi egestas luctus porttitor. Donec massa magna, + placerat sit amet felis eget, venenatis fringilla ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Donec congue laoreet orci, nec elementum dolor consequat quis. Curabitur rhoncus justo sed dapibus + tincidunt. Vestibulum cursus ut risus sit amet congue. Nunc luctus, urna ullamcorper facilisis Jia Le, risus + eros aliquam erat, ut efficitur ante neque id odio. Nam orci leo, dignissim sit amet dolor ut, congue + gravida enim. Donec rhoncus aliquam nisl, ac cursus enim bibendum vitae. Nunc sit amet elit ornare, + malesuada urna eu, fringilla mauris. Vivamus bibendum turpis est, id elementum purus euismod sit amet. Etiam + sit amet maximus augue. Vivamus erat sapien, ultricies fringilla tellus id, condimentum blandit justo. + Praesent quis nunc dignissim, pharetra neque molestie, molestie lectus. + </DxcParagraph> + </DxcFlex> + </DxcInset> + </DxcDialog> + </ExampleContainer> +); + +const DialogCloseNoVisible = () => ( + <ExampleContainer expanded={true}> + <Title title="Dialog Close Visible" theme="dark" level={4} /> + <DxcDialog closable={false}> + <DxcInset space="1.5rem"> + <DxcParagraph> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi egestas luctus porttitor. Donec massa magna, + placerat sit amet felis eget, venenatis fringilla ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Donec congue laoreet orci, nec elementum dolor consequat quis. Curabitur rhoncus justo sed dapibus + tincidunt. Vestibulum cursus ut risus sit amet congue. Nunc luctus, urna ullamcorper facilisis Jia Le, risus + eros aliquam erat, ut efficitur ante neque id odio. Nam orci leo, dignissim sit amet dolor ut, congue gravida + enim. Donec rhoncus aliquam nisl, ac cursus enim bibendum vitae. Nunc sit amet elit ornare, malesuada urna eu, + fringilla mauris. Vivamus bibendum turpis est, id elementum purus euismod sit amet. Etiam sit amet maximus + augue. Vivamus erat sapien, ultricies fringilla tellus id, condimentum blandit justo. Praesent quis nunc + dignissim, pharetra neque molestie, molestie lectus. + </DxcParagraph> + </DxcInset> + </DxcDialog> + </ExampleContainer> +); + +const RespDialog = () => ( + <ExampleContainer expanded={true}> + <Title title="Responsive dialog" theme="light" level={4} /> + <DxcDialog> + <DxcInset space="1.5rem"> + <DxcFlex gap="2rem" direction="column"> + <DxcHeading level={4} text="Example form" /> + <DxcFlex gap="1rem" direction="column"> + <DxcTextInput size="fillParent" label="Name" /> + <DxcTextInput size="fillParent" label="Surname" /> + </DxcFlex> + <DxcFlex justifyContent="flex-end" gap="0.5rem"> + <DxcButton label="Cancel" mode="tertiary" /> + <DxcButton label="Save" /> + </DxcFlex> + </DxcFlex> + </DxcInset> + </DxcDialog> + </ExampleContainer> +); + const ScrollingDialog = () => ( <ExampleContainer expanded={true}> <Title title="Default dialog" theme="light" level={4} /> @@ -240,130 +345,54 @@ const ScrollingDialog = () => ( </ExampleContainer> ); -export const DialogWithInputs = () => ( - <ExampleContainer expanded={true}> - <Title title="Dialog with inputs" theme="light" level={4} /> - <DxcDialog> - <DxcInset space="1.5rem"> - <DxcFlex gap="2rem" direction="column"> - <DxcHeading level={4} text="Example form" /> - <DxcFlex gap="1rem" direction="column"> - <DxcTextInput size="fillParent" label="Name" /> - <DxcTextInput size="fillParent" label="Surname" /> - </DxcFlex> - <DxcAlert - semantic="error" - title="Error" - message={{ - text: "User: arn:aws:xxx::xxxxxxxxxxxx:assumed-role/assure-sandbox-xxxx-xxxxxxxxxxxxxxxxxxxxxxxxxx/sandbox-xxxx-xxxxxxxxxxxxxxxxxx is not authorized to perform: lambda:xxxxxxxxxxxxxx on resource: arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:sandbox-xxxx-xx-xxxxxxx-xxxxxxx-lambda because no identity-based policy allows the lambda:xxxxxxxxxxxxxx action", - }} - /> - <DxcFlex justifyContent="flex-end" gap="0.5rem"> - <DxcButton label="Cancel" mode="tertiary" /> - <DxcButton label="Save" /> - </DxcFlex> - </DxcFlex> - </DxcInset> - </DxcDialog> - </ExampleContainer> -); +type Story = StoryObj<typeof DxcDialog>; -const RespDialog = () => ( - <ExampleContainer expanded={true}> - <Title title="Responsive dialog" theme="light" level={4} /> - <DxcDialog> - <DxcInset space="1.5rem"> - <DxcFlex gap="2rem" direction="column"> - <DxcHeading level={4} text="Example form" /> - <DxcFlex gap="1rem" direction="column"> - <DxcTextInput size="fillParent" label="Name" /> - <DxcTextInput size="fillParent" label="Surname" /> - </DxcFlex> - <DxcFlex justifyContent="flex-end" gap="0.5rem"> - <DxcButton label="Cancel" mode="tertiary" /> - <DxcButton label="Save" /> - </DxcFlex> - </DxcFlex> - </DxcInset> - </DxcDialog> - </ExampleContainer> -); +export const DefaultDialog: Story = { + render: Dialog, +}; -export const DialogWithoutOverlay = () => ( - <ExampleContainer expanded={true}> - <Title title="Dialog Without Overlay" theme="light" level={4} /> - <DxcDialog overlay={false}> - <DxcInset space="1.5rem"> - <DxcFlex direction="column" gap="1rem"> - <DxcHeading level={4} text="Example title" /> - <DxcParagraph> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi egestas luctus porttitor. Donec massa magna, - placerat sit amet felis eget, venenatis fringilla ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing - elit. Donec congue laoreet orci, nec elementum dolor consequat quis. Curabitur rhoncus justo sed dapibus - tincidunt. Vestibulum cursus ut risus sit amet congue. Nunc luctus, urna ullamcorper facilisis Jia Le, risus - eros aliquam erat, ut efficitur ante neque id odio. Nam orci leo, dignissim sit amet dolor ut, congue - gravida enim. Donec rhoncus aliquam nisl, ac cursus enim bibendum vitae. Nunc sit amet elit ornare, - malesuada urna eu, fringilla mauris. Vivamus bibendum turpis est, id elementum purus euismod sit amet. Etiam - sit amet maximus augue. Vivamus erat sapien, ultricies fringilla tellus id, condimentum blandit justo. - Praesent quis nunc dignissim, pharetra neque molestie, molestie lectus. - </DxcParagraph> - </DxcFlex> - </DxcInset> - </DxcDialog> - </ExampleContainer> -); +export const DefaultDialogOpinionated: Story = { + render: DialogOpinionated, +}; -export const DialogCloseVisibleFalse = () => ( - <ExampleContainer expanded={true}> - <Title title="Dialog Close Visible" theme="dark" level={4} /> - <DxcDialog closable={false}> - <DxcInset space="1.5rem"> - <DxcParagraph> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi egestas luctus porttitor. Donec massa magna, - placerat sit amet felis eget, venenatis fringilla ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing - elit. Donec congue laoreet orci, nec elementum dolor consequat quis. Curabitur rhoncus justo sed dapibus - tincidunt. Vestibulum cursus ut risus sit amet congue. Nunc luctus, urna ullamcorper facilisis Jia Le, risus - eros aliquam erat, ut efficitur ante neque id odio. Nam orci leo, dignissim sit amet dolor ut, congue gravida - enim. Donec rhoncus aliquam nisl, ac cursus enim bibendum vitae. Nunc sit amet elit ornare, malesuada urna eu, - fringilla mauris. Vivamus bibendum turpis est, id elementum purus euismod sit amet. Etiam sit amet maximus - augue. Vivamus erat sapien, ultricies fringilla tellus id, condimentum blandit justo. Praesent quis nunc - dignissim, pharetra neque molestie, molestie lectus. - </DxcParagraph> - </DxcInset> - </DxcDialog> - </ExampleContainer> -); +export const DialogWithInputs: Story = { + render: DialogInput, +}; -const customViewports = { - resizedScreen: { - name: "Custom viewport", - styles: { - width: "720px", - height: "900px", - }, - }, +export const DialogWithoutOverlay: Story = { + render: DialogNoOverlay, }; -export const ResponsiveDialog = DefaultDialog.bind({}); -ResponsiveDialog.parameters = { - viewport: { - viewports: customViewports, - defaultViewport: "resizedScreen", +export const DialogCloseVisibleFalse: Story = { + render: DialogCloseNoVisible, +}; + +export const ResponsiveDialog: Story = { + render: Dialog, + parameters: { + viewport: { + viewports: customViewports, + defaultViewport: "resizedScreen", + }, + chromatic: { viewports: [720] }, }, - chromatic: { viewports: [720] }, }; -export const MobileResponsiveDialog = RespDialog.bind({}); -MobileResponsiveDialog.parameters = { - viewport: { - defaultViewport: "iphonex", +export const MobileResponsiveDialog: Story = { + render: RespDialog, + parameters: { + viewport: { + defaultViewport: "iphonex", + }, + chromatic: { viewports: [375] }, }, - chromatic: { viewports: [375] }, }; -export const ScrollDialog = ScrollingDialog.bind({}); -ScrollDialog.play = async () => { - await userEvent.tab(); - await userEvent.tab(); - await userEvent.tab(); +export const ScrollDialog: Story = { + render: ScrollingDialog, + play: async () => { + await userEvent.tab(); + await userEvent.tab(); + await userEvent.tab(); + }, }; diff --git a/packages/lib/src/divider/Divider.stories.tsx b/packages/lib/src/divider/Divider.stories.tsx index 17d744d00e..05bd94a93a 100644 --- a/packages/lib/src/divider/Divider.stories.tsx +++ b/packages/lib/src/divider/Divider.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcFlex from "../flex/Flex"; @@ -7,9 +8,9 @@ import DxcDivider from "./Divider"; export default { title: "Divider", component: DxcDivider, -}; +} as Meta<typeof DxcDivider>; -export const Chromatic = () => ( +const Divider = () => ( <> <Title title="Default" level={4} /> <ExampleContainer> @@ -221,3 +222,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcDivider>; + +export const Chromatic: Story = { + render: Divider, +}; diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index 00eb04e67b..dc7bd65aaf 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -8,11 +8,12 @@ import useTheme from "../utils/useTheme"; import DxcDropdown from "./Dropdown"; import DropdownMenu from "./DropdownMenu"; import { Option } from "./types"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Dropdown", component: DxcDropdown, -}; +} as Meta<typeof DxcDropdown>; const iconSVG = ( <svg viewBox="0 0 24 24" height="24" width="24" fill="currentColor"> @@ -402,28 +403,38 @@ const TooltipTitle = () => ( </ExampleContainer> ); -export const Chromatic = Dropdown.bind({}); -Chromatic.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const buttonList = canvas.getAllByRole("button"); - await userEvent.click(buttonList[buttonList.length - 1]); +type Story = StoryObj<typeof DxcDropdown>; + +export const Chromatic: Story = { + render: Dropdown, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const buttonList = canvas.getAllByRole("button"); + await userEvent.click(buttonList[buttonList.length - 1]); + }, }; -export const OpinionatedThemed = OpinionatedTheme.bind({}); -OpinionatedThemed.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const buttonList = canvas.getAllByRole("button"); - await userEvent.click(buttonList[buttonList.length - 1]); +export const OpinionatedThemed: Story = { + render: OpinionatedTheme, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const buttonList = canvas.getAllByRole("button"); + await userEvent.click(buttonList[buttonList.length - 1]); + }, }; -export const MenuStates = DropdownListStates.bind({}); -MenuStates.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("button")[0]); +export const MenuStates: Story = { + render: DropdownListStates, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("button")[0]); + }, }; -export const MenuTooltip = TooltipTitle.bind({}); -MenuTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.hover(canvas.getByRole("button")); +export const MenuTooltip: Story = { + render: TooltipTitle, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.hover(canvas.getByRole("button")); + }, }; diff --git a/packages/lib/src/file-input/FileInput.stories.tsx b/packages/lib/src/file-input/FileInput.stories.tsx index 7d6690b729..9adc64af92 100644 --- a/packages/lib/src/file-input/FileInput.stories.tsx +++ b/packages/lib/src/file-input/FileInput.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; @@ -7,7 +8,7 @@ import FileItem from "./FileItem"; export default { title: "File Input", component: DxcFileInput, -}; +} as Meta<typeof DxcFileInput>; const picPreview = "https://cdn.mos.cms.futurecdn.net/CAZ6JXi6huSuN4QGE627NR.jpg"; @@ -76,7 +77,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const FileInput = () => ( <> <Title title="File item states" theme="light" level={2} /> <ExampleContainer pseudoState="pseudo-hover"> @@ -632,3 +633,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcFileInput>; + +export const Chromatic: Story = { + render: FileInput, +}; diff --git a/packages/lib/src/flex/Flex.stories.tsx b/packages/lib/src/flex/Flex.stories.tsx index 33dba9e931..99b1a9e265 100644 --- a/packages/lib/src/flex/Flex.stories.tsx +++ b/packages/lib/src/flex/Flex.stories.tsx @@ -1,13 +1,30 @@ import styled from "styled-components"; import Title from "../../.storybook/components/Title"; import DxcFlex from "./Flex"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Flex", component: DxcFlex, -}; +} as Meta<typeof DxcFlex>; + +const Container = styled.div<{ height?: string }>` + display: flex; + background: #f2eafa; + margin: 2.5rem; + ${({ height }) => (height ? `height: ${height}` : "max-height: 150px")}; +`; + +const Placeholder = styled.div<{ minWidth?: string; width?: string }>` + height: 40px; + min-width: ${({ minWidth }) => minWidth ?? "200px"}; + width: ${({ width }) => width}; + border: 1px solid #a46ede; + border-radius: 0.5rem; + background-color: #e5d5f6; +`; -export const Chromatic = () => ( +const Flex = () => ( <> <Title title="Default" level={4} /> <Container> @@ -94,18 +111,8 @@ export const Chromatic = () => ( </> ); -const Container = styled.div<{ height?: string }>` - display: flex; - background: #f2eafa; - margin: 2.5rem; - ${({ height }) => (height ? `height: ${height}` : "max-height: 150px")}; -`; +type Story = StoryObj<typeof DxcFlex>; -const Placeholder = styled.div<{ minWidth?: string; width?: string }>` - height: 40px; - min-width: ${({ minWidth }) => minWidth ?? "200px"}; - width: ${({ width }) => width}; - border: 1px solid #a46ede; - border-radius: 0.5rem; - background-color: #e5d5f6; -`; +export const Chromatic: Story = { + render: Flex, +}; diff --git a/packages/lib/src/footer/Footer.stories.tsx b/packages/lib/src/footer/Footer.stories.tsx index 45cd87cfec..7df8d635c2 100644 --- a/packages/lib/src/footer/Footer.stories.tsx +++ b/packages/lib/src/footer/Footer.stories.tsx @@ -7,6 +7,7 @@ import { HalstackProvider } from "../HalstackContext"; import DxcFlex from "../flex/Flex"; import DxcTypography from "../typography/Typography"; import DxcFooter from "./Footer"; +import { Meta, StoryObj } from "@storybook/react"; const social = [ { @@ -119,7 +120,7 @@ export default { }, }, }, -}; +} as Meta<typeof DxcFooter>; const opinionatedTheme = { footer: { @@ -135,7 +136,7 @@ const info = [ { label: "Example Label", text: "Example" }, ]; -export const Chromatic = () => ( +const Footer = () => ( <> <ExampleContainer> <Title title="Default" theme="light" level={4} /> @@ -216,16 +217,26 @@ const Tooltip = () => { ); }; -export const FooterTooltipFirst = Tooltip.bind({}); -FooterTooltipFirst.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const link = canvas.getAllByRole("link")[0]; - await userEvent.hover(link); +type Story = StoryObj<typeof DxcFooter>; + +export const Chromatic: Story = { + render: Footer, +}; + +export const FooterTooltipFirst: Story = { + render: Tooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const link = canvas.getAllByRole("link")[0]; + await userEvent.hover(link); + }, }; -export const FooterTooltipSecond = Tooltip.bind({}); -FooterTooltipSecond.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const link = canvas.getAllByRole("link")[1]; - await userEvent.hover(link); +export const FooterTooltipSecond: Story = { + render: Tooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const link = canvas.getAllByRole("link")[1]; + await userEvent.hover(link); + }, }; diff --git a/packages/lib/src/grid/Grid.stories.tsx b/packages/lib/src/grid/Grid.stories.tsx index e778d7b8d5..392dc9e335 100644 --- a/packages/lib/src/grid/Grid.stories.tsx +++ b/packages/lib/src/grid/Grid.stories.tsx @@ -3,13 +3,41 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcInset from "../inset/Inset"; import DxcGrid from "./Grid"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Grid", component: DxcGrid, -}; +} as Meta<typeof DxcGrid>; + +const Container = styled.div<{ height?: string }>` + display: grid; + overflow: auto; + margin: 2.5rem; + ${({ height }) => height && `height: ${height}`}; +`; + +const ColoredContainer = styled.div<{ color?: string; width?: string; height?: string }>` + box-sizing: border-box; + display: flex; + justify-content: center; + align-items: center; + background-color: ${({ color }) => color ?? "#e5d5f6"}; + padding: 1rem; + border: 1px solid #a46ede; + border-radius: 0.5rem; + font-family: + Open Sans, + sans-serif; + font-size: 1.5rem; + font-weight: bold; + color: #a46ede; + + ${({ width }) => width && `width: ${width}`}; + ${({ height }) => height && `height: ${height}`}; +`; -export const Chromatic = () => ( +const Grid = () => ( <> <Title title="Default" level={4} /> <ExampleContainer> @@ -192,29 +220,8 @@ export const Chromatic = () => ( </> ); -const Container = styled.div<{ height?: string }>` - display: grid; - overflow: auto; - margin: 2.5rem; - ${({ height }) => height && `height: ${height}`}; -`; +type Story = StoryObj<typeof DxcGrid>; -const ColoredContainer = styled.div<{ color?: string; width?: string; height?: string }>` - box-sizing: border-box; - display: flex; - justify-content: center; - align-items: center; - background-color: ${({ color }) => color ?? "#e5d5f6"}; - padding: 1rem; - border: 1px solid #a46ede; - border-radius: 0.5rem; - font-family: - Open Sans, - sans-serif; - font-size: 1.5rem; - font-weight: bold; - color: #a46ede; - - ${({ width }) => width && `width: ${width}`}; - ${({ height }) => height && `height: ${height}`}; -`; +export const Chromatic: Story = { + render: Grid, +}; diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx index e566438d42..eff9f0d3e2 100644 --- a/packages/lib/src/header/Header.stories.tsx +++ b/packages/lib/src/header/Header.stories.tsx @@ -9,6 +9,7 @@ import DxcFlex from "../flex/Flex"; import { HalstackProvider } from "../HalstackContext"; import DxcLink from "../link/Link"; import DxcHeader from "./Header"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Header", @@ -26,7 +27,7 @@ export default { viewports: INITIAL_VIEWPORTS, }, }, -}; +} as Meta<typeof DxcHeader>; const options: any = [ { @@ -65,7 +66,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Header = () => ( <> <ExampleContainer> <Title title="Default with dropdown" theme="light" level={4} /> @@ -145,7 +146,7 @@ export const Chromatic = () => ( </> ); -export const ResponsiveHeader = () => ( +const Responsive = () => ( <ExampleContainer> <Title title="Responsive" theme="light" level={4} /> <DxcHeader @@ -195,87 +196,108 @@ const RespHeaderMenuOpinionated = () => ( </ExampleContainer> ); -ResponsiveHeader.parameters = { - viewport: { - defaultViewport: "iphonex", - }, - chromatic: { viewports: [375] }, +type Story = StoryObj<typeof DxcHeader>; + +export const Chromatic: Story = { + render: Header, }; -export const ResponsiveHeaderFocus = RespHeaderFocus.bind({}); -ResponsiveHeaderFocus.parameters = { - viewport: { - defaultViewport: "iphonex", +export const ResponsiveHeader: Story = { + render: Responsive, + parameters: { + viewport: { + defaultViewport: "iphonex", + }, + chromatic: { viewports: [375] }, }, - chromatic: { viewports: [375] }, -}; -ResponsiveHeaderFocus.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await waitFor(() => canvas.findByText("Menu")); }; -export const ResponsiveHeaderHover = RespHeaderHover.bind({}); -ResponsiveHeaderHover.parameters = { - viewport: { - defaultViewport: "iphonex", +export const ResponsiveHeaderFocus: Story = { + render: RespHeaderFocus, + parameters: { + viewport: { + defaultViewport: "iphonex", + }, + chromatic: { viewports: [375] }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await waitFor(() => canvas.findByText("Menu")); }, - chromatic: { viewports: [375] }, -}; -ResponsiveHeaderHover.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await waitFor(() => canvas.findByText("Menu")); }; -export const ResponsiveHeaderMenuMobile = RespHeaderMenuMobile.bind({}); -ResponsiveHeaderMenuMobile.parameters = { - viewport: { - defaultViewport: "iphonex", +export const ResponsiveHeaderHover: Story = { + render: RespHeaderHover, + parameters: { + viewport: { + defaultViewport: "iphonex", + }, + chromatic: { viewports: [375] }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await waitFor(() => canvas.findByText("Menu")); }, - chromatic: { viewports: [375] }, -}; -ResponsiveHeaderMenuMobile.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await waitFor(() => canvas.findByText("Menu")); - await userEvent.click(canvas.getByText("Menu")); }; -export const ResponsiveHeaderMenuTablet = RespHeaderMenuTablet.bind({}); -ResponsiveHeaderMenuTablet.parameters = { - viewport: { - defaultViewport: "pixelxl", +export const ResponsiveHeaderMenuMobile: Story = { + render: RespHeaderMenuMobile, + parameters: { + viewport: { + defaultViewport: "iphonex", + }, + chromatic: { viewports: [375] }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await waitFor(() => canvas.findByText("Menu")); + await userEvent.click(canvas.getByText("Menu")); }, - chromatic: { viewports: [720] }, -}; -ResponsiveHeaderMenuTablet.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await waitFor(() => canvas.findByText("Menu")); - await userEvent.click(canvas.getByText("Menu")); }; -export const ResponsiveHeaderMenuOpinionated = RespHeaderMenuOpinionated.bind({}); -ResponsiveHeaderMenuOpinionated.parameters = { - viewport: { - defaultViewport: "pixelxl", +export const ResponsiveHeaderMenuTablet: Story = { + render: RespHeaderMenuTablet, + parameters: { + viewport: { + defaultViewport: "pixelxl", + }, + chromatic: { viewports: [720] }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await waitFor(() => canvas.findByText("Menu")); + await userEvent.click(canvas.getByText("Menu")); }, - chromatic: { viewports: [720] }, -}; -ResponsiveHeaderMenuOpinionated.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await waitFor(() => canvas.findByText("Menu")); - await userEvent.click(canvas.getByText("Menu")); }; -export const ResponsiveHeaderTooltip = RespHeaderMenuMobile.bind({}); -ResponsiveHeaderTooltip.parameters = { - viewport: { - defaultViewport: "iphonex", +export const ResponsiveHeaderMenuOpinionated: Story = { + render: RespHeaderMenuOpinionated, + parameters: { + viewport: { + defaultViewport: "pixelxl", + }, + chromatic: { viewports: [720] }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await waitFor(() => canvas.findByText("Menu")); + await userEvent.click(canvas.getByText("Menu")); }, - chromatic: { viewports: [375] }, }; -ResponsiveHeaderTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await waitFor(() => canvas.findByText("Menu")); - await userEvent.click(canvas.getByText("Menu")); - const closeButton = canvas.getAllByRole("button")[1]; - await userEvent.hover(closeButton); + +export const ResponsiveHeaderTooltip: Story = { + render: RespHeaderMenuMobile, + parameters: { + viewport: { + defaultViewport: "iphonex", + }, + chromatic: { viewports: [375] }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await waitFor(() => canvas.findByText("Menu")); + await userEvent.click(canvas.getByText("Menu")); + const closeButton = canvas.getAllByRole("button")[1]; + await userEvent.hover(closeButton); + }, }; diff --git a/packages/lib/src/heading/Heading.stories.tsx b/packages/lib/src/heading/Heading.stories.tsx index b05b939245..86b03b1886 100644 --- a/packages/lib/src/heading/Heading.stories.tsx +++ b/packages/lib/src/heading/Heading.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcHeading from "./Heading"; @@ -5,9 +6,9 @@ import DxcHeading from "./Heading"; export default { title: "Heading", component: DxcHeading, -}; +} as Meta<typeof DxcHeading>; -export const Chromatic = () => ( +const Heading = () => ( <> <Title title="Levels" theme="light" level={2} /> <ExampleContainer> @@ -51,3 +52,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcHeading>; + +export const Chromatic: Story = { + render: Heading, +}; diff --git a/packages/lib/src/icon/Icon.stories.tsx b/packages/lib/src/icon/Icon.stories.tsx index 9ad97def09..6c82ef5066 100644 --- a/packages/lib/src/icon/Icon.stories.tsx +++ b/packages/lib/src/icon/Icon.stories.tsx @@ -2,13 +2,14 @@ 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", component: DxcIcon, -}; +} as Meta<typeof DxcIcon>; -export const Chromatic = () => ( +const Icon = () => ( <> <Title title="Icon component" theme="light" level={2} /> <ExampleContainer> @@ -25,3 +26,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcIcon>; + +export const Chromatic: Story = { + render: Icon, +}; diff --git a/packages/lib/src/image/Image.stories.tsx b/packages/lib/src/image/Image.stories.tsx index 22f548bf22..8fcc668c4e 100644 --- a/packages/lib/src/image/Image.stories.tsx +++ b/packages/lib/src/image/Image.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcFlex from "../flex/Flex"; @@ -8,9 +9,9 @@ import DxcImage from "./Image"; export default { title: "Image", component: DxcImage, -}; +} as Meta<typeof DxcImage>; -export const Chromatic = () => ( +const Image = () => ( <> <Title title="Image component" theme="light" level={2} /> <ExampleContainer> @@ -126,3 +127,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcImage>; + +export const Chromatic: Story = { + render: Image, +}; diff --git a/packages/lib/src/inset/Inset.stories.tsx b/packages/lib/src/inset/Inset.stories.tsx index d746e01bda..881073928f 100644 --- a/packages/lib/src/inset/Inset.stories.tsx +++ b/packages/lib/src/inset/Inset.stories.tsx @@ -2,13 +2,27 @@ import styled from "styled-components"; import Title from "../../.storybook/components/Title"; import DxcFlex from "./../flex/Flex"; import DxcInset from "./Inset"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Inset", component: DxcInset, -}; +} as Meta<typeof DxcInset>; + +const Container = styled.div` + background: #f2eafa; + margin: 2.5rem; +`; + +const Placeholder = styled.div` + min-height: 40px; + min-width: 120px; + border: 1px solid #a46ede; + border-radius: 0.5rem; + background-color: #e5d5f6; +`; -export const Chromatic = () => ( +const Inset = () => ( <> <Title title="Default" level={4} /> <Container> @@ -215,15 +229,8 @@ export const Chromatic = () => ( </> ); -const Container = styled.div` - background: #f2eafa; - margin: 2.5rem; -`; +type Story = StoryObj<typeof DxcInset>; -const Placeholder = styled.div` - min-height: 40px; - min-width: 120px; - border: 1px solid #a46ede; - border-radius: 0.5rem; - background-color: #e5d5f6; -`; +export const Chromatic: Story = { + render: Inset, +}; diff --git a/packages/lib/src/layout/ApplicationLayout.stories.tsx b/packages/lib/src/layout/ApplicationLayout.stories.tsx index 98d91b9692..09d0a621b0 100644 --- a/packages/lib/src/layout/ApplicationLayout.stories.tsx +++ b/packages/lib/src/layout/ApplicationLayout.stories.tsx @@ -2,6 +2,7 @@ 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", @@ -11,9 +12,9 @@ export default { viewports: INITIAL_VIEWPORTS, }, }, -}; +} as Meta<typeof DxcApplicationLayout>; -export const DefaultApplicationLayout = () => ( +const ApplicationLayout = () => ( <> <DxcApplicationLayout> <DxcApplicationLayout.Main> @@ -27,7 +28,7 @@ export const DefaultApplicationLayout = () => ( </> ); -export const ApplicationLayoutWithDefaultSidenav = () => ( +const ApplicationLayoutDefaultSidenav = () => ( <> <DxcApplicationLayout sidenav={ @@ -58,7 +59,7 @@ export const ApplicationLayoutWithDefaultSidenav = () => ( </> ); -export const ApplicationLayoutWithResponsiveSidenav = () => ( +const ApplicationLayoutResponsiveSidenav = () => ( <> <DxcApplicationLayout visibilityToggleLabel="Example" @@ -90,14 +91,7 @@ export const ApplicationLayoutWithResponsiveSidenav = () => ( </> ); -ApplicationLayoutWithResponsiveSidenav.parameters = { - viewport: { - defaultViewport: "pixel", - }, - chromatic: { viewports: [540] }, -}; - -export const ApplicationLayoutWithCustomHeader = () => ( +const ApplicationLayoutCustomHeader = () => ( <> <DxcApplicationLayout header={<p>Custom Header</p>} @@ -129,7 +123,7 @@ export const ApplicationLayoutWithCustomHeader = () => ( </> ); -export const ApplicationLayoutWithCustomFooter = () => ( +const ApplicationLayoutCustomFooter = () => ( <> <DxcApplicationLayout footer={<p>Custom Footer</p>} @@ -179,15 +173,43 @@ const Tooltip = () => ( </> ); -export const ApplicationLayoutTooltip = Tooltip.bind({}); -ApplicationLayoutTooltip.parameters = { - viewport: { - defaultViewport: "pixel", +type Story = StoryObj<typeof DxcApplicationLayout>; + +export const DefaultApplicationLayout: Story = { + render: ApplicationLayout, +}; +export const ApplicationLayoutWithDefaultSidenav: Story = { + render: ApplicationLayoutDefaultSidenav, +}; +export const ApplicationLayoutWithResponsiveSidenav: Story = { + render: ApplicationLayoutResponsiveSidenav, + parameters: { + viewport: { + defaultViewport: "pixel", + }, + chromatic: { viewports: [540] }, }, - chromatic: { viewports: [540] }, }; -ApplicationLayoutTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const toggleVisibility = await canvas.findByRole("button"); - await userEvent.hover(toggleVisibility); + +export const ApplicationLayoutWithCustomHeader: Story = { + render: ApplicationLayoutCustomHeader, +}; + +export const ApplicationLayoutWithCustomFooter: Story = { + render: ApplicationLayoutCustomFooter, +}; + +export const ApplicationLayoutTooltip: Story = { + render: Tooltip, + parameters: { + viewport: { + defaultViewport: "pixel", + }, + chromatic: { viewports: [540] }, + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const toggleVisibility = await canvas.findByRole("button"); + await userEvent.hover(toggleVisibility); + }, }; diff --git a/packages/lib/src/link/Link.stories.tsx b/packages/lib/src/link/Link.stories.tsx index 1d42d20f02..8565fb03ec 100644 --- a/packages/lib/src/link/Link.stories.tsx +++ b/packages/lib/src/link/Link.stories.tsx @@ -3,11 +3,12 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcLink from "./Link"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Link", component: DxcLink, -}; +} as Meta<typeof DxcLink>; const icon = ( <svg viewBox="0 0 24 24" enableBackground="new 0 0 24 24" fill="currentColor"> @@ -26,7 +27,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Link = () => ( <> <Title title="With anchor" theme="light" level={2} /> <ExampleContainer> @@ -207,3 +208,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcLink>; + +export const Chromatic: Story = { + render: Link, +}; diff --git a/packages/lib/src/nav-tabs/NavTabs.stories.tsx b/packages/lib/src/nav-tabs/NavTabs.stories.tsx index fd4440f82d..1d1c48e82d 100644 --- a/packages/lib/src/nav-tabs/NavTabs.stories.tsx +++ b/packages/lib/src/nav-tabs/NavTabs.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcContainer from "../container/Container"; @@ -7,7 +8,7 @@ import DxcNavTabs from "./NavTabs"; export default { title: "Nav Tabs", component: DxcNavTabs, -}; +} as Meta<typeof DxcNavTabs>; const iconSVG = ( <svg viewBox="0 0 24 24" fill="currentColor"> @@ -27,7 +28,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const NavTabs = () => ( <> <ExampleContainer> <Title title="Only label" theme="light" level={4} /> @@ -291,3 +292,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcNavTabs>; + +export const Chromatic: Story = { + render: NavTabs, +}; diff --git a/packages/lib/src/number-input/NumberInput.stories.tsx b/packages/lib/src/number-input/NumberInput.stories.tsx index 7bf005a95f..07a818b5f8 100644 --- a/packages/lib/src/number-input/NumberInput.stories.tsx +++ b/packages/lib/src/number-input/NumberInput.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcFlex from "../flex/Flex"; @@ -6,9 +7,9 @@ import DxcNumberInput from "./NumberInput"; export default { title: "Number Input", component: DxcNumberInput, -}; +} as Meta<typeof DxcNumberInput>; -export const Chromatic = () => ( +const NumberInput = () => ( <> <ExampleContainer> <Title title="Without label" theme="light" level={4} /> @@ -123,3 +124,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcNumberInput>; + +export const Chromatic: Story = { + render: NumberInput, +}; diff --git a/packages/lib/src/paginator/Paginator.stories.tsx b/packages/lib/src/paginator/Paginator.stories.tsx index 2a5f17e2f5..f44fbccdf3 100644 --- a/packages/lib/src/paginator/Paginator.stories.tsx +++ b/packages/lib/src/paginator/Paginator.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; @@ -6,7 +7,7 @@ import DxcPaginator from "./Paginator"; export default { title: "Paginator", component: DxcPaginator, -}; +} as Meta<typeof DxcPaginator>; const opinionatedTheme = { paginator: { @@ -15,7 +16,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Paginator = () => ( <> <ExampleContainer> <Title title="Default" theme="light" level={4} /> @@ -84,3 +85,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcPaginator>; + +export const Chromatic: Story = { + render: Paginator, +}; \ No newline at end of file diff --git a/packages/lib/src/paragraph/Paragraph.stories.tsx b/packages/lib/src/paragraph/Paragraph.stories.tsx index 4e8a70111c..e40b97173d 100644 --- a/packages/lib/src/paragraph/Paragraph.stories.tsx +++ b/packages/lib/src/paragraph/Paragraph.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcParagraph from "./Paragraph"; @@ -5,9 +6,9 @@ import DxcParagraph from "./Paragraph"; export default { title: "Paragraph", component: DxcParagraph, -}; +} as Meta<typeof DxcParagraph>; -export const Chromatic = () => ( +const Paragraph = () => ( <> <ExampleContainer> <Title title="Default Paragraph" level={4} /> @@ -24,3 +25,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcParagraph>; + +export const Chromatic: Story = { + render: Paragraph, +}; diff --git a/packages/lib/src/password-input/PasswordInput.stories.tsx b/packages/lib/src/password-input/PasswordInput.stories.tsx index db5f8b0f91..085c2569b9 100644 --- a/packages/lib/src/password-input/PasswordInput.stories.tsx +++ b/packages/lib/src/password-input/PasswordInput.stories.tsx @@ -3,13 +3,14 @@ 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", component: DxcPasswordInput, -}; +} as Meta<typeof DxcPasswordInput>; -export const Chromatic = () => ( +const PasswordInput = () => ( <> <ExampleContainer> <Title title="Without label" theme="light" level={4} /> @@ -92,16 +93,24 @@ export const Chromatic = () => ( </> ); -const Password = () => ( +const PasswordInteraction = () => ( <ExampleContainer expanded> <Title title="Show password" theme="light" level={4} /> <DxcPasswordInput label="Password input" value="Password" /> </ExampleContainer> ); -export const ShowPassword = Password.bind({}); -ShowPassword.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const passwordBtn = canvas.getByRole("button"); - await userEvent.click(passwordBtn); +type Story = StoryObj<typeof DxcPasswordInput>; + +export const Chromatic: Story = { + render: PasswordInput, +}; + +export const ShowPassword: Story = { + render: PasswordInteraction, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const passwordBtn = canvas.getByRole("button"); + await userEvent.click(passwordBtn); + }, }; diff --git a/packages/lib/src/progress-bar/ProgressBar.stories.tsx b/packages/lib/src/progress-bar/ProgressBar.stories.tsx index 872cb2f98e..7e078f4cdd 100644 --- a/packages/lib/src/progress-bar/ProgressBar.stories.tsx +++ b/packages/lib/src/progress-bar/ProgressBar.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; @@ -6,7 +7,7 @@ import DxcProgressBar from "./ProgressBar"; export default { title: "Progress Bar", component: DxcProgressBar, -}; +} as Meta<typeof DxcProgressBar>; const opinionatedTheme = { progressBar: { @@ -18,7 +19,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const ProgressBar = () => ( <> <ExampleContainer> <Title title="Without labels" theme="light" level={4} /> @@ -75,14 +76,14 @@ export const Chromatic = () => ( </> ); -export const ProgressBarOverlay = () => ( +const ProgressBarWithOverlay = () => ( <ExampleContainer> <Title title="Overlay" theme="dark" level={4} /> <DxcProgressBar label="Overlay" helperText="Helper text" overlay showValue value={50} /> </ExampleContainer> ); -export const ProgressBarOverlayOpinionated = () => ( +const ProgressBarWithOverlayOpinionated = () => ( <ExampleContainer> <Title title="Overlay" theme="dark" level={4} /> <HalstackProvider theme={opinionatedTheme}> @@ -90,3 +91,17 @@ export const ProgressBarOverlayOpinionated = () => ( </HalstackProvider> </ExampleContainer> ); + +type Story = StoryObj<typeof DxcProgressBar>; + +export const Chromatic: Story = { + render: ProgressBar, +}; + +export const ProgressBarOverlay: Story = { + render: ProgressBarWithOverlay, +}; + +export const ProgressBarOverlayOpinionated: Story = { + render: ProgressBarWithOverlayOpinionated, +}; diff --git a/packages/lib/src/quick-nav/QuickNav.stories.tsx b/packages/lib/src/quick-nav/QuickNav.stories.tsx index 3c17a89edb..9f52a423b2 100644 --- a/packages/lib/src/quick-nav/QuickNav.stories.tsx +++ b/packages/lib/src/quick-nav/QuickNav.stories.tsx @@ -5,11 +5,12 @@ import { HalstackProvider } from "../HalstackContext"; 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", component: DxcQuickNav, -}; +} as Meta<typeof DxcQuickNav>; const opinionatedTheme = { quickNav: { @@ -77,7 +78,30 @@ const links = [ }, ]; -export const Chromatic = () => ( +const Container = styled.div` + display: flex; + flex-direction: row; + width: 100%; +`; + +const ContentContainer = styled.div` + display: flex; + flex-direction: column; + padding: 60px; + margin: 0 auto; + width: 800px; +`; + +const Content = styled.div``; + +const QuickNavContainer = styled.div` + max-height: calc(100vh - 100px); + position: sticky; + top: 100px; + width: 300px; +`; + +const QuickNav = () => ( <> <ExampleContainer> <Title title="Default" level={4} /> @@ -331,25 +355,8 @@ export const Chromatic = () => ( </> ); -const Container = styled.div` - display: flex; - flex-direction: row; - width: 100%; -`; +type Story = StoryObj<typeof DxcQuickNav>; -const ContentContainer = styled.div` - display: flex; - flex-direction: column; - padding: 60px; - margin: 0 auto; - width: 800px; -`; - -const Content = styled.div``; - -const QuickNavContainer = styled.div` - max-height: calc(100vh - 100px); - position: sticky; - top: 100px; - width: 300px; -`; +export const Chromatic: Story = { + render: QuickNav, +}; diff --git a/packages/lib/src/radio-group/RadioGroup.stories.tsx b/packages/lib/src/radio-group/RadioGroup.stories.tsx index 82812dfbdd..82d7356921 100644 --- a/packages/lib/src/radio-group/RadioGroup.stories.tsx +++ b/packages/lib/src/radio-group/RadioGroup.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; @@ -6,7 +7,7 @@ import DxcRadioGroup from "./RadioGroup"; export default { title: "Radio Group", component: DxcRadioGroup, -}; +} as Meta<typeof DxcRadioGroup>; const single_option = [{ label: "Option A", value: "A" }]; @@ -26,7 +27,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const RadioGroup = () => ( <> <Title title="Radio input states" theme="light" level={2} /> <ExampleContainer> @@ -211,3 +212,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcRadioGroup>; + +export const Chromatic: Story = { + render: RadioGroup, +}; diff --git a/packages/lib/src/resultset-table/ResultsetTable.stories.tsx b/packages/lib/src/resultset-table/ResultsetTable.stories.tsx index b398347b30..8f003c64f2 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.stories.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.stories.tsx @@ -7,6 +7,7 @@ import { disabledRules } from "../../test/accessibility/rules/specific/resultset import { HalstackProvider } from "../HalstackContext"; import DxcResultsetTable from "./ResultsetTable"; import { ActionsPropsType } from "../table/types"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Resultset Table", @@ -21,7 +22,7 @@ export default { }, }, }, -}; +} as Meta<typeof DxcResultsetTable>; const deleteIcon = ( <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"> @@ -269,7 +270,11 @@ const longRows = [ ], ]; -export const Chromatic = () => ( +const SmallContainer = styled.div` + width: 500px; +`; + +const ResultsetTable = () => ( <> <ExampleContainer> <Title title="Sortable table" theme="light" level={4} /> @@ -352,10 +357,6 @@ export const Chromatic = () => ( </> ); -const SmallContainer = styled.div` - width: 500px; -`; - const ResultsetTableAsc = () => ( <ExampleContainer> <Title title="Ascendant sorting" theme="light" level={4} /> @@ -364,15 +365,6 @@ const ResultsetTableAsc = () => ( </ExampleContainer> ); -export const AscendentSorting = ResultsetTableAsc.bind({}); -AscendentSorting.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const idHeader = canvas.getAllByRole("button")[0]; - const idHeader2 = canvas.getAllByRole("button")[6]; - await userEvent.click(idHeader); - await userEvent.click(idHeader2); -}; - const ResultsetTableDesc = () => ( <ExampleContainer> <Title title="Descendant sorting" theme="light" level={4} /> @@ -381,17 +373,6 @@ const ResultsetTableDesc = () => ( </ExampleContainer> ); -export const DescendantSorting = ResultsetTableDesc.bind({}); -DescendantSorting.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const nameHeader = canvas.getAllByRole("button")[1]; - const nameHeader2 = canvas.getAllByRole("button")[7]; - await userEvent.click(nameHeader); - await userEvent.click(nameHeader); - await userEvent.click(nameHeader2); - await userEvent.click(nameHeader2); -}; - const ResultsetTableMiddle = () => ( <ExampleContainer> <Title title="Middle page" theme="light" level={4} /> @@ -399,13 +380,6 @@ const ResultsetTableMiddle = () => ( </ExampleContainer> ); -export const MiddlePage = ResultsetTableMiddle.bind({}); -MiddlePage.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const nextButton = canvas.getAllByRole("button")[2]; - await userEvent.click(nextButton); -}; - const ResultsetTableLast = () => ( <ExampleContainer> <Title title="Last page" theme="light" level={4} /> @@ -413,13 +387,6 @@ const ResultsetTableLast = () => ( </ExampleContainer> ); -export const LastPage = ResultsetTableLast.bind({}); -LastPage.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const nextButton = canvas.getAllByRole("button")[3]; - await userEvent.click(nextButton); -}; - const ResultsetActionsCellDropdown = () => ( <ExampleContainer> <Title title="Dropdown Action" theme="light" level={4} /> @@ -431,9 +398,59 @@ const ResultsetActionsCellDropdown = () => ( </ExampleContainer> ); -export const DropdownAction = ResultsetActionsCellDropdown.bind({}); -DropdownAction.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const dropdown = canvas.getAllByRole("button")[5]; - userEvent.click(dropdown); +type Story = StoryObj<typeof DxcResultsetTable>; + +export const Chromatic: Story = { + render: ResultsetTable, +}; + +export const AscendentSorting: Story = { + render: ResultsetTableAsc, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const idHeader = canvas.getAllByRole("button")[0]; + const idHeader2 = canvas.getAllByRole("button")[6]; + await userEvent.click(idHeader); + await userEvent.click(idHeader2); + }, +}; + +export const DescendantSorting: Story = { + render: ResultsetTableDesc, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const nameHeader = canvas.getAllByRole("button")[1]; + const nameHeader2 = canvas.getAllByRole("button")[7]; + await userEvent.click(nameHeader); + await userEvent.click(nameHeader); + await userEvent.click(nameHeader2); + await userEvent.click(nameHeader2); + }, +}; + +export const MiddlePage: Story = { + render: ResultsetTableMiddle, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const nextButton = canvas.getAllByRole("button")[2]; + await userEvent.click(nextButton); + }, +}; + +export const LastPage: Story = { + render: ResultsetTableLast, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const nextButton = canvas.getAllByRole("button")[3]; + await userEvent.click(nextButton); + }, +}; + +export const DropdownAction: Story = { + render: ResultsetActionsCellDropdown, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const dropdown = canvas.getAllByRole("button")[5]; + userEvent.click(dropdown); + }, }; diff --git a/packages/lib/src/select/Select.stories.tsx b/packages/lib/src/select/Select.stories.tsx index 10ef505083..1bd23f4369 100644 --- a/packages/lib/src/select/Select.stories.tsx +++ b/packages/lib/src/select/Select.stories.tsx @@ -9,6 +9,7 @@ import { HalstackProvider } from "../HalstackContext"; import useTheme from "../utils/useTheme"; import Listbox from "./Listbox"; import DxcSelect from "./Select"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Select", @@ -23,7 +24,7 @@ export default { }, }, }, -}; +} as Meta<typeof DxcSelect>; const one_option = [{ label: "Option 01", value: "1" }]; @@ -673,95 +674,125 @@ const TooltipClear = () => ( </ExampleContainer> ); -export const Chromatic = Select.bind({}); -Chromatic.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[24]); +type Story = StoryObj<typeof DxcSelect>; + +export const Chromatic: Story = { + render: Select, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("combobox")[24]); + }, }; -export const OpinionatedTheme = Opinionated.bind({}); -OpinionatedTheme.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[2]); +export const OpinionatedTheme: Story = { + render: Opinionated, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("combobox")[2]); + }, }; -export const ListboxStates = SelectListbox.bind({}); -ListboxStates.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const select = canvas.getByRole("combobox"); - await userEvent.click(select); +export const ListboxStates: Story = { + render: SelectListbox, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const select = canvas.getByRole("combobox"); + await userEvent.click(select); + }, }; -export const Searchable = SearchableSelect.bind({}); -Searchable.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.type(canvas.getByRole("combobox"), "r"); +export const Searchable: Story = { + render: SearchableSelect, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.type(canvas.getByRole("combobox"), "r"); + }, }; -export const SearchableWithValue = SearchValue.bind({}); -SearchableWithValue.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole("combobox")); +export const SearchableWithValue: Story = { + render: SearchValue, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getByRole("combobox")); + }, }; -export const MultipleSearchableWithValue = MultipleSearchable.bind({}); -MultipleSearchableWithValue.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[0]); +export const MultipleSearchableWithValue: Story = { + render: MultipleSearchable, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("combobox")[0]); + }, }; -export const GroupOptionsDisplayed = DefaultGroupedOptionsSelect.bind({}); -GroupOptionsDisplayed.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const select = canvas.getByRole("combobox"); - await userEvent.click(select); +export const GroupOptionsDisplayed: Story = { + render: DefaultGroupedOptionsSelect, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const select = canvas.getByRole("combobox"); + await userEvent.click(select); + }, }; -export const GroupOptionsDisplayedOpinionated = DefaultGroupedOptionsSelectOpinionated.bind({}); -GroupOptionsDisplayedOpinionated.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const select = canvas.getByRole("combobox"); - await userEvent.click(select); +export const GroupOptionsDisplayedOpinionated: Story = { + render: DefaultGroupedOptionsSelectOpinionated, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const select = canvas.getByRole("combobox"); + await userEvent.click(select); + }, }; -export const MultipleOptionsDisplayed = MultipleSelect.bind({}); -MultipleOptionsDisplayed.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[0]); +export const MultipleOptionsDisplayed: Story = { + render: MultipleSelect, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.click(canvas.getAllByRole("combobox")[0]); + }, }; -export const MultipleGroupedOptionsDisplayed = MultipleGroupedOptionsSelect.bind({}); -MultipleGroupedOptionsDisplayed.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const select = canvas.getByRole("combobox"); - await userEvent.click(select); +export const MultipleGroupedOptionsDisplayed: Story = { + render: MultipleGroupedOptionsSelect, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const select = canvas.getByRole("combobox"); + await userEvent.click(select); + }, }; -export const ValueWithEllipsisTooltip = TooltipValue.bind({}); -ValueWithEllipsisTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.hover(canvas.getByText("Option 01, Option 02, Option 03, Option 04")); - await userEvent.hover(canvas.getByText("Option 01, Option 02, Option 03, Option 04")); +export const ValueWithEllipsisTooltip: Story = { + render: TooltipValue, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.hover(canvas.getByText("Option 01, Option 02, Option 03, Option 04")); + await userEvent.hover(canvas.getByText("Option 01, Option 02, Option 03, Option 04")); + }, }; -export const ListboxOptionWithEllipsisTooltip = TooltipOption.bind({}); -ListboxOptionWithEllipsisTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.hover(canvas.getByText("Optiond123456789012345678901234567890123451231231")); - await userEvent.hover(canvas.getByText("Optiond123456789012345678901234567890123451231231")); +export const ListboxOptionWithEllipsisTooltip: Story = { + render: TooltipOption, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.hover(canvas.getByText("Optiond123456789012345678901234567890123451231231")); + await userEvent.hover(canvas.getByText("Optiond123456789012345678901234567890123451231231")); + }, }; -export const ClearActionTooltip = TooltipClear.bind({}); -ClearActionTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const clearSelectionButton = canvas.getByRole("button"); - await userEvent.hover(clearSelectionButton); +export const ClearActionTooltip: Story = { + render: TooltipClear, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const clearSelectionButton = canvas.getByRole("button"); + await userEvent.hover(clearSelectionButton); + }, }; -export const SearchableClearActionTooltip = SearchableSelect.bind({}); -SearchableClearActionTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.type(canvas.getByRole("combobox"), "r"); - const clearSelectionButton = canvas.getByRole("button"); - await userEvent.hover(clearSelectionButton); +export const SearchableClearActionTooltip: Story = { + render: SearchableSelect, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.type(canvas.getByRole("combobox"), "r"); + const clearSelectionButton = canvas.getByRole("button"); + await userEvent.hover(clearSelectionButton); + }, }; diff --git a/packages/lib/src/sidenav/Sidenav.stories.tsx b/packages/lib/src/sidenav/Sidenav.stories.tsx index 45d3a85547..9f30048ffd 100644 --- a/packages/lib/src/sidenav/Sidenav.stories.tsx +++ b/packages/lib/src/sidenav/Sidenav.stories.tsx @@ -5,11 +5,12 @@ import { HalstackProvider } from "../HalstackContext"; import DxcInset from "../inset/Inset"; import DxcSelect from "../select/Select"; import DxcSidenav from "./Sidenav"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Sidenav", component: DxcSidenav, -}; +} as Meta<typeof DxcSidenav>; const iconSVG = ( <svg @@ -44,7 +45,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const SideNav = () => ( <> <ExampleContainer> <Title title="Default sidenav" theme="light" level={4} /> @@ -249,28 +250,40 @@ const ActiveGroupSidenav = () => ( </ExampleContainer> ); -export const CollapsableGroup = CollapsedGroupSidenav.bind({}); -CollapsableGroup.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const collapsableGroups = canvas.getAllByText("Collapsed Group"); - collapsableGroups.forEach((group) => { - userEvent.click(group); - }); +type Story = StoryObj<typeof DxcSidenav>; + +export const Chromatic: Story = { + render: SideNav, }; -export const CollapsedHoverGroup = HoveredGroupSidenav.bind({}); -CollapsedHoverGroup.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const collapsableGroups = canvas.getAllByText("Collapsed Group"); - collapsableGroups.forEach((group) => { - userEvent.click(group); - }); - await new Promise((resolve) => setTimeout(resolve, 1000)); +export const CollapsableGroup: Story = { + render: CollapsedGroupSidenav, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const collapsableGroups = canvas.getAllByText("Collapsed Group"); + collapsableGroups.forEach((group) => { + userEvent.click(group); + }); + }, }; -export const CollapsedActiveGroup = ActiveGroupSidenav.bind({}); -CollapsedActiveGroup.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const collapsableGroups = canvas.getAllByText("Collapsed Group"); - userEvent.click(collapsableGroups[0]); +export const CollapsedHoverGroup: Story = { + render: HoveredGroupSidenav, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const collapsableGroups = canvas.getAllByText("Collapsed Group"); + collapsableGroups.forEach((group) => { + userEvent.click(group); + }); + await new Promise((resolve) => setTimeout(resolve, 1000)); + }, +}; + +export const CollapsedActiveGroup: Story = { + render: ActiveGroupSidenav, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const collapsableGroups = canvas.getAllByText("Collapsed Group"); + userEvent.click(collapsableGroups[0]); + }, }; diff --git a/packages/lib/src/slider/Slider.stories.tsx b/packages/lib/src/slider/Slider.stories.tsx index 916a6c5ae2..6165e4cad4 100644 --- a/packages/lib/src/slider/Slider.stories.tsx +++ b/packages/lib/src/slider/Slider.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; @@ -6,7 +7,7 @@ import DxcSlider from "./Slider"; export default { title: "Slider", component: DxcSlider, -}; +} as Meta<typeof DxcSlider>; const labelFormat = (value) => `${value}E100000000000000000000000`; @@ -18,7 +19,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Slider = () => ( <> <Title title="States" theme="light" level={2} /> <ExampleContainer pseudoState="pseudo-hover"> @@ -177,3 +178,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcSlider>; + +export const Chromatic: Story = { + render: Slider, +}; diff --git a/packages/lib/src/spinner/Spinner.stories.tsx b/packages/lib/src/spinner/Spinner.stories.tsx index e23e9c8633..c52854fcfc 100644 --- a/packages/lib/src/spinner/Spinner.stories.tsx +++ b/packages/lib/src/spinner/Spinner.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; @@ -6,7 +7,7 @@ import DxcSpinner from "./Spinner"; export default { title: "Spinner", component: DxcSpinner, -}; +} as Meta<typeof DxcSpinner>; const opinionatedTheme = { spinner: { @@ -18,7 +19,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Spinner = () => ( <> <ExampleContainer> <Title title="With label" theme="light" level={4} /> @@ -83,42 +84,42 @@ export const Chromatic = () => ( </> ); -export const SpinnerOverlay = () => ( +const SpinnerOverlay = () => ( <ExampleContainer> <Title title="Mode overlay" theme="light" level={4} /> <DxcSpinner mode="overlay" value={25}></DxcSpinner> </ExampleContainer> ); -export const SpinnerOverlayWith100 = () => ( +const SpinnerOverlay100 = () => ( <ExampleContainer> <Title title="Mode overlay" theme="light" level={4} /> <DxcSpinner mode="overlay" value={100}></DxcSpinner> </ExampleContainer> ); -export const SpinnerOverlayWithLabel = () => ( +const SpinnerOverlayLabel = () => ( <ExampleContainer> <Title title="Mode overlay" theme="light" level={4} /> <DxcSpinner mode="overlay" value={50} label="Label"></DxcSpinner> </ExampleContainer> ); -export const SpinnerOverlayWithValue = () => ( +const SpinnerOverlayValue = () => ( <ExampleContainer> <Title title="Mode overlay" theme="light" level={4} /> <DxcSpinner mode="overlay" value={50} showValue></DxcSpinner> </ExampleContainer> ); -export const SpinnerOverlayWithValueAndLabel = () => ( +const SpinnerOverlayValueAndLabel = () => ( <ExampleContainer> <Title title="Mode overlay" theme="light" level={4} /> <DxcSpinner mode="overlay" label="Label" value={50} showValue></DxcSpinner> </ExampleContainer> ); -export const SpinnerOverlayWithValueAndLabelOpinionated = () => ( +const SpinnerOverlayValueAndLabelOpinionated = () => ( <ExampleContainer> <HalstackProvider theme={opinionatedTheme}> <Title title="Mode overlay" theme="light" level={4} /> @@ -126,3 +127,29 @@ export const SpinnerOverlayWithValueAndLabelOpinionated = () => ( </HalstackProvider> </ExampleContainer> ); + +type Story = StoryObj<typeof DxcSpinner>; + +export const Chromatic: Story = { + render: Spinner, +}; + +export const SpinnerWithOverlay: Story = { + render: SpinnerOverlay, +}; +export const SpinnerOverlayWith100: Story = { + render: SpinnerOverlay100, +}; +export const SpinnerOverlayWithLabel: Story = { + render: SpinnerOverlayLabel, +}; + +export const SpinnerOverlayWithValue: Story = { + render: SpinnerOverlayValue, +}; +export const SpinnerOverlayWithValueAndLabel: Story = { + render: SpinnerOverlayValueAndLabel, +}; +export const SpinnerOverlayWithValueAndLabelOpinionated: Story = { + render: SpinnerOverlayValueAndLabelOpinionated, +}; diff --git a/packages/lib/src/status-light/StatusLight.stories.tsx b/packages/lib/src/status-light/StatusLight.stories.tsx index 9eb718afff..1521327fc9 100644 --- a/packages/lib/src/status-light/StatusLight.stories.tsx +++ b/packages/lib/src/status-light/StatusLight.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcStatusLight from "./StatusLight"; @@ -5,9 +6,9 @@ import DxcStatusLight from "./StatusLight"; export default { title: "Status Light", component: DxcStatusLight, -}; +} as Meta<typeof DxcStatusLight>; -export const Chromatic = () => ( +const StatusLight = () => ( <> <ExampleContainer> <Title title="Default light small" theme="light" level={4} /> @@ -71,3 +72,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcStatusLight>; + +export const Chromatic: Story = { + render: StatusLight, +}; diff --git a/packages/lib/src/switch/Switch.stories.tsx b/packages/lib/src/switch/Switch.stories.tsx index 74f0171cbe..097a01c378 100644 --- a/packages/lib/src/switch/Switch.stories.tsx +++ b/packages/lib/src/switch/Switch.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import preview from "../../.storybook/preview"; @@ -18,7 +19,7 @@ export default { }, }, }, -}; +} as Meta<typeof DxcSwitch>; const opinionatedTheme = { switch: { @@ -27,7 +28,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Switch = () => ( <> <ExampleContainer> <Title title="With label after" theme="light" level={4} /> @@ -146,3 +147,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcSwitch>; + +export const Chromatic: Story = { + render: Switch, +}; diff --git a/packages/lib/src/table/Table.stories.tsx b/packages/lib/src/table/Table.stories.tsx index 5fe1d4b0f2..230f6fadfd 100644 --- a/packages/lib/src/table/Table.stories.tsx +++ b/packages/lib/src/table/Table.stories.tsx @@ -6,6 +6,7 @@ import { disabledRules } from "../../test/accessibility/rules/specific/table/dis import { HalstackProvider } from "../HalstackContext"; import DxcTable from "./Table"; import { ActionsPropsType } from "./types"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Table", @@ -20,7 +21,7 @@ export default { }, }, }, -}; +} as Meta<typeof DxcTable>; const opinionatedTheme = { table: { @@ -115,7 +116,7 @@ const actions: ActionsPropsType = [ }, ]; -export const Chromatic = () => ( +const Table = () => ( <> <ExampleContainer> <Title title="Default" theme="light" level={4} /> @@ -655,9 +656,17 @@ const ActionsCellDropdown = () => ( </ExampleContainer> ); -export const DropdownAction = ActionsCellDropdown.bind({}); -DropdownAction.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const nextButton = canvas.getAllByRole("button")[8]; - await userEvent.click(nextButton); +type Story = StoryObj<typeof DxcTable>; + +export const Chromatic: Story = { + render: Table, +}; + +export const DropdownAction: Story = { + render: ActionsCellDropdown, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const nextButton = canvas.getAllByRole("button")[8]; + await userEvent.click(nextButton); + }, }; diff --git a/packages/lib/src/tabs/Tabs.stories.tsx b/packages/lib/src/tabs/Tabs.stories.tsx index ee65767482..c406afd60c 100644 --- a/packages/lib/src/tabs/Tabs.stories.tsx +++ b/packages/lib/src/tabs/Tabs.stories.tsx @@ -4,6 +4,7 @@ import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcTabs from "./Tabs"; import type { Space } from "./types"; +import { Meta, StoryObj } from "@storybook/react/*"; export default { title: "Tabs", @@ -13,7 +14,7 @@ export default { viewports: INITIAL_VIEWPORTS, }, }, -}; +} as Meta<typeof DxcTabs>; const iconSVG = ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" height="20" width="20" fill="currentColor"> @@ -173,7 +174,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Tabs = () => ( <> <ExampleContainer> <Title title="Only label" theme="light" level={4} /> @@ -272,7 +273,7 @@ export const Chromatic = () => ( </> ); -export const ScrollableTabs = () => ( +const Scroll = () => ( <> <ExampleContainer> <Title title="Only label" theme="light" level={4} /> @@ -293,9 +294,18 @@ export const ScrollableTabs = () => ( </> ); -ScrollableTabs.parameters = { - viewport: { - defaultViewport: "iphonex", +type Story = StoryObj<typeof DxcTabs>; + +export const Chromatic: Story = { + render: Tabs, +}; + +export const ScrollableTabs: Story = { + render: Scroll, + parameters: { + viewport: { + defaultViewport: "iphonex", + }, + chromatic: { viewports: [375], delay: 5000 }, }, - chromatic: { viewports: [375], delay: 5000 }, }; diff --git a/packages/lib/src/tabs/TabsLegacy.stories.tsx b/packages/lib/src/tabs/TabsLegacy.stories.tsx index a6b13dea18..11e97a66bc 100644 --- a/packages/lib/src/tabs/TabsLegacy.stories.tsx +++ b/packages/lib/src/tabs/TabsLegacy.stories.tsx @@ -3,6 +3,7 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcTabsLegacy from "./TabsLegacy"; +import { Meta, StoryObj } from "@storybook/react/*"; export default { title: "Tabs Legacy", @@ -12,7 +13,7 @@ export default { viewports: INITIAL_VIEWPORTS, }, }, -}; +} as Meta<typeof DxcTabsLegacy>; const iconSVG = ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" height="20" width="20" fill="currentColor"> @@ -91,7 +92,7 @@ const opinionatedTheme = { }, }; -export const ChromaticLegacy = () => ( +const Chromatic = () => ( <> <ExampleContainer> <Title title="Only label" theme="light" level={4} /> @@ -200,7 +201,7 @@ export const ChromaticLegacy = () => ( </> ); -export const ScrollableTabsLegacy = () => ( +const Scrollable = () => ( <> <ExampleContainer> <Title title="Only label" theme="light" level={4} /> @@ -221,9 +222,18 @@ export const ScrollableTabsLegacy = () => ( </> ); -ScrollableTabsLegacy.parameters = { - viewport: { - defaultViewport: "iphonex", +type Story = StoryObj<typeof DxcTabsLegacy>; + +export const ChromaticLegacy: Story = { + render: Chromatic, +}; + +export const ScrollableTabsLegacy: Story = { + render: Scrollable, + parameters: { + viewport: { + defaultViewport: "iphonex", + }, + chromatic: { viewports: [375], delay: 5000 }, }, - chromatic: { viewports: [375], delay: 5000 }, }; diff --git a/packages/lib/src/tabs/types.ts b/packages/lib/src/tabs/types.ts index 240998fa93..63062c6e19 100644 --- a/packages/lib/src/tabs/types.ts +++ b/packages/lib/src/tabs/types.ts @@ -88,7 +88,7 @@ type LegacyProps = { /** * An array of objects representing the tabs. */ - tabs: (TabLabelProps | TabIconProps)[]; + tabs?: (TabLabelProps | TabIconProps)[]; /** * Whether the icon should appear above or to the left of the label. */ diff --git a/packages/lib/src/tag/Tag.stories.tsx b/packages/lib/src/tag/Tag.stories.tsx index f367b7eda8..8ab9621af0 100644 --- a/packages/lib/src/tag/Tag.stories.tsx +++ b/packages/lib/src/tag/Tag.stories.tsx @@ -3,11 +3,12 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcTag from "./Tag"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Tag", component: DxcTag, -}; +} as Meta<typeof DxcTag>; const icon = ( <svg viewBox="0 0 24 24" fill="currentColor"> @@ -30,7 +31,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Tag = () => ( <> <ExampleContainer> <Title title="With icon" theme="light" level={4} /> @@ -144,8 +145,16 @@ const LinkTag = () => ( </ExampleContainer> ); -export const HoverLinkTag = LinkTag.bind({}); -HoverLinkTag.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - await userEvent.hover(canvas.getByText("Tag")); +type Story = StoryObj<typeof DxcTag>; + +export const Chromatic: Story = { + render: Tag, +}; + +export const HoverLinkTag: Story = { + render: LinkTag, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await userEvent.hover(canvas.getByText("Tag")); + }, }; diff --git a/packages/lib/src/text-input/TextInput.stories.tsx b/packages/lib/src/text-input/TextInput.stories.tsx index 6f177967b7..700d613e65 100644 --- a/packages/lib/src/text-input/TextInput.stories.tsx +++ b/packages/lib/src/text-input/TextInput.stories.tsx @@ -7,11 +7,12 @@ import DxcFlex from "../flex/Flex"; import useTheme from "../utils/useTheme"; import Suggestions from "./Suggestions"; import DxcTextInput from "./TextInput"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Text Input", component: DxcTextInput, -}; +} as Meta<typeof DxcTextInput>; const action = { onClick: () => {}, @@ -42,6 +43,7 @@ const actionLargeIconURL = { }; const country = ["Afghanistan"]; + const countries = [ "Afghanistan", "Albania", @@ -74,7 +76,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const TextInput = () => ( <> <ExampleContainer pseudoState="pseudo-hover"> <Title title="Hovered input" theme="light" level={4} /> @@ -468,9 +470,17 @@ const AutosuggestListbox = () => { ); }; -export const AutosuggestListboxStates = AutosuggestListbox.bind({}); -AutosuggestListboxStates.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const select = canvas.getByRole("combobox"); - await userEvent.click(select); +type Story = StoryObj<typeof DxcTextInput>; + +export const Chromatic: Story = { + render: TextInput, +}; + +export const AutosuggestListboxStates: Story = { + render: AutosuggestListbox, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const select = canvas.getByRole("combobox"); + await userEvent.click(select); + }, }; diff --git a/packages/lib/src/textarea/Textarea.stories.tsx b/packages/lib/src/textarea/Textarea.stories.tsx index bcfa6ad138..21d037dd09 100644 --- a/packages/lib/src/textarea/Textarea.stories.tsx +++ b/packages/lib/src/textarea/Textarea.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; @@ -6,7 +7,7 @@ import DxcTextarea from "./Textarea"; export default { title: "Textarea", component: DxcTextarea, -}; +} as Meta<typeof DxcTextarea>; const opinionatedTheme = { textarea: { @@ -15,7 +16,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const TextArea = () => ( <> <ExampleContainer pseudoState="pseudo-hover"> <Title title="Hovered" theme="light" level={4} /> @@ -171,3 +172,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcTextarea>; + +export const Chromatic: Story = { + render: TextArea, +}; diff --git a/packages/lib/src/toast/Toast.stories.tsx b/packages/lib/src/toast/Toast.stories.tsx index fa09ed20c5..70835f4898 100644 --- a/packages/lib/src/toast/Toast.stories.tsx +++ b/packages/lib/src/toast/Toast.stories.tsx @@ -7,11 +7,12 @@ 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"; export default { title: "Toast", component: DxcToast, -}; +} as Meta<typeof DxcToast>; const action = { label: "Action", @@ -28,7 +29,7 @@ const actionIcon = { }; const onClear = () => {}; -export const Chromatic = () => ( +const Toast = () => ( <> <Title title="Default" level={2} /> <ExampleContainer> @@ -249,6 +250,7 @@ const Screens = () => { </ExampleContainer> ); }; + const ToastsQueue = () => ( <DxcToastsQueue> <Screens /> @@ -262,14 +264,24 @@ const playFunc = async ({ canvasElement }) => { await userEvent.click(canvas.getByText("Show success toast")); }; -export const FullScreenToast = ToastsQueue.bind({}); -FullScreenToast.play = playFunc; +type Story = StoryObj<typeof DxcToast>; + +export const Chromatic: Story = { + render: Toast, +}; + +export const FullScreenToast: Story = { + render: ToastsQueue, + play: playFunc, +}; -export const MobileScreenToast = ToastsQueue.bind({}); -MobileScreenToast.parameters = { - viewport: { - viewports: INITIAL_VIEWPORTS, - defaultViewport: "iphonex", +export const MobileScreenToast: Story = { + render: ToastsQueue, + play: playFunc, + parameters: { + viewport: { + viewports: INITIAL_VIEWPORTS, + defaultViewport: "iphonex", + }, }, }; -MobileScreenToast.play = playFunc; diff --git a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx index 969fb346fd..4099966f40 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.stories.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.stories.tsx @@ -3,11 +3,12 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcToggleGroup from "./ToggleGroup"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Toggle Group", component: DxcToggleGroup, -}; +} as Meta<typeof DxcToggleGroup>; const ethernetSVG = ( <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="currentColor"> @@ -102,7 +103,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const ToggleGroup = () => ( <> <ExampleContainer> <Title title="Basic toggle group" theme="light" level={4} /> @@ -200,18 +201,30 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + const OptionSelected = () => <DxcToggleGroup label="Toggle group" helperText="HelperText" options={options} />; -export const ToggleGroupSelectedActived = OptionSelected.bind({}); -ToggleGroupSelectedActived.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const option = canvas.getByText("Linkedin"); - await userEvent.click(option); +type Story = StoryObj<typeof DxcToggleGroup>; + +export const Chromatic: Story = { + render: ToggleGroup, +}; + +export const ToggleGroupSelectedActived: Story = { + render: OptionSelected, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const option = canvas.getByText("Linkedin"); + await userEvent.click(option); + }, }; -export const ToggleGroupUnselectedActived = OptionSelected.bind({}); -ToggleGroupUnselectedActived.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const option = canvas.getByText("X"); - await userEvent.click(option); - userEvent.tab(); + +export const ToggleGroupUnselectedActived: Story = { + render: OptionSelected, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const option = canvas.getByText("X"); + await userEvent.click(option); + userEvent.tab(); + }, }; diff --git a/packages/lib/src/tooltip/Tooltip.stories.tsx b/packages/lib/src/tooltip/Tooltip.stories.tsx index 55d8ba7c9c..98fc64922e 100644 --- a/packages/lib/src/tooltip/Tooltip.stories.tsx +++ b/packages/lib/src/tooltip/Tooltip.stories.tsx @@ -5,11 +5,12 @@ 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", component: DxcTooltip, -}; +} as Meta<typeof DxcTooltip>; const Tooltip = () => ( <> @@ -74,37 +75,49 @@ const RightTooltip = () => ( </> ); -export const Chromatic = Tooltip.bind({}); -Chromatic.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +type Story = StoryObj<typeof DxcTooltip>; + +export const Chromatic: Story = { + render: Tooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; -export const LargeTextTooltip = LargeTextWithinTooltip.bind({}); -LargeTextTooltip.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +export const LargeTextTooltip: Story = { + render: LargeTextWithinTooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; -export const TooltipPositionTop = TopTooltip.bind({}); -TooltipPositionTop.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +export const TooltipPositionTop: Story = { + render: TopTooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; -export const TooltipPositionLeft = LeftTooltip.bind({}); -TooltipPositionLeft.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +export const TooltipPositionLeft: Story = { + render: LeftTooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; -export const TooltipPositionRight = RightTooltip.bind({}); -TooltipPositionRight.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button"); - await userEvent.hover(button); +export const TooltipPositionRight: Story = { + render: RightTooltip, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const button = canvas.getByRole("button"); + await userEvent.hover(button); + }, }; diff --git a/packages/lib/src/typography/Typography.stories.tsx b/packages/lib/src/typography/Typography.stories.tsx index 03430b2282..e14c88cd92 100644 --- a/packages/lib/src/typography/Typography.stories.tsx +++ b/packages/lib/src/typography/Typography.stories.tsx @@ -1,3 +1,4 @@ +import { Meta, StoryObj } from "@storybook/react"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import DxcTypography from "./Typography"; @@ -5,9 +6,9 @@ import DxcTypography from "./Typography"; export default { title: "Typography", component: DxcTypography, -}; +} as Meta<typeof DxcTypography>; -export const Chromatic = () => ( +const Typography = () => ( <> <ExampleContainer> <Title title="Default Typography" theme="light" level={4} /> @@ -193,3 +194,9 @@ export const Chromatic = () => ( </ExampleContainer> </> ); + +type Story = StoryObj<typeof DxcTypography>; + +export const Chromatic: Story = { + render: Typography, +}; diff --git a/packages/lib/src/wizard/Wizard.stories.tsx b/packages/lib/src/wizard/Wizard.stories.tsx index 2b47150727..fcf3ca7902 100644 --- a/packages/lib/src/wizard/Wizard.stories.tsx +++ b/packages/lib/src/wizard/Wizard.stories.tsx @@ -3,11 +3,12 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcWizard from "./Wizard"; +import { Meta, StoryObj } from "@storybook/react"; export default { title: "Wizard", component: DxcWizard, -}; +} as Meta<typeof DxcWizard>; const favoriteSVG = ( <svg viewBox="0 0 24 24" fill="currentColor"> @@ -155,7 +156,7 @@ const opinionatedTheme = { }, }; -export const Chromatic = () => ( +const Wizard = () => ( <> <ExampleContainer> <Title title="Current step in the third step, labels and description" theme="light" level={4} /> @@ -263,9 +264,17 @@ const WizardSelected = () => ( </ExampleContainer> ); -export const WizardStepActived = WizardSelected.bind({}); -WizardStepActived.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - const option = canvas.getByText("Third step"); - await userEvent.click(option); +type Story = StoryObj<typeof DxcWizard>; + +export const Chromatic: Story = { + render: Wizard, +}; + +export const WizardStepActived: Story = { + render: WizardSelected, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + const option = canvas.getByText("Third step"); + await userEvent.click(option); + }, }; From 3818b589f0553049592ab6db53d67c20f1051ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:34:11 +0100 Subject: [PATCH 23/41] Removing undesired optional chaining operators --- packages/lib/src/container/Container.tsx | 19 ++++++------------- .../contextual-menu/ContextualMenu.test.tsx | 8 ++++---- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/packages/lib/src/container/Container.tsx b/packages/lib/src/container/Container.tsx index 72d200bbf9..86ba16425a 100644 --- a/packages/lib/src/container/Container.tsx +++ b/packages/lib/src/container/Container.tsx @@ -4,8 +4,8 @@ import ContainerPropsType, { BorderProperties, StyledProps } from "./types"; import { spaces } from "../common/variables"; const getBorderStyles = (direction: "top" | "bottom" | "left" | "right", borderProperties: BorderProperties) => - `border-${direction}: ${borderProperties?.width ?? ""} ${borderProperties?.style ?? ""} ${ - borderProperties?.color ? getCoreColorToken(borderProperties?.color) : "" + `border-${direction}: ${borderProperties.width ?? ""} ${borderProperties.style ?? ""} ${ + borderProperties.color ? getCoreColorToken(borderProperties.color) : "" };`; const Container = styled.div<StyledProps>` @@ -25,7 +25,6 @@ const Container = styled.div<StyledProps>` float: ${({ float }) => float}; z-index: ${({ zIndex }) => zIndex}; box-shadow: ${({ boxShadow }) => boxShadow}; - background-attachment: ${({ background }) => background?.attachment}; background-clip: ${({ background }) => background?.clip}; background-color: ${({ background }) => (background?.color ? getCoreColorToken(background?.color) : "")}; @@ -34,44 +33,38 @@ const Container = styled.div<StyledProps>` background-position: ${({ background }) => background?.position}; background-repeat: ${({ background }) => background?.repeat}; background-size: ${({ background }) => background?.size}; - border-radius: ${({ borderRadius }) => borderRadius}; border-width: ${({ border }) => (border && "width" in border ? `${border?.width}` : "")}; border-style: ${({ border }) => (border && "style" in border ? `${border?.style}` : "")}; border-color: ${({ border }) => border && "color" in border && border?.color ? `${getCoreColorToken(border?.color)}` : ""}; - ${({ border }) => { let styles = ""; if (border != null) { switch (true) { case "top" in border: - styles += border?.top ? getBorderStyles("top", border.top) : ""; + styles += border.top ? getBorderStyles("top", border.top) : ""; case "right" in border: - styles += border?.right ? getBorderStyles("right", border.right) : ""; + styles += border.right ? getBorderStyles("right", border.right) : ""; case "left" in border: - styles += border?.left ? getBorderStyles("left", border.left) : ""; + styles += border.left ? getBorderStyles("left", border.left) : ""; case "bottom" in border: - styles += border?.bottom ? getBorderStyles("bottom", border.bottom) : ""; + styles += border.bottom ? getBorderStyles("bottom", border.bottom) : ""; } } return styles; }}; - margin: ${({ margin }) => (typeof margin === "string" ? spaces[margin] : "")}; margin-top: ${({ margin }) => (typeof margin === "object" && margin.top ? spaces[margin.top] : "")}; margin-right: ${({ margin }) => (typeof margin === "object" && margin.right ? spaces[margin.right] : "")}; margin-bottom: ${({ margin }) => (typeof margin === "object" && margin.bottom ? spaces[margin.bottom] : "")}; margin-left: ${({ margin }) => (typeof margin === "object" && margin.left ? spaces[margin.left] : "")}; - outline: ${({ outline }) => `${outline?.width ?? ""} ${outline?.style ?? ""} ${outline?.color ? getCoreColorToken(outline?.color) : ""}`}; outline-offset: ${({ outline }) => outline?.offset}; - overflow: ${({ $overflow }) => (typeof $overflow === "string" ? $overflow : "")}; overflow-x: ${({ $overflow }) => (typeof $overflow === "object" ? `${$overflow?.x}` : "")}; overflow-y: ${({ $overflow }) => (typeof $overflow === "object" ? `${$overflow?.y}` : "")}; - padding: ${({ padding }) => (typeof padding === "string" ? spaces[padding] : "")}; padding-top: ${({ padding }) => (typeof padding === "object" && padding.top ? spaces[padding.top] : "")}; padding-right: ${({ padding }) => (typeof padding === "object" && padding.right ? spaces[padding.right] : "")}; diff --git a/packages/lib/src/contextual-menu/ContextualMenu.test.tsx b/packages/lib/src/contextual-menu/ContextualMenu.test.tsx index 62204900a3..9b271f3a15 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.test.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.test.tsx @@ -69,10 +69,10 @@ describe("Contextual menu component tests", () => { group1 != null && userEvent.click(group1); expect(group1?.getAttribute("aria-expanded")).toBeTruthy(); expect(group1?.getAttribute("aria-controls")).toBe(group1?.nextElementSibling?.id); - const groupedItem1 = getAllByRole("button")[2]; - const groupedItem2 = getAllByRole("button")[6]; - groupedItem1 != null && userEvent.click(groupedItem1); - groupedItem2 != null && userEvent.click(groupedItem2); + const expandedGroupItem1 = getAllByRole("button")[2]; + expandedGroupItem1 != null && userEvent.click(expandedGroupItem1); + const expandedGroupedItem2 = getAllByRole("button")[6]; + expandedGroupedItem2 != null && userEvent.click(expandedGroupedItem2); expect(getAllByRole("menuitem").length).toBe(10); const optionToBeClicked = getAllByRole("button")[4]; optionToBeClicked != null && userEvent.click(optionToBeClicked); From 40d194d15d363f492882d1b7b5a8cde02493f0b3 Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Wed, 18 Dec 2024 14:57:32 +0100 Subject: [PATCH 24/41] Removed 'React.' and importing functions directly instead --- apps/website/screens/common/Figure.tsx | 5 +++-- apps/website/screens/common/PageHeading.tsx | 3 ++- .../screens/common/QuickNavContainerLayout.tsx | 4 +++- apps/website/screens/common/Section.tsx | 3 ++- apps/website/screens/common/TableCode.tsx | 3 ++- .../components/accordion/AccordionPageLayout.tsx | 3 ++- .../screens/components/alert/AlertPageLayout.tsx | 3 ++- .../ApplicationLayoutPageLayout.tsx | 3 ++- .../screens/components/badge/BadgePageLayout.tsx | 3 ++- .../screens/components/bleed/BleedPageLayout.tsx | 3 ++- .../breadcrumbs/BreadcrumbsPageLayout.tsx | 3 ++- .../bulleted-list/BulletedListPageLayout.tsx | 3 ++- .../components/button/ButtonPageLayout.tsx | 3 ++- .../screens/components/card/CardPageLayout.tsx | 3 ++- .../components/checkbox/CheckboxPageLayout.tsx | 3 ++- .../screens/components/chip/ChipPageLayout.tsx | 3 ++- .../components/container/ContainerPageLayout.tsx | 3 ++- .../contextual-menu/ContextualMenuPageLayout.tsx | 3 ++- .../components/data-grid/DataGridPageLayout.tsx | 3 ++- .../date-input/DateInputPageLayout.tsx | 3 ++- .../components/dialog/DialogPageLayout.tsx | 3 ++- .../components/divider/DividerPageLayout.tsx | 3 ++- .../components/dropdown/DropdownPageLayout.tsx | 3 ++- .../file-input/FileInputPageLayout.tsx | 3 ++- .../screens/components/flex/FlexPageLayout.tsx | 3 ++- .../components/footer/FooterPageLayout.tsx | 3 ++- .../screens/components/grid/GridPageLayout.tsx | 3 ++- .../components/header/HeaderPageLayout.tsx | 3 ++- .../components/heading/HeadingPageLayout.tsx | 3 ++- .../screens/components/image/ImagePageLayout.tsx | 3 ++- .../screens/components/inset/InsetPageLayout.tsx | 3 ++- .../screens/components/link/LinkPageLayout.tsx | 3 ++- .../components/link/code/examples/routerLink.tsx | 3 ++- .../components/nav-tabs/NavTabsPageLayout.tsx | 3 ++- .../number-input/NumberInputPageLayout.tsx | 3 ++- .../components/paginator/PaginatorPageLayout.tsx | 3 ++- .../components/paragraph/ParagraphPageLayout.tsx | 3 ++- .../password-input/PasswordInputPageLayout.tsx | 3 ++- .../progress-bar/ProgressBarPageLayout.tsx | 3 ++- .../components/quick-nav/QuickNavPageLayout.tsx | 3 ++- .../radio-group/RadioGroupPageLayout.tsx | 3 ++- .../resultset-table/ResultsetTablePageLayout.tsx | 3 ++- .../components/select/SelectPageLayout.tsx | 3 ++- .../components/sidenav/SidenavPageLayout.tsx | 3 ++- .../components/slider/SliderPageLayout.tsx | 3 ++- .../components/spinner/SpinnerPageLayout.tsx | 3 ++- .../status-light/StatusLightPageLayout.tsx | 3 ++- .../components/switch/SwitchPageLayout.tsx | 3 ++- .../screens/components/table/TablePageLayout.tsx | 3 ++- .../screens/components/tabs/TabsPageLayout.tsx | 3 ++- .../screens/components/tag/TagPageLayout.tsx | 3 ++- .../text-input/TextInputPageLayout.tsx | 3 ++- .../components/textarea/TextareaPageLayout.tsx | 3 ++- .../screens/components/toast/ToastPageLayout.tsx | 3 ++- .../toggle-group/ToggleGroupPageLayout.tsx | 3 ++- .../components/tooltip/TooltipPageLayout.tsx | 3 ++- .../typography/TypographyPageLayout.tsx | 3 ++- .../components/wizard/WizardPageLayout.tsx | 3 ++- .../screens/overview/releases/ReleasesPage.tsx | 3 ++- .../typography/TypographyPageLayout.tsx | 3 ++- .../screens/theme-generator/components/Mode.tsx | 3 ++- .../components/previews/DataGrid.tsx | 6 +++--- .../components/previews/Header.tsx | 3 ++- .../components/previews/PreviewContainer.tsx | 3 ++- .../components/widgets/ImageConfig.tsx | 6 +++--- apps/website/screens/theme-generator/types.ts | 4 +++- .../.storybook/components/ExampleContainer.tsx | 3 ++- packages/lib/src/BackgroundColorContext.tsx | 4 ++-- packages/lib/src/accordion/types.ts | 3 ++- packages/lib/src/alert/types.ts | 5 +++-- packages/lib/src/bleed/types.ts | 3 ++- packages/lib/src/bulleted-list/types.ts | 7 ++++--- packages/lib/src/card/types.ts | 3 ++- packages/lib/src/common/utils.ts | 3 ++- packages/lib/src/container/types.ts | 3 ++- .../src/contextual-menu/ContextualMenu.test.tsx | 2 +- packages/lib/src/contextual-menu/ItemAction.tsx | 4 ++-- packages/lib/src/contextual-menu/types.ts | 13 +++++++------ packages/lib/src/dialog/types.ts | 4 +++- packages/lib/src/dropdown/types.ts | 5 +++-- packages/lib/src/flex/types.ts | 3 ++- packages/lib/src/footer/types.ts | 3 ++- packages/lib/src/grid/types.ts | 3 ++- packages/lib/src/header/types.ts | 5 +++-- packages/lib/src/image/types.ts | 10 ++++++---- packages/lib/src/inset/types.ts | 3 ++- packages/lib/src/layout/types.ts | 16 +++++++++------- packages/lib/src/layout/utils.tsx | 4 ++-- packages/lib/src/link/Link.tsx | 4 ++-- packages/lib/src/resultset-table/types.ts | 5 +++-- packages/lib/src/select/ListOption.tsx | 4 ++-- packages/lib/src/select/types.ts | 3 ++- packages/lib/src/sidenav/Sidenav.tsx | 6 +++--- packages/lib/src/sidenav/types.ts | 15 ++++++++------- packages/lib/src/table/types.ts | 3 ++- packages/lib/src/tabs/Tab.tsx | 6 +++--- packages/lib/src/tabs/TabLegacy.tsx | 2 +- packages/lib/src/tabs/Tabs.tsx | 3 ++- packages/lib/src/tabs/TabsLegacy.tsx | 6 +++--- packages/lib/src/tabs/types.ts | 6 +++--- packages/lib/src/tag/Tag.tsx | 8 ++++---- packages/lib/src/text-input/types.ts | 7 ++++--- packages/lib/src/toast/types.ts | 3 ++- packages/lib/src/tooltip/types.tsx | 2 +- packages/lib/src/typography/Typography.tsx | 4 ++-- packages/lib/src/typography/types.ts | 4 +++- packages/lib/src/utils/FocusLock.tsx | 8 ++++---- 107 files changed, 256 insertions(+), 160 deletions(-) diff --git a/apps/website/screens/common/Figure.tsx b/apps/website/screens/common/Figure.tsx index 05912ce016..f81e71878c 100644 --- a/apps/website/screens/common/Figure.tsx +++ b/apps/website/screens/common/Figure.tsx @@ -1,9 +1,10 @@ import styled from "styled-components"; import { DxcFlex } from "@dxc-technology/halstack-react"; +import { ReactNode } from "react"; type DocImageProps = { - children: React.ReactNode; - caption: string | React.ReactNode; + children: ReactNode; + caption: string | ReactNode; }; const Figure = ({ caption, children }: DocImageProps) => { return ( diff --git a/apps/website/screens/common/PageHeading.tsx b/apps/website/screens/common/PageHeading.tsx index aa3551fa4a..5303b64e6e 100644 --- a/apps/website/screens/common/PageHeading.tsx +++ b/apps/website/screens/common/PageHeading.tsx @@ -1,6 +1,7 @@ +import { ReactNode } from "react"; import styled from "styled-components"; -const PageHeading = ({ children }: { children: React.ReactNode }) => { +const PageHeading = ({ children }: { children: ReactNode }) => { return <PageHeadingContainer>{children}</PageHeadingContainer>; }; diff --git a/apps/website/screens/common/QuickNavContainerLayout.tsx b/apps/website/screens/common/QuickNavContainerLayout.tsx index 2df70cad35..c07dd5626a 100644 --- a/apps/website/screens/common/QuickNavContainerLayout.tsx +++ b/apps/website/screens/common/QuickNavContainerLayout.tsx @@ -1,5 +1,7 @@ +import { ReactNode } from "react"; + type QuickNavContainerLayoutProps = { - children: React.ReactNode; + children: ReactNode; }; const QuickNavContainerLayout = ({ children }: QuickNavContainerLayoutProps) => <>{children}</>; diff --git a/apps/website/screens/common/Section.tsx b/apps/website/screens/common/Section.tsx index d5662fc896..8d7a033233 100644 --- a/apps/website/screens/common/Section.tsx +++ b/apps/website/screens/common/Section.tsx @@ -1,5 +1,6 @@ import { DxcFlex } from "@dxc-technology/halstack-react"; import HeadingLink from "./HeadingLink"; +import { ReactNode } from "react"; type LevelEnum = 1 | 2 | 3 | 4 | 5; @@ -7,7 +8,7 @@ export type SectionType = { title: string; level?: LevelEnum; subSections?: SectionType[]; - content?: React.ReactNode; + content?: ReactNode; navSubtitle?: string; }; diff --git a/apps/website/screens/common/TableCode.tsx b/apps/website/screens/common/TableCode.tsx index 028791457b..95783c2439 100644 --- a/apps/website/screens/common/TableCode.tsx +++ b/apps/website/screens/common/TableCode.tsx @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import styled from "styled-components"; const TableCode = styled.code` @@ -8,7 +9,7 @@ const TableCode = styled.code` border-radius: 0.25rem; `; -export const ExtendedTableCode = ({ children }: { children: React.ReactNode }) => ( +export const ExtendedTableCode = ({ children }: { children: ReactNode }) => ( <ExtendedCodeContainer> <StyledExtendedCode>{children}</StyledExtendedCode> </ExtendedCodeContainer> diff --git a/apps/website/screens/components/accordion/AccordionPageLayout.tsx b/apps/website/screens/components/accordion/AccordionPageLayout.tsx index 8db40c0e5c..1bfd1ce897 100644 --- a/apps/website/screens/components/accordion/AccordionPageLayout.tsx +++ b/apps/website/screens/components/accordion/AccordionPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import TabsPageHeading from "@/common/TabsPageLayout"; import PageHeading from "@/common/PageHeading"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const AccordionPageHeading = ({ children }: { children: React.ReactNode }) => { +const AccordionPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/accordion" }, { label: "Usage", path: "/components/accordion/usage" }, diff --git a/apps/website/screens/components/alert/AlertPageLayout.tsx b/apps/website/screens/components/alert/AlertPageLayout.tsx index 76b524fff5..bdaf827c25 100644 --- a/apps/website/screens/components/alert/AlertPageLayout.tsx +++ b/apps/website/screens/components/alert/AlertPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const AlertPageHeading = ({ children }: { children: React.ReactNode }) => { +const AlertPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/alert" }, { label: "Usage", path: "/components/alert/usage" }, diff --git a/apps/website/screens/components/application-layout/ApplicationLayoutPageLayout.tsx b/apps/website/screens/components/application-layout/ApplicationLayoutPageLayout.tsx index 6dce61edfb..c420de33c3 100644 --- a/apps/website/screens/components/application-layout/ApplicationLayoutPageLayout.tsx +++ b/apps/website/screens/components/application-layout/ApplicationLayoutPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ApplicationLayoutPageHeading = ({ children }: { children: React.ReactNode }) => { +const ApplicationLayoutPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/application-layout" }, { label: "Usage", path: "/components/application-layout/usage" }, diff --git a/apps/website/screens/components/badge/BadgePageLayout.tsx b/apps/website/screens/components/badge/BadgePageLayout.tsx index 2f83956a60..b303876caf 100644 --- a/apps/website/screens/components/badge/BadgePageLayout.tsx +++ b/apps/website/screens/components/badge/BadgePageLayout.tsx @@ -2,8 +2,9 @@ import { DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const BadgePageHeading = ({ children }: { children: React.ReactNode }) => { +const BadgePageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/badge" }, { label: "Usage", path: "/components/badge/usage" }, diff --git a/apps/website/screens/components/bleed/BleedPageLayout.tsx b/apps/website/screens/components/bleed/BleedPageLayout.tsx index 45bea66ddd..60dab729e4 100644 --- a/apps/website/screens/components/bleed/BleedPageLayout.tsx +++ b/apps/website/screens/components/bleed/BleedPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const BleedPageHeading = ({ children }: { children: React.ReactNode }) => { +const BleedPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/bleed" }, { label: "Usage", path: "/components/bleed/usage" }, diff --git a/apps/website/screens/components/breadcrumbs/BreadcrumbsPageLayout.tsx b/apps/website/screens/components/breadcrumbs/BreadcrumbsPageLayout.tsx index 4e254af83a..a96d5c06b4 100644 --- a/apps/website/screens/components/breadcrumbs/BreadcrumbsPageLayout.tsx +++ b/apps/website/screens/components/breadcrumbs/BreadcrumbsPageLayout.tsx @@ -2,8 +2,9 @@ import PageHeading from "@/common/PageHeading"; import { DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const BreadcrumbsPageHeading = ({ children }: { children: React.ReactNode }) => { +const BreadcrumbsPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/breadcrumbs" }, { label: "Usage", path: "/components/breadcrumbs/usage" }, diff --git a/apps/website/screens/components/bulleted-list/BulletedListPageLayout.tsx b/apps/website/screens/components/bulleted-list/BulletedListPageLayout.tsx index e401749693..bf5a3a8704 100644 --- a/apps/website/screens/components/bulleted-list/BulletedListPageLayout.tsx +++ b/apps/website/screens/components/bulleted-list/BulletedListPageLayout.tsx @@ -2,8 +2,9 @@ import PageHeading from "@/common/PageHeading"; import { DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const BulletedListPageHeading = ({ children }: { children: React.ReactNode }) => { +const BulletedListPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/bulleted-list" }, { label: "Usage", path: "/components/bulleted-list/usage" }, diff --git a/apps/website/screens/components/button/ButtonPageLayout.tsx b/apps/website/screens/components/button/ButtonPageLayout.tsx index 645a02915f..2c0a7a3f15 100644 --- a/apps/website/screens/components/button/ButtonPageLayout.tsx +++ b/apps/website/screens/components/button/ButtonPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ButtonPageHeading = ({ children }: { children: React.ReactNode }) => { +const ButtonPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/button" }, { label: "Usage", path: "/components/button/usage" }, diff --git a/apps/website/screens/components/card/CardPageLayout.tsx b/apps/website/screens/components/card/CardPageLayout.tsx index aa085af6e3..41ec875fe5 100644 --- a/apps/website/screens/components/card/CardPageLayout.tsx +++ b/apps/website/screens/components/card/CardPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const CardPageHeading = ({ children }: { children: React.ReactNode }) => { +const CardPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/card" }, { label: "Usage", path: "/components/card/usage" }, diff --git a/apps/website/screens/components/checkbox/CheckboxPageLayout.tsx b/apps/website/screens/components/checkbox/CheckboxPageLayout.tsx index 0fbc1dcbac..7044bc1192 100644 --- a/apps/website/screens/components/checkbox/CheckboxPageLayout.tsx +++ b/apps/website/screens/components/checkbox/CheckboxPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const CheckboxPageHeading = ({ children }: { children: React.ReactNode }) => { +const CheckboxPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/checkbox" }, { label: "Usage", path: "/components/checkbox/usage" }, diff --git a/apps/website/screens/components/chip/ChipPageLayout.tsx b/apps/website/screens/components/chip/ChipPageLayout.tsx index 5dba227a0f..cf36827872 100644 --- a/apps/website/screens/components/chip/ChipPageLayout.tsx +++ b/apps/website/screens/components/chip/ChipPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ChipPageHeading = ({ children }: { children: React.ReactNode }) => { +const ChipPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/chip" }, { label: "Usage", path: "/components/chip/usage" }, diff --git a/apps/website/screens/components/container/ContainerPageLayout.tsx b/apps/website/screens/components/container/ContainerPageLayout.tsx index bfcda107dd..71e015cc1b 100644 --- a/apps/website/screens/components/container/ContainerPageLayout.tsx +++ b/apps/website/screens/components/container/ContainerPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ContainerPageHeading = ({ children }: { children: React.ReactNode }) => { +const ContainerPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/container" }, { label: "Usage", path: "/components/container/usage" }, diff --git a/apps/website/screens/components/contextual-menu/ContextualMenuPageLayout.tsx b/apps/website/screens/components/contextual-menu/ContextualMenuPageLayout.tsx index c2b71f0d85..000323545b 100644 --- a/apps/website/screens/components/contextual-menu/ContextualMenuPageLayout.tsx +++ b/apps/website/screens/components/contextual-menu/ContextualMenuPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ContextualMenuPageHeading = ({ children }: { children: React.ReactNode }) => { +const ContextualMenuPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/contextual-menu" }, { label: "Usage", path: "/components/contextual-menu/usage" }, diff --git a/apps/website/screens/components/data-grid/DataGridPageLayout.tsx b/apps/website/screens/components/data-grid/DataGridPageLayout.tsx index 21b9d9b76f..119c920258 100644 --- a/apps/website/screens/components/data-grid/DataGridPageLayout.tsx +++ b/apps/website/screens/components/data-grid/DataGridPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex, DxcAlert } from "@dxc-technology/halstack-react" import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const DataGridPageHeading = ({ children }: { children: React.ReactNode }) => { +const DataGridPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/data-grid" }, { label: "Usage", path: "/components/data-grid/usage" }, diff --git a/apps/website/screens/components/date-input/DateInputPageLayout.tsx b/apps/website/screens/components/date-input/DateInputPageLayout.tsx index efd176781a..2e72577cdf 100644 --- a/apps/website/screens/components/date-input/DateInputPageLayout.tsx +++ b/apps/website/screens/components/date-input/DateInputPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const DateInputPageHeading = ({ children }: { children: React.ReactNode }) => { +const DateInputPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/date-input" }, { label: "Usage", path: "/components/date-input/usage" }, diff --git a/apps/website/screens/components/dialog/DialogPageLayout.tsx b/apps/website/screens/components/dialog/DialogPageLayout.tsx index 3f87f87aea..ef53a736fb 100644 --- a/apps/website/screens/components/dialog/DialogPageLayout.tsx +++ b/apps/website/screens/components/dialog/DialogPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const DialogPageHeading = ({ children }: { children: React.ReactNode }) => { +const DialogPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/dialog" }, { label: "Usage", path: "/components/dialog/usage" }, diff --git a/apps/website/screens/components/divider/DividerPageLayout.tsx b/apps/website/screens/components/divider/DividerPageLayout.tsx index 61981581c9..a51ed8d4ec 100644 --- a/apps/website/screens/components/divider/DividerPageLayout.tsx +++ b/apps/website/screens/components/divider/DividerPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const DividerPageHeading = ({ children }: { children: React.ReactNode }) => { +const DividerPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/divider" }, { label: "Usage", path: "/components/divider/usage" }, diff --git a/apps/website/screens/components/dropdown/DropdownPageLayout.tsx b/apps/website/screens/components/dropdown/DropdownPageLayout.tsx index 03d8e650dd..2c62cf8ee1 100644 --- a/apps/website/screens/components/dropdown/DropdownPageLayout.tsx +++ b/apps/website/screens/components/dropdown/DropdownPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const DropdownPageHeading = ({ children }: { children: React.ReactNode }) => { +const DropdownPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/dropdown" }, { label: "Usage", path: "/components/dropdown/usage" }, diff --git a/apps/website/screens/components/file-input/FileInputPageLayout.tsx b/apps/website/screens/components/file-input/FileInputPageLayout.tsx index d96b9e437e..01012df243 100644 --- a/apps/website/screens/components/file-input/FileInputPageLayout.tsx +++ b/apps/website/screens/components/file-input/FileInputPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const FileInputPageHeading = ({ children }: { children: React.ReactNode }) => { +const FileInputPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/file-input" }, { label: "Usage", path: "/components/file-input/usage" }, diff --git a/apps/website/screens/components/flex/FlexPageLayout.tsx b/apps/website/screens/components/flex/FlexPageLayout.tsx index 48800951e6..2e5741901a 100644 --- a/apps/website/screens/components/flex/FlexPageLayout.tsx +++ b/apps/website/screens/components/flex/FlexPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex, DxcLink } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const FlexPageHeading = ({ children }: { children: React.ReactNode }) => { +const FlexPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/flex" }, { label: "Usage", path: "/components/flex/usage" }, diff --git a/apps/website/screens/components/footer/FooterPageLayout.tsx b/apps/website/screens/components/footer/FooterPageLayout.tsx index 9248d06578..dc32eaa4ad 100644 --- a/apps/website/screens/components/footer/FooterPageLayout.tsx +++ b/apps/website/screens/components/footer/FooterPageLayout.tsx @@ -3,8 +3,9 @@ import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; import Link from "next/link"; +import { ReactNode } from "react"; -const FooterPageHeading = ({ children }: { children: React.ReactNode }) => { +const FooterPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/footer" }, { label: "Usage", path: "/components/footer/usage" }, diff --git a/apps/website/screens/components/grid/GridPageLayout.tsx b/apps/website/screens/components/grid/GridPageLayout.tsx index 177daf6199..5e9f243492 100644 --- a/apps/website/screens/components/grid/GridPageLayout.tsx +++ b/apps/website/screens/components/grid/GridPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex, DxcLink } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const GridPageHeading = ({ children }: { children: React.ReactNode }) => { +const GridPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/grid" }, { label: "Usage", path: "/components/grid/usage" }, diff --git a/apps/website/screens/components/header/HeaderPageLayout.tsx b/apps/website/screens/components/header/HeaderPageLayout.tsx index 113099a9f2..e4e463d0df 100644 --- a/apps/website/screens/components/header/HeaderPageLayout.tsx +++ b/apps/website/screens/components/header/HeaderPageLayout.tsx @@ -3,8 +3,9 @@ import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; import Link from "next/link"; +import { ReactNode } from "react"; -const HeaderPageHeading = ({ children }: { children: React.ReactNode }) => { +const HeaderPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/header" }, { label: "Usage", path: "/components/header/usage" }, diff --git a/apps/website/screens/components/heading/HeadingPageLayout.tsx b/apps/website/screens/components/heading/HeadingPageLayout.tsx index d0e9a190f8..1487acdea0 100644 --- a/apps/website/screens/components/heading/HeadingPageLayout.tsx +++ b/apps/website/screens/components/heading/HeadingPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const HeadingPageHeading = ({ children }: { children: React.ReactNode }) => { +const HeadingPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/heading" }, { label: "Usage", path: "/components/heading/usage" }, diff --git a/apps/website/screens/components/image/ImagePageLayout.tsx b/apps/website/screens/components/image/ImagePageLayout.tsx index 5bbfd43228..8935daceba 100644 --- a/apps/website/screens/components/image/ImagePageLayout.tsx +++ b/apps/website/screens/components/image/ImagePageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ImagePageHeading = ({ children }: { children: React.ReactNode }) => { +const ImagePageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/image" }, { label: "Usage", path: "/components/image/usage" }, diff --git a/apps/website/screens/components/inset/InsetPageLayout.tsx b/apps/website/screens/components/inset/InsetPageLayout.tsx index 2d145c5c85..4e97fae4e0 100644 --- a/apps/website/screens/components/inset/InsetPageLayout.tsx +++ b/apps/website/screens/components/inset/InsetPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const InsetPageHeading = ({ children }: { children: React.ReactNode }) => { +const InsetPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/inset" }, { label: "Usage", path: "/components/inset/usage" }, diff --git a/apps/website/screens/components/link/LinkPageLayout.tsx b/apps/website/screens/components/link/LinkPageLayout.tsx index 42f89e1764..3a2c17a655 100644 --- a/apps/website/screens/components/link/LinkPageLayout.tsx +++ b/apps/website/screens/components/link/LinkPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const LinkPageHeading = ({ children }: { children: React.ReactNode }) => { +const LinkPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/link" }, { label: "Usage", path: "/components/link/usage" }, diff --git a/apps/website/screens/components/link/code/examples/routerLink.tsx b/apps/website/screens/components/link/code/examples/routerLink.tsx index f4c79b5249..d10c01b037 100644 --- a/apps/website/screens/components/link/code/examples/routerLink.tsx +++ b/apps/website/screens/components/link/code/examples/routerLink.tsx @@ -1,8 +1,9 @@ import { DxcLink, DxcInset } from "@dxc-technology/halstack-react"; +import { ReactNode } from "react"; type routerProps = { to: string; - component: React.ReactNode; + component: ReactNode; children: string; }; diff --git a/apps/website/screens/components/nav-tabs/NavTabsPageLayout.tsx b/apps/website/screens/components/nav-tabs/NavTabsPageLayout.tsx index 0982311931..4c2a936fdd 100644 --- a/apps/website/screens/components/nav-tabs/NavTabsPageLayout.tsx +++ b/apps/website/screens/components/nav-tabs/NavTabsPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const NumberInputPageHeading = ({ children }: { children: React.ReactNode }) => { +const NumberInputPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/nav-tabs" }, { label: "Usage", path: "/components/nav-tabs/usage" }, diff --git a/apps/website/screens/components/number-input/NumberInputPageLayout.tsx b/apps/website/screens/components/number-input/NumberInputPageLayout.tsx index d19e3cabb2..dabcadd6b2 100644 --- a/apps/website/screens/components/number-input/NumberInputPageLayout.tsx +++ b/apps/website/screens/components/number-input/NumberInputPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const NumberInputPageHeading = ({ children }: { children: React.ReactNode }) => { +const NumberInputPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/number-input" }, { label: "Usage", path: "/components/number-input/usage" }, diff --git a/apps/website/screens/components/paginator/PaginatorPageLayout.tsx b/apps/website/screens/components/paginator/PaginatorPageLayout.tsx index 27d817acdc..00ae9636b8 100644 --- a/apps/website/screens/components/paginator/PaginatorPageLayout.tsx +++ b/apps/website/screens/components/paginator/PaginatorPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const PaginatorPageHeading = ({ children }: { children: React.ReactNode }) => { +const PaginatorPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/paginator" }, { label: "Usage", path: "/components/paginator/usage" }, diff --git a/apps/website/screens/components/paragraph/ParagraphPageLayout.tsx b/apps/website/screens/components/paragraph/ParagraphPageLayout.tsx index 222bab8345..65063328ca 100644 --- a/apps/website/screens/components/paragraph/ParagraphPageLayout.tsx +++ b/apps/website/screens/components/paragraph/ParagraphPageLayout.tsx @@ -2,8 +2,9 @@ import PageHeading from "@/common/PageHeading"; import { DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ParagraphPageHeading = ({ children }: { children: React.ReactNode }) => { +const ParagraphPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/paragraph" }, { label: "Usage", path: "/components/paragraph/usage" }, diff --git a/apps/website/screens/components/password-input/PasswordInputPageLayout.tsx b/apps/website/screens/components/password-input/PasswordInputPageLayout.tsx index 0aeeecdc25..8a3c6208e9 100644 --- a/apps/website/screens/components/password-input/PasswordInputPageLayout.tsx +++ b/apps/website/screens/components/password-input/PasswordInputPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const PasswordInputPageHeading = ({ children }: { children: React.ReactNode }) => { +const PasswordInputPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/password-input" }, { label: "Usage", path: "/components/password-input/usage" }, diff --git a/apps/website/screens/components/progress-bar/ProgressBarPageLayout.tsx b/apps/website/screens/components/progress-bar/ProgressBarPageLayout.tsx index 0e75213c84..231fd62ee8 100644 --- a/apps/website/screens/components/progress-bar/ProgressBarPageLayout.tsx +++ b/apps/website/screens/components/progress-bar/ProgressBarPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ProgressBarPageHeading = ({ children }: { children: React.ReactNode }) => { +const ProgressBarPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/progress-bar" }, { label: "Usage", path: "/components/progress-bar/usage" }, diff --git a/apps/website/screens/components/quick-nav/QuickNavPageLayout.tsx b/apps/website/screens/components/quick-nav/QuickNavPageLayout.tsx index f651e15548..78ccea72d3 100644 --- a/apps/website/screens/components/quick-nav/QuickNavPageLayout.tsx +++ b/apps/website/screens/components/quick-nav/QuickNavPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const QuickNavPageHeading = ({ children }: { children: React.ReactNode }) => { +const QuickNavPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/quick-nav" }, { label: "Usage", path: "/components/quick-nav/usage" }, diff --git a/apps/website/screens/components/radio-group/RadioGroupPageLayout.tsx b/apps/website/screens/components/radio-group/RadioGroupPageLayout.tsx index 21aa69435b..961904f34e 100644 --- a/apps/website/screens/components/radio-group/RadioGroupPageLayout.tsx +++ b/apps/website/screens/components/radio-group/RadioGroupPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const RadioGroupPageHeading = ({ children }: { children: React.ReactNode }) => { +const RadioGroupPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/radio-group" }, { label: "Usage", path: "/components/radio-group/usage" }, diff --git a/apps/website/screens/components/resultset-table/ResultsetTablePageLayout.tsx b/apps/website/screens/components/resultset-table/ResultsetTablePageLayout.tsx index 23d24610f4..7ef2e6c3d7 100644 --- a/apps/website/screens/components/resultset-table/ResultsetTablePageLayout.tsx +++ b/apps/website/screens/components/resultset-table/ResultsetTablePageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ResultsetTablePageHeading = ({ children }: { children: React.ReactNode }) => { +const ResultsetTablePageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/resultset-table" }, { label: "Usage", path: "/components/resultset-table/usage" }, diff --git a/apps/website/screens/components/select/SelectPageLayout.tsx b/apps/website/screens/components/select/SelectPageLayout.tsx index 6abb331081..c8fbe8d076 100644 --- a/apps/website/screens/components/select/SelectPageLayout.tsx +++ b/apps/website/screens/components/select/SelectPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const SelectPageHeading = ({ children }: { children: React.ReactNode }) => { +const SelectPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/select" }, { label: "Usage", path: "/components/select/usage" }, diff --git a/apps/website/screens/components/sidenav/SidenavPageLayout.tsx b/apps/website/screens/components/sidenav/SidenavPageLayout.tsx index 09bf0dc7bf..0629ad362f 100644 --- a/apps/website/screens/components/sidenav/SidenavPageLayout.tsx +++ b/apps/website/screens/components/sidenav/SidenavPageLayout.tsx @@ -3,8 +3,9 @@ import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; import Link from "next/link"; +import { ReactNode } from "react"; -const SidenavPageHeading = ({ children }: { children: React.ReactNode }) => { +const SidenavPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/sidenav" }, { label: "Usage", path: "/components/sidenav/usage" }, diff --git a/apps/website/screens/components/slider/SliderPageLayout.tsx b/apps/website/screens/components/slider/SliderPageLayout.tsx index a80a0e528a..fe27cda196 100644 --- a/apps/website/screens/components/slider/SliderPageLayout.tsx +++ b/apps/website/screens/components/slider/SliderPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const SliderPageHeading = ({ children }: { children: React.ReactNode }) => { +const SliderPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/slider" }, { label: "Usage", path: "/components/slider/usage" }, diff --git a/apps/website/screens/components/spinner/SpinnerPageLayout.tsx b/apps/website/screens/components/spinner/SpinnerPageLayout.tsx index edbbe57fc1..0390e9adf7 100644 --- a/apps/website/screens/components/spinner/SpinnerPageLayout.tsx +++ b/apps/website/screens/components/spinner/SpinnerPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const SpinnerPageHeading = ({ children }: { children: React.ReactNode }) => { +const SpinnerPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/spinner" }, { label: "Usage", path: "/components/spinner/usage" }, diff --git a/apps/website/screens/components/status-light/StatusLightPageLayout.tsx b/apps/website/screens/components/status-light/StatusLightPageLayout.tsx index dba19b81ce..fbf4fcb7f8 100644 --- a/apps/website/screens/components/status-light/StatusLightPageLayout.tsx +++ b/apps/website/screens/components/status-light/StatusLightPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const StatusLightPageHeading = ({ children }: { children: React.ReactNode }) => { +const StatusLightPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/status-light" }, { label: "Usage", path: "/components/status-light/usage" }, diff --git a/apps/website/screens/components/switch/SwitchPageLayout.tsx b/apps/website/screens/components/switch/SwitchPageLayout.tsx index fdbf5719d0..df66b64c7a 100644 --- a/apps/website/screens/components/switch/SwitchPageLayout.tsx +++ b/apps/website/screens/components/switch/SwitchPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const SwitchPageHeading = ({ children }: { children: React.ReactNode }) => { +const SwitchPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/switch" }, { label: "Usage", path: "/components/switch/usage" }, diff --git a/apps/website/screens/components/table/TablePageLayout.tsx b/apps/website/screens/components/table/TablePageLayout.tsx index bbcbb804dc..fa5013f25b 100644 --- a/apps/website/screens/components/table/TablePageLayout.tsx +++ b/apps/website/screens/components/table/TablePageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const TablePageHeading = ({ children }: { children: React.ReactNode }) => { +const TablePageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/table" }, { label: "Usage", path: "/components/table/usage" }, diff --git a/apps/website/screens/components/tabs/TabsPageLayout.tsx b/apps/website/screens/components/tabs/TabsPageLayout.tsx index 86540cdfbf..f36e61bd52 100644 --- a/apps/website/screens/components/tabs/TabsPageLayout.tsx +++ b/apps/website/screens/components/tabs/TabsPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageLayout from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const TabsPageHeading = ({ children }: { children: React.ReactNode }) => { +const TabsPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/tabs" }, { label: "Usage", path: "/components/tabs/usage" }, diff --git a/apps/website/screens/components/tag/TagPageLayout.tsx b/apps/website/screens/components/tag/TagPageLayout.tsx index 0707c82db8..5149f68540 100644 --- a/apps/website/screens/components/tag/TagPageLayout.tsx +++ b/apps/website/screens/components/tag/TagPageLayout.tsx @@ -3,8 +3,9 @@ import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; import Link from "next/link"; +import { ReactNode } from "react"; -const TagPageHeading = ({ children }: { children: React.ReactNode }) => { +const TagPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/tag" }, { label: "Usage", path: "/components/tag/usage" }, diff --git a/apps/website/screens/components/text-input/TextInputPageLayout.tsx b/apps/website/screens/components/text-input/TextInputPageLayout.tsx index cf4d8192ef..422fc960f3 100644 --- a/apps/website/screens/components/text-input/TextInputPageLayout.tsx +++ b/apps/website/screens/components/text-input/TextInputPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const TextInputPageHeading = ({ children }: { children: React.ReactNode }) => { +const TextInputPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/text-input" }, { label: "Usage", path: "/components/text-input/usage" }, diff --git a/apps/website/screens/components/textarea/TextareaPageLayout.tsx b/apps/website/screens/components/textarea/TextareaPageLayout.tsx index 8733f45373..245469b840 100644 --- a/apps/website/screens/components/textarea/TextareaPageLayout.tsx +++ b/apps/website/screens/components/textarea/TextareaPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const TextareaPageHeading = ({ children }: { children: React.ReactNode }) => { +const TextareaPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/textarea" }, { label: "Usage", path: "/components/textarea/usage" }, diff --git a/apps/website/screens/components/toast/ToastPageLayout.tsx b/apps/website/screens/components/toast/ToastPageLayout.tsx index d55e1e9a78..f636cf1b6e 100644 --- a/apps/website/screens/components/toast/ToastPageLayout.tsx +++ b/apps/website/screens/components/toast/ToastPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ToastPageHeading = ({ children }: { children: React.ReactNode }) => { +const ToastPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/toast" }, { label: "Usage", path: "/components/toast/usage" }, diff --git a/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx b/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx index c2156cbcbc..e38d6e79d9 100644 --- a/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx +++ b/apps/website/screens/components/toggle-group/ToggleGroupPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const ToggleGroupPageHeading = ({ children }: { children: React.ReactNode }) => { +const ToggleGroupPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/toggle-group" }, { label: "Usage", path: "/components/toggle-group/usage" }, diff --git a/apps/website/screens/components/tooltip/TooltipPageLayout.tsx b/apps/website/screens/components/tooltip/TooltipPageLayout.tsx index 4be4361404..b0dfb1e923 100644 --- a/apps/website/screens/components/tooltip/TooltipPageLayout.tsx +++ b/apps/website/screens/components/tooltip/TooltipPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const TooltipPageHeading = ({ children }: { children: React.ReactNode }) => { +const TooltipPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/tooltip" }, { label: "Usage", path: "/components/tooltip/usage" }, diff --git a/apps/website/screens/components/typography/TypographyPageLayout.tsx b/apps/website/screens/components/typography/TypographyPageLayout.tsx index 1822f75212..7354fe382e 100644 --- a/apps/website/screens/components/typography/TypographyPageLayout.tsx +++ b/apps/website/screens/components/typography/TypographyPageLayout.tsx @@ -2,8 +2,9 @@ import PageHeading from "@/common/PageHeading"; import { DxcFlex, DxcAlert } from "@dxc-technology/halstack-react"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const TypographyPageHeading = ({ children }: { children: React.ReactNode }) => { +const TypographyPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/typography" }, { label: "Usage", path: "/components/typography/usage" }, diff --git a/apps/website/screens/components/wizard/WizardPageLayout.tsx b/apps/website/screens/components/wizard/WizardPageLayout.tsx index e91d0edfd0..fef897a481 100644 --- a/apps/website/screens/components/wizard/WizardPageLayout.tsx +++ b/apps/website/screens/components/wizard/WizardPageLayout.tsx @@ -2,8 +2,9 @@ import { DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; import ComponentHeading from "@/common/ComponentHeading"; +import { ReactNode } from "react"; -const WizardPageHeading = ({ children }: { children: React.ReactNode }) => { +const WizardPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/components/wizard" }, { label: "Usage", path: "/components/wizard/usage" }, diff --git a/apps/website/screens/overview/releases/ReleasesPage.tsx b/apps/website/screens/overview/releases/ReleasesPage.tsx index cdb9581a2f..9af6e78248 100644 --- a/apps/website/screens/overview/releases/ReleasesPage.tsx +++ b/apps/website/screens/overview/releases/ReleasesPage.tsx @@ -6,11 +6,12 @@ import QuickNavContainerLayout from "@/common/QuickNavContainerLayout"; import HalstackMarkdownParser from "@/common/HalstackMarkdownParser"; import Link from "next/link"; import Code from "@/common/Code"; +import { ReactNode } from "react"; type Section = { title: string; level?: 1 | 2 | 3 | 4 | 5; - content?: React.ReactNode; + content?: ReactNode; subSections?: Section[]; navSubtitle?: string; }; diff --git a/apps/website/screens/principles/typography/TypographyPageLayout.tsx b/apps/website/screens/principles/typography/TypographyPageLayout.tsx index f94604cc95..12a74eb8b2 100644 --- a/apps/website/screens/principles/typography/TypographyPageLayout.tsx +++ b/apps/website/screens/principles/typography/TypographyPageLayout.tsx @@ -1,8 +1,9 @@ import { DxcHeading, DxcParagraph, DxcFlex } from "@dxc-technology/halstack-react"; import PageHeading from "@/common/PageHeading"; import TabsPageHeading from "@/common/TabsPageLayout"; +import { ReactNode } from "react"; -const TypographyPageHeading = ({ children }: { children: React.ReactNode }) => { +const TypographyPageHeading = ({ children }: { children: ReactNode }) => { const tabs = [ { label: "Code", path: "/principles/typography" }, { label: "Usage", path: "/principles/typography/usage" }, diff --git a/apps/website/screens/theme-generator/components/Mode.tsx b/apps/website/screens/theme-generator/components/Mode.tsx index 8c522eefd2..b883eeced3 100644 --- a/apps/website/screens/theme-generator/components/Mode.tsx +++ b/apps/website/screens/theme-generator/components/Mode.tsx @@ -1,9 +1,10 @@ +import { ReactNode } from "react"; import styled from "styled-components"; type ModeProps = { text: string; mode?: "light" | "dark"; - children: React.ReactNode; + children: ReactNode; }; const Mode = ({ text, mode = "light", children }: ModeProps): JSX.Element => ( <ModeContainer mode={mode}> diff --git a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx index 3ef5ac31cf..07da45522f 100644 --- a/apps/website/screens/theme-generator/components/previews/DataGrid.tsx +++ b/apps/website/screens/theme-generator/components/previews/DataGrid.tsx @@ -1,12 +1,12 @@ -import { useState } from "react"; +import { ComponentProps, useState } from "react"; import { DxcContainer, DxcDataGrid } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; -type DataGridPropsType = React.ComponentProps<typeof DxcDataGrid>; +type DataGridPropsType = ComponentProps<typeof DxcDataGrid>; type DataGridColumnsPropsType = DataGridPropsType["columns"]; type DataGridRowsPropsType = DataGridPropsType["rows"]; -type DataGridActionsPropType = React.ComponentProps<typeof DxcDataGrid.ActionsCell>["actions"]; +type DataGridActionsPropType = ComponentProps<typeof DxcDataGrid.ActionsCell>["actions"]; const actions: DataGridActionsPropType = [ { diff --git a/apps/website/screens/theme-generator/components/previews/Header.tsx b/apps/website/screens/theme-generator/components/previews/Header.tsx index 5eff19df5e..dbfe603ae9 100644 --- a/apps/website/screens/theme-generator/components/previews/Header.tsx +++ b/apps/website/screens/theme-generator/components/previews/Header.tsx @@ -1,8 +1,9 @@ import { DxcApplicationLayout, DxcDropdown, DxcFlex, DxcLink, DxcParagraph } from "@dxc-technology/halstack-react"; import Mode from "../Mode"; import PreviewContainer from "./PreviewContainer"; +import { ComponentProps } from "react"; -type DropdownTypes = React.ComponentProps<typeof DxcDropdown>; +type DropdownTypes = ComponentProps<typeof DxcDropdown>; const options: DropdownTypes["options"] = [ { diff --git a/apps/website/screens/theme-generator/components/previews/PreviewContainer.tsx b/apps/website/screens/theme-generator/components/previews/PreviewContainer.tsx index 0483d6f23e..3af49aaeb7 100644 --- a/apps/website/screens/theme-generator/components/previews/PreviewContainer.tsx +++ b/apps/website/screens/theme-generator/components/previews/PreviewContainer.tsx @@ -1,7 +1,8 @@ import { DxcFlex } from "@dxc-technology/halstack-react"; +import { ReactNode } from "react"; type PreviewContainerProps = { - children: React.ReactNode; + children: ReactNode; }; const PreviewContainer = ({ children }: PreviewContainerProps) => ( diff --git a/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx b/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx index 81912a765a..a78ebef6f0 100644 --- a/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx +++ b/apps/website/screens/theme-generator/components/widgets/ImageConfig.tsx @@ -1,16 +1,16 @@ import { DxcFlex } from "@dxc-technology/halstack-react"; -import { useState, useEffect } from "react"; +import { useState, useEffect, MouseEvent, ChangeEvent } from "react"; import styled from "styled-components"; import ThemeInputWidgetProps from "./common/types"; const ImageConfig = ({ propertyName, propertyValue, onChangeCustomTheme }: ThemeInputWidgetProps): JSX.Element => { const [logoImage, setLogoImage] = useState(propertyValue); - const clickToUpload = (event: React.MouseEvent<HTMLButtonElement>) => { + const clickToUpload = (event: MouseEvent<HTMLButtonElement>) => { const input = event.currentTarget.previousSibling as HTMLInputElement; input.click(); }; - const upload = (event: React.ChangeEvent<HTMLInputElement>) => { + const upload = (event: ChangeEvent<HTMLInputElement>) => { const files = event.target.files; if (files && files[0]) { const url = URL.createObjectURL(files[0]); diff --git a/apps/website/screens/theme-generator/types.ts b/apps/website/screens/theme-generator/types.ts index ef0665362e..236fcec92c 100644 --- a/apps/website/screens/theme-generator/types.ts +++ b/apps/website/screens/theme-generator/types.ts @@ -1,3 +1,5 @@ +import { Dispatch, SetStateAction } from "react"; + export type IndexedThemeInput = { [key: string]: string; }; @@ -7,7 +9,7 @@ export type IndexedTheme = { type ImportDialogProps = { customThemeSchema: IndexedTheme; - setCustomTheme: React.Dispatch<React.SetStateAction<IndexedTheme>>; + setCustomTheme: Dispatch<SetStateAction<IndexedTheme>>; setDialogVisible: (isVisible: boolean) => void; }; diff --git a/packages/lib/.storybook/components/ExampleContainer.tsx b/packages/lib/.storybook/components/ExampleContainer.tsx index d292bff691..a72cf04038 100644 --- a/packages/lib/.storybook/components/ExampleContainer.tsx +++ b/packages/lib/.storybook/components/ExampleContainer.tsx @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import styled from "styled-components"; type PseudoStates = @@ -11,7 +12,7 @@ type PseudoStates = | "pseudo-visited"; type Props = { - children?: React.ReactNode; + children?: ReactNode; pseudoState?: PseudoStates; expanded?: boolean; }; diff --git a/packages/lib/src/BackgroundColorContext.tsx b/packages/lib/src/BackgroundColorContext.tsx index e4120745d2..57d27557d0 100644 --- a/packages/lib/src/BackgroundColorContext.tsx +++ b/packages/lib/src/BackgroundColorContext.tsx @@ -1,4 +1,4 @@ -import { createContext, useMemo } from "react"; +import { createContext, ReactNode, useMemo } from "react"; import Color from "color"; type BackgroundColors = "dark" | "light"; @@ -18,7 +18,7 @@ const getColorType = (hexColor: string): BackgroundColors => { type BackgroundColorProviderPropsType = { color: string; - children: React.ReactNode; + children: ReactNode; }; const BackgroundColorProvider = ({ color, children }: BackgroundColorProviderPropsType): JSX.Element => { const colorType = useMemo(() => getColorType(color), [color]); diff --git a/packages/lib/src/accordion/types.ts b/packages/lib/src/accordion/types.ts index 48f1140c99..cfa1c191dd 100644 --- a/packages/lib/src/accordion/types.ts +++ b/packages/lib/src/accordion/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { Margin, SVG, Space } from "../common/utils"; type Props = { @@ -36,7 +37,7 @@ type Props = { * The expanded panel of the accordion. This area can be used to render * custom content. */ - children: React.ReactNode; + children: ReactNode; /** * Size of the margin to be applied to the component ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge'). * You can pass an object with 'top', 'bottom', 'left' and 'right' properties in order to specify different margin sizes. diff --git a/packages/lib/src/alert/types.ts b/packages/lib/src/alert/types.ts index 98207b34d8..71c6643784 100644 --- a/packages/lib/src/alert/types.ts +++ b/packages/lib/src/alert/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { SVG } from "../common/utils"; type Action = { @@ -8,7 +9,7 @@ type Action = { type Message = { onClose?: () => void; - text: React.ReactNode; + text: ReactNode; }; type CommonProps = { @@ -36,5 +37,5 @@ export default Props; export type ModalAlertWrapperProps = { condition: boolean; onClose?: () => void; - children: React.ReactNode; + children: ReactNode; }; diff --git a/packages/lib/src/bleed/types.ts b/packages/lib/src/bleed/types.ts index 7128520ac4..f724eaf446 100644 --- a/packages/lib/src/bleed/types.ts +++ b/packages/lib/src/bleed/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { CoreSpacingTokensType } from "../common/coreTokens"; type Props = { @@ -32,7 +33,7 @@ type Props = { /** * Custom content inside the bleed. */ - children: React.ReactNode; + children: ReactNode; }; export default Props; diff --git a/packages/lib/src/bulleted-list/types.ts b/packages/lib/src/bulleted-list/types.ts index eed522066c..280d444429 100644 --- a/packages/lib/src/bulleted-list/types.ts +++ b/packages/lib/src/bulleted-list/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { SVG } from "../common/utils"; type IconProps = { @@ -12,7 +13,7 @@ type IconProps = { /** * Text to be shown in the list. */ - children: React.ReactNode; + children: ReactNode; }; type OtherProps = { @@ -27,7 +28,7 @@ type OtherProps = { /** * Text to be shown in the list. */ - children: React.ReactNode; + children: ReactNode; }; type Props = IconProps | OtherProps; @@ -38,5 +39,5 @@ export type BulletedListItemPropsType = { /** * Text to be shown in the list. */ - children?: React.ReactNode; + children?: ReactNode; }; diff --git a/packages/lib/src/card/types.ts b/packages/lib/src/card/types.ts index ad4b37bbe1..bca1810ff7 100644 --- a/packages/lib/src/card/types.ts +++ b/packages/lib/src/card/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { Space } from "../common/utils"; type Size = { @@ -58,7 +59,7 @@ type Props = { /** * Custom content that will be placed inside the component. */ - children?: React.ReactNode; + children?: ReactNode; }; export default Props; diff --git a/packages/lib/src/common/utils.ts b/packages/lib/src/common/utils.ts index 077cd1ad97..c861119b5e 100644 --- a/packages/lib/src/common/utils.ts +++ b/packages/lib/src/common/utils.ts @@ -1,3 +1,4 @@ +import { ReactNode, SVGProps } from "react"; import { spaces } from "./variables"; export type Space = "xxsmall" | "xsmall" | "small" | "medium" | "large" | "xlarge" | "xxlarge"; @@ -7,7 +8,7 @@ export type Margin = { left?: Space; right?: Space; }; -export type SVG = React.ReactNode & React.SVGProps<SVGSVGElement>; +export type SVG = ReactNode & SVGProps<SVGSVGElement>; export type Side = keyof Margin; export const getMargin = (marginProp: Space | Margin | undefined, side: Side) => diff --git a/packages/lib/src/container/types.ts b/packages/lib/src/container/types.ts index bfefc8672f..056f6b4cd8 100644 --- a/packages/lib/src/container/types.ts +++ b/packages/lib/src/container/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { CoreColorTokensType } from "../common/coreTokens"; import { Space as SpacingValues } from "../common/utils" type Space = @@ -80,7 +81,7 @@ type Props = { /** * Custom content inside the container. */ - children: React.ReactNode; + children: ReactNode; /** * Sets the display CSS property. * The set of values is limited to the ones related to the outer display type. diff --git a/packages/lib/src/contextual-menu/ContextualMenu.test.tsx b/packages/lib/src/contextual-menu/ContextualMenu.test.tsx index 62204900a3..9645f998a3 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.test.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, getByText, render } from "@testing-library/react"; +import { fireEvent, render } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import DxcContextualMenu from "./ContextualMenu"; diff --git a/packages/lib/src/contextual-menu/ItemAction.tsx b/packages/lib/src/contextual-menu/ItemAction.tsx index d16921a3af..3435b2bb60 100644 --- a/packages/lib/src/contextual-menu/ItemAction.tsx +++ b/packages/lib/src/contextual-menu/ItemAction.tsx @@ -1,4 +1,4 @@ -import { cloneElement, useState } from "react"; +import { cloneElement, MouseEvent, useState } from "react"; import styled from "styled-components"; import CoreTokens from "../common/coreTokens"; import { ItemActionProps } from "./types"; @@ -17,7 +17,7 @@ const ItemAction = ({ badge, collapseIcon, icon, label, depthLevel, ...props }: {icon && depthLevel === 0 && <Icon>{typeof icon === "string" ? <DxcIcon icon={icon} /> : icon}</Icon>} <Text selected={props.selected} - onMouseEnter={(event: React.MouseEvent<HTMLSpanElement>) => { + onMouseEnter={(event: MouseEvent<HTMLSpanElement>) => { const text = event.currentTarget; setHasTooltip(text.scrollWidth > text.clientWidth); }} diff --git a/packages/lib/src/contextual-menu/types.ts b/packages/lib/src/contextual-menu/types.ts index f9846b53bd..c6107f8ae6 100644 --- a/packages/lib/src/contextual-menu/types.ts +++ b/packages/lib/src/contextual-menu/types.ts @@ -1,7 +1,8 @@ +import { ButtonHTMLAttributes, Dispatch, ReactElement, ReactNode, SetStateAction } from "react"; import { SVG } from "../common/utils"; type CommonItemProps = { - badge?: React.ReactElement; + badge?: ReactElement; icon?: string | SVG; label: string; }; @@ -23,7 +24,7 @@ type Props = { type ItemWithId = Item & { id: number }; type GroupItemWithId = { - badge?: React.ReactElement; + badge?: ReactElement; icon: string | SVG; items: (ItemWithId | GroupItemWithId)[]; label: string; @@ -33,9 +34,9 @@ type SectionWithId = { items: (ItemWithId | GroupItemWithId)[]; title?: string } type SingleItemProps = ItemWithId & { depthLevel: number }; type GroupItemProps = GroupItemWithId & { depthLevel: number }; type MenuItemProps = { item: ItemWithId | GroupItemWithId; depthLevel?: number }; -type ItemActionProps = React.ButtonHTMLAttributes<HTMLButtonElement> & { +type ItemActionProps = ButtonHTMLAttributes<HTMLButtonElement> & { badge?: Item["badge"]; - collapseIcon?: React.ReactNode; + collapseIcon?: ReactNode; depthLevel: number; icon?: Item["icon"]; label: Item["label"]; @@ -46,10 +47,10 @@ type SectionProps = { index: number; length: number; }; -type SubMenuProps = { children: React.ReactNode; id?: string }; +type SubMenuProps = { children: ReactNode; id?: string }; type ContextualMenuContextProps = { selectedItemId: number; - setSelectedItemId: React.Dispatch<React.SetStateAction<number>>; + setSelectedItemId: Dispatch<SetStateAction<number>>; }; export default Props; diff --git a/packages/lib/src/dialog/types.ts b/packages/lib/src/dialog/types.ts index 8884d413d2..a7f729ae6d 100644 --- a/packages/lib/src/dialog/types.ts +++ b/packages/lib/src/dialog/types.ts @@ -1,3 +1,5 @@ +import { ReactNode } from "react"; + type Props = { /** * If true, the close button will be visible. @@ -24,7 +26,7 @@ type Props = { * can lead to unpredictable behaviour for keyboard users, affecting * the order of focus and focus locking within the dialog. */ - children: React.ReactNode; + children: ReactNode; /** * Value of the tabindex applied to the close button. * Note that values greater than 0 are strongly discouraged. It can diff --git a/packages/lib/src/dropdown/types.ts b/packages/lib/src/dropdown/types.ts index a08cdd9ace..459d783548 100644 --- a/packages/lib/src/dropdown/types.ts +++ b/packages/lib/src/dropdown/types.ts @@ -1,3 +1,4 @@ +import { CSSProperties, KeyboardEvent } from "react"; import { Margin, SVG, Space } from "../common/utils"; type Size = "small" | "medium" | "large" | "fillParent" | "fitContent"; @@ -83,9 +84,9 @@ export type DropdownMenuProps = { iconsPosition: "before" | "after"; visualFocusIndex: number; menuItemOnClick: (value: string) => void; - onKeyDown: (event: React.KeyboardEvent<HTMLUListElement>) => void; + onKeyDown: (event: KeyboardEvent<HTMLUListElement>) => void; options: Option[]; - styles: React.CSSProperties; + styles: CSSProperties; }; export type DropdownMenuItemProps = { diff --git a/packages/lib/src/flex/types.ts b/packages/lib/src/flex/types.ts index e8a7104930..e125840f10 100644 --- a/packages/lib/src/flex/types.ts +++ b/packages/lib/src/flex/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { CoreSpacingTokensType } from "../common/coreTokens"; type Gap = @@ -113,7 +114,7 @@ type Props = CommonProps & { /** * Custom content inside the flex container. */ - children: React.ReactNode; + children: ReactNode; }; export type StyledProps = CommonProps & { diff --git a/packages/lib/src/footer/types.ts b/packages/lib/src/footer/types.ts index cfa3e561f0..e87c1c1fd3 100644 --- a/packages/lib/src/footer/types.ts +++ b/packages/lib/src/footer/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { SVG, Space } from "../common/utils"; type SocialLink = { @@ -45,7 +46,7 @@ type FooterPropsType = { * The center section of the footer. Can be used to render custom * content in this area. */ - children?: React.ReactNode; + children?: ReactNode; /** * Size of the top margin to be applied to the footer. */ diff --git a/packages/lib/src/grid/types.ts b/packages/lib/src/grid/types.ts index 97ccdcddd9..1d4d198f67 100644 --- a/packages/lib/src/grid/types.ts +++ b/packages/lib/src/grid/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { CoreSpacingTokensType } from "../common/coreTokens"; type Gap = { rowGap: CoreSpacingTokensType; columnGap?: CoreSpacingTokensType } | { rowGap?: CoreSpacingTokensType; columnGap: CoreSpacingTokensType } | CoreSpacingTokensType; @@ -53,7 +54,7 @@ export type GridItemProps = { /** * Custom content inside the grid container. */ - children: React.ReactNode; + children: ReactNode; }; type Props = GridItemProps & { diff --git a/packages/lib/src/header/types.ts b/packages/lib/src/header/types.ts index c2b8fbea5e..6049310c0a 100644 --- a/packages/lib/src/header/types.ts +++ b/packages/lib/src/header/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { Space } from "../common/utils"; type Props = { @@ -10,12 +11,12 @@ type Props = { * for the first child in the content, so we recommend the use of React.Fragment * to be applied correctly. Otherwise, the styles can be modified. */ - content?: React.ReactNode; + content?: ReactNode; /** * Content shown in responsive version. It receives the close menu handler that can * be used to add that functionality when a element is clicked. */ - responsiveContent?: (closeHandler: () => void) => React.ReactNode; + responsiveContent?: (closeHandler: () => void) => ReactNode; /** * This function will be called when the user clicks the header logo. */ diff --git a/packages/lib/src/image/types.ts b/packages/lib/src/image/types.ts index 86f1191ef0..c78742f830 100644 --- a/packages/lib/src/image/types.ts +++ b/packages/lib/src/image/types.ts @@ -1,3 +1,5 @@ +import { ReactEventHandler, ReactNode } from "react"; + type Props = { /** * Alternative text description displayed when the specified image is not loaded. @@ -57,17 +59,17 @@ type Props = { /** * This function will be called when the image is loaded. */ - onLoad?: React.ReactEventHandler<HTMLImageElement>; + onLoad?: ReactEventHandler<HTMLImageElement>; /** * This function will be called when the image fails to load. */ - onError?: React.ReactEventHandler<HTMLImageElement>; + onError?: ReactEventHandler<HTMLImageElement>; }; export type CaptionWrapperProps = { condition: boolean; - wrapper: (children: React.ReactNode) => JSX.Element; - children: React.ReactNode; + wrapper: (children: ReactNode) => JSX.Element; + children: ReactNode; }; export default Props; diff --git a/packages/lib/src/inset/types.ts b/packages/lib/src/inset/types.ts index 1410b85f81..cb6567e7c2 100644 --- a/packages/lib/src/inset/types.ts +++ b/packages/lib/src/inset/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { CoreSpacingTokensType } from "../common/coreTokens"; type Props = { @@ -32,7 +33,7 @@ type Props = { /** * Custom content inside the inset. */ - children: React.ReactNode; + children: ReactNode; }; export default Props; diff --git a/packages/lib/src/layout/types.ts b/packages/lib/src/layout/types.ts index 5302d84856..45d151c95d 100644 --- a/packages/lib/src/layout/types.ts +++ b/packages/lib/src/layout/types.ts @@ -1,19 +1,21 @@ +import { ReactElement, ReactNode } from "react"; + export type AppLayoutMainPropsType = { /** * Everything between the tags will be displayed as the content of the main part of the application. */ - children: React.ReactNode; + children: ReactNode; }; export type AppLayoutSidenavPropsType = { /** * The area inside the sidenav. This area can be used to render the content inside the sidenav. */ - children: React.ReactNode; + children: ReactNode; /** * The area assigned to render the sidenav title. It is highly recommended to use the sidenav title. */ - title?: React.ReactNode; + title?: ReactNode; }; type ApplicationLayoutPropsType = { @@ -25,19 +27,19 @@ type ApplicationLayoutPropsType = { /** * Header content. */ - header?: React.ReactNode; + header?: ReactNode; /** * Sidenav content */ - sidenav?: React.ReactNode; + sidenav?: ReactNode; /** * Footer content */ - footer?: React.ReactNode; + footer?: ReactNode; /** * Use the DxcApplicationLayout.Main provided to render main content. */ - children: React.ReactElement<AppLayoutMainPropsType>; + children: ReactElement<AppLayoutMainPropsType>; }; export default ApplicationLayoutPropsType; diff --git a/packages/lib/src/layout/utils.tsx b/packages/lib/src/layout/utils.tsx index 9436eceef5..7f785f7c26 100644 --- a/packages/lib/src/layout/utils.tsx +++ b/packages/lib/src/layout/utils.tsx @@ -1,4 +1,4 @@ -import { Children, isValidElement, useState, useEffect } from "react"; +import { Children, isValidElement, useState, useEffect, ElementType } from "react"; import layoutIcons from "./Icons"; import ApplicationLayoutPropsType from "./types"; @@ -37,7 +37,7 @@ export const socialLinks = [ }, ]; -export const findChildType = (children: ApplicationLayoutPropsType["children"], childType: React.ElementType) => +export const findChildType = (children: ApplicationLayoutPropsType["children"], childType: ElementType) => Children.toArray(children).find((child) => isValidElement(child) && child.type === childType); export const useResponsive = (breakpoint: string) => { diff --git a/packages/lib/src/link/Link.tsx b/packages/lib/src/link/Link.tsx index 56b1fced6f..2310df5f26 100644 --- a/packages/lib/src/link/Link.tsx +++ b/packages/lib/src/link/Link.tsx @@ -1,4 +1,4 @@ -import { forwardRef } from "react"; +import { forwardRef, Ref } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; @@ -99,7 +99,7 @@ const DxcLink = forwardRef( children, ...otherProps }: LinkProps, - ref: React.Ref<HTMLAnchorElement> + ref: Ref<HTMLAnchorElement> ): JSX.Element => { const colorsTheme = useTheme(); diff --git a/packages/lib/src/resultset-table/types.ts b/packages/lib/src/resultset-table/types.ts index 0dd827ac36..981c708cfe 100644 --- a/packages/lib/src/resultset-table/types.ts +++ b/packages/lib/src/resultset-table/types.ts @@ -1,10 +1,11 @@ +import { ReactNode } from "react"; import { Margin, Space } from "../common/utils"; export type Column = { /** * Column display value. */ - displayValue: React.ReactNode; + displayValue: ReactNode; /** * Boolean value to indicate whether the column is sortable or not. */ @@ -15,7 +16,7 @@ type Cell = { /** * Value to be displayed in the cell. */ - displayValue: React.ReactNode; + displayValue: ReactNode; /** * Value to be used when sorting the table by that * column. If not indicated displayValue will be used for sorting. diff --git a/packages/lib/src/select/ListOption.tsx b/packages/lib/src/select/ListOption.tsx index 3f2b82ce21..e61c5f6cbb 100644 --- a/packages/lib/src/select/ListOption.tsx +++ b/packages/lib/src/select/ListOption.tsx @@ -2,7 +2,7 @@ import styled from "styled-components"; import { OptionProps } from "./types"; import DxcCheckbox from "../checkbox/Checkbox"; import DxcIcon from "../icon/Icon"; -import { useState } from "react"; +import { MouseEvent, useState } from "react"; import { TooltipWrapper } from "../tooltip/Tooltip"; const ListOption = ({ @@ -17,7 +17,7 @@ const ListOption = ({ }: OptionProps): JSX.Element => { const [hasTooltip, setHasTooltip] = useState(false); - const handleOnMouseEnter = (event: React.MouseEvent<HTMLSpanElement>) => { + const handleOnMouseEnter = (event: MouseEvent<HTMLSpanElement>) => { const text = event.currentTarget; setHasTooltip(text.scrollWidth > text.clientWidth); }; diff --git a/packages/lib/src/select/types.ts b/packages/lib/src/select/types.ts index 936c72aa43..d6858b661c 100644 --- a/packages/lib/src/select/types.ts +++ b/packages/lib/src/select/types.ts @@ -1,3 +1,4 @@ +import { CSSProperties } from "react"; import { Margin, SVG, Space } from "../common/utils"; export type ListOptionGroupType = { @@ -186,7 +187,7 @@ export type ListboxProps = { optionalItem: ListOptionType; searchable: boolean; handleOptionOnClick: (option: ListOptionType) => void; - styles: React.CSSProperties; + styles: CSSProperties; }; /** diff --git a/packages/lib/src/sidenav/Sidenav.tsx b/packages/lib/src/sidenav/Sidenav.tsx index 5472c307a8..3f30ae6708 100644 --- a/packages/lib/src/sidenav/Sidenav.tsx +++ b/packages/lib/src/sidenav/Sidenav.tsx @@ -1,4 +1,4 @@ -import { createContext, forwardRef, useContext, useEffect, useState } from "react"; +import { createContext, Dispatch, forwardRef, MouseEvent, SetStateAction, useContext, useEffect, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcBleed from "../bleed/Bleed"; import CoreTokens from "../common/coreTokens"; @@ -44,7 +44,7 @@ const Section = ({ children }: SidenavSectionPropsType): JSX.Element => ( </> ); -const GroupContext = createContext<React.Dispatch<React.SetStateAction<boolean>> | null>(null); +const GroupContext = createContext<Dispatch<SetStateAction<boolean>> | null>(null); const Group = ({ title, collapsable = false, icon, children }: SidenavGroupPropsType): JSX.Element => { const [collapsed, setCollapsed] = useState(false); const [isSelected, changeIsSelected] = useState(false); @@ -85,7 +85,7 @@ const Link = forwardRef<HTMLAnchorElement, SidenavLinkPropsType>( ): JSX.Element => { const changeIsGroupSelected = useContext(GroupContext); const setIsSidenavVisibleResponsive = useResponsiveSidenavVisibility(); - const handleClick = ($event: React.MouseEvent<HTMLAnchorElement>) => { + const handleClick = ($event: MouseEvent<HTMLAnchorElement>) => { onClick?.($event); setIsSidenavVisibleResponsive?.(false); }; diff --git a/packages/lib/src/sidenav/types.ts b/packages/lib/src/sidenav/types.ts index 4567e8a166..ed577b49d5 100644 --- a/packages/lib/src/sidenav/types.ts +++ b/packages/lib/src/sidenav/types.ts @@ -1,17 +1,18 @@ +import { MouseEvent, ReactNode } from "react"; import { SVG } from "../common/utils"; export type SidenavTitlePropsType = { /** * The area inside the sidenav title. This area can be used to render custom content. */ - children: React.ReactNode; + children: ReactNode; }; export type SidenavSectionPropsType = { /** * The area inside the sidenav section. This area can be used to render sidenav groups, links and custom content. */ - children: React.ReactNode; + children: ReactNode; }; export type SidenavGroupPropsType = { @@ -31,7 +32,7 @@ export type SidenavGroupPropsType = { /** * The area inside the sidenav group. This area can be used to render sidenav links. */ - children: React.ReactNode; + children: ReactNode; }; export type SidenavLinkPropsType = { @@ -56,11 +57,11 @@ export type SidenavLinkPropsType = { /** * This function will be called when the user clicks the link and the event will be passed to this function. */ - onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void; + onClick?: (event: MouseEvent<HTMLAnchorElement>) => void; /** * The area inside the sidenav link. */ - children: React.ReactNode; + children: ReactNode; /** * Value of the tabindex. */ @@ -71,11 +72,11 @@ type Props = { /** * The area assigned to render the sidenav title. It is highly recommended to use the sidenav title. */ - title?: React.ReactNode; + title?: ReactNode; /** * The area inside the sidenav. This area can be used to render the content inside the sidenav. */ - children: React.ReactNode; + children: ReactNode; }; export default Props; diff --git a/packages/lib/src/table/types.ts b/packages/lib/src/table/types.ts index b779b795ab..0c9350550b 100644 --- a/packages/lib/src/table/types.ts +++ b/packages/lib/src/table/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { Margin, SVG, Space } from "../common/utils"; import { Option } from "../dropdown/types"; @@ -29,7 +30,7 @@ type Props = { * The center section of the table. Can be used to render custom * content in this area. */ - children: React.ReactNode; + children: ReactNode; /** * Size of the margin to be applied to the component ('xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge'). * You can pass an object with 'top', 'bottom', 'left' and 'right' properties in order to specify different margin sizes. diff --git a/packages/lib/src/tabs/Tab.tsx b/packages/lib/src/tabs/Tab.tsx index 76d12c9815..d23dc56052 100644 --- a/packages/lib/src/tabs/Tab.tsx +++ b/packages/lib/src/tabs/Tab.tsx @@ -1,4 +1,4 @@ -import { forwardRef, Ref, useContext, useEffect, useRef } from "react"; +import { forwardRef, KeyboardEvent, MutableRefObject, Ref, useContext, useEffect, useRef } from "react"; import styled from "styled-components"; import DxcBadge from "../badge/Badge"; import DxcIcon from "../icon/Icon"; @@ -53,7 +53,7 @@ const DxcTab = forwardRef( } }, [active, label]); - const handleOnKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => { + const handleOnKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => { switch (event.key) { case " ": case "Enter": @@ -82,7 +82,7 @@ const DxcTab = forwardRef( if (typeof ref === "function") { ref(anchorRef); } else { - const currentRef = ref as React.MutableRefObject<HTMLButtonElement | null>; + const currentRef = ref as MutableRefObject<HTMLButtonElement | null>; currentRef.current = anchorRef; } } diff --git a/packages/lib/src/tabs/TabLegacy.tsx b/packages/lib/src/tabs/TabLegacy.tsx index 1c6d2ce6a4..c5494987f2 100644 --- a/packages/lib/src/tabs/TabLegacy.tsx +++ b/packages/lib/src/tabs/TabLegacy.tsx @@ -7,7 +7,7 @@ import { TabPropsLegacy } from "./types"; const Tab = forwardRef( ( { active, tab, tabIndex, hasLabelAndIcon, iconPosition, onClick, onMouseEnter, onMouseLeave }: TabPropsLegacy, - ref: React.Ref<HTMLButtonElement> + ref: Ref<HTMLButtonElement> ): JSX.Element => ( <TabContainer role="tab" diff --git a/packages/lib/src/tabs/Tabs.tsx b/packages/lib/src/tabs/Tabs.tsx index 71d9694af4..73a60fe232 100644 --- a/packages/lib/src/tabs/Tabs.tsx +++ b/packages/lib/src/tabs/Tabs.tsx @@ -1,6 +1,7 @@ import { Children, isValidElement, + KeyboardEvent, MutableRefObject, ReactElement, useCallback, @@ -154,7 +155,7 @@ const DxcTabs = ({ setCountClick(moveX); }; - const handleOnKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => { + const handleOnKeyDown = (event: KeyboardEvent<HTMLDivElement>) => { const activeTab = childrenArray.findIndex((child: ReactElement) => child.props.label === activeTabLabel); switch (event.key) { case "Left": diff --git a/packages/lib/src/tabs/TabsLegacy.tsx b/packages/lib/src/tabs/TabsLegacy.tsx index 5ffd215e14..72bb57e8ba 100644 --- a/packages/lib/src/tabs/TabsLegacy.tsx +++ b/packages/lib/src/tabs/TabsLegacy.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { KeyboardEvent, MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; @@ -7,7 +7,7 @@ import useTranslatedLabels from "../utils/useTranslatedLabels"; import Tab from "./TabLegacy"; import TabsPropsType from "./types"; -const useResize = (refTabList: React.MutableRefObject<HTMLDivElement | null>) => { +const useResize = (refTabList: MutableRefObject<HTMLDivElement | null>) => { const [viewWidth, setViewWidth] = useState(0); const handleWindowSizeChange = useCallback(() => { @@ -183,7 +183,7 @@ const DxcTabs = ({ } }; - const handleOnKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => { + const handleOnKeyDown = (event: KeyboardEvent<HTMLDivElement>) => { switch (event.key) { case "Left": case "ArrowLeft": diff --git a/packages/lib/src/tabs/types.ts b/packages/lib/src/tabs/types.ts index 63062c6e19..96a41c9daf 100644 --- a/packages/lib/src/tabs/types.ts +++ b/packages/lib/src/tabs/types.ts @@ -1,4 +1,4 @@ -import { ReactNode, SVGProps } from "react"; +import { ReactNode } from "react"; import type { Space, Margin, SVG } from "../common/utils"; @@ -70,7 +70,7 @@ export type TabProps = { title?: string; disabled?: boolean; notificationNumber?: boolean | number; - children: React.ReactNode; + children: ReactNode; onClick?: () => void; onHover?: () => void; }; @@ -132,7 +132,7 @@ type NewProps = { * Contains one or more DxcTabs.Tab. */ // children?: React.ReactElement<TabProps>[]; - children?: React.ReactNode; + children?: ReactNode; }; type Props = LegacyProps & NewProps; diff --git a/packages/lib/src/tag/Tag.tsx b/packages/lib/src/tag/Tag.tsx index 407fc508e1..2b62ef25bf 100644 --- a/packages/lib/src/tag/Tag.tsx +++ b/packages/lib/src/tag/Tag.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { ReactNode, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; @@ -9,8 +9,8 @@ import CoreTokens from "../common/coreTokens"; type TagWrapperProps = { condition: boolean; - wrapper: (_children: React.ReactNode) => JSX.Element; - children: React.ReactNode; + wrapper: (_children: ReactNode) => JSX.Element; + children: ReactNode; }; const TagWrapper = ({ condition, wrapper, children }: TagWrapperProps): JSX.Element => ( @@ -35,7 +35,7 @@ const DxcTag = ({ const colorsTheme = useTheme(); const [isHovered, changeIsHovered] = useState(false); - const wrapperComponent = (children: React.ReactNode) => { + const wrapperComponent = (children: ReactNode) => { if (onClick) { return <StyledButton tabIndex={tabIndex}>{children}</StyledButton>; } diff --git a/packages/lib/src/text-input/types.ts b/packages/lib/src/text-input/types.ts index fb36c8fde5..36cd8d3369 100644 --- a/packages/lib/src/text-input/types.ts +++ b/packages/lib/src/text-input/types.ts @@ -1,3 +1,4 @@ +import { CSSProperties, ReactNode } from "react"; import { Margin, SVG, Space } from "../common/utils"; type Action = { @@ -168,7 +169,7 @@ export type SuggestionsProps = { searchHasErrors: boolean; isSearching: boolean; suggestionOnClick: (suggestion: string) => void; - styles: React.CSSProperties; + styles: CSSProperties; }; /** @@ -191,8 +192,8 @@ export type SuggestionProps = { export type AutosuggestWrapperProps = { condition: boolean; - wrapper: (children: React.ReactNode) => JSX.Element; - children: React.ReactNode; + wrapper: (children: ReactNode) => JSX.Element; + children: ReactNode; }; export default Props; diff --git a/packages/lib/src/toast/types.ts b/packages/lib/src/toast/types.ts index d378786d60..ec3d7c0837 100644 --- a/packages/lib/src/toast/types.ts +++ b/packages/lib/src/toast/types.ts @@ -1,3 +1,4 @@ +import { ReactNode } from "react"; import { SVG } from "../common/utils"; type Action = { @@ -41,7 +42,7 @@ type ToastPropsType = { hideSemanticIcon?: boolean; }; -type ToastsQueuePropsType = { duration?: number; children: React.ReactNode }; +type ToastsQueuePropsType = { duration?: number; children: ReactNode }; export default ToastPropsType; export type { diff --git a/packages/lib/src/tooltip/types.tsx b/packages/lib/src/tooltip/types.tsx index 66eb434f95..541c7d71ef 100644 --- a/packages/lib/src/tooltip/types.tsx +++ b/packages/lib/src/tooltip/types.tsx @@ -17,7 +17,7 @@ type Props = { export type TooltipWrapperProps = { condition?: boolean; - children: React.ReactNode; + children: ReactNode; label?: string; }; diff --git a/packages/lib/src/typography/Typography.tsx b/packages/lib/src/typography/Typography.tsx index 0679f0d8b7..41a937682e 100644 --- a/packages/lib/src/typography/Typography.tsx +++ b/packages/lib/src/typography/Typography.tsx @@ -1,4 +1,4 @@ -import { useContext, useMemo } from "react"; +import { createContext, useContext, useMemo } from "react"; import styled from "styled-components"; import TypographyPropsTypes, { TypographyContextProps } from "./types"; @@ -20,7 +20,7 @@ const Typography = styled.span<TypographyPropsTypes>` overflow: ${({ textOverflow }) => (textOverflow !== "unset" ? "hidden" : "visible")}; `; -const TypographyContext = React.createContext<TypographyContextProps | null>(null); +const TypographyContext = createContext<TypographyContextProps | null>(null); export default function DxcTypography({ as, diff --git a/packages/lib/src/typography/types.ts b/packages/lib/src/typography/types.ts index ca542e3f63..5f5c16c3bb 100644 --- a/packages/lib/src/typography/types.ts +++ b/packages/lib/src/typography/types.ts @@ -1,6 +1,8 @@ +import { ReactNode } from "react"; + export type Props = { as?: "a" | "blockquote" | "cite" | "code" | "div" | "em" | "figcaption" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "pre" | "small" | "span" | "strong"; - children: React.ReactNode; + children: ReactNode; color?: string; display?: "inline" | "block"; fontFamily?: "Open Sans, sans-serif" | "Source Code Pro, monospace"; diff --git a/packages/lib/src/utils/FocusLock.tsx b/packages/lib/src/utils/FocusLock.tsx index 04f110c616..7a7ed2df4f 100644 --- a/packages/lib/src/utils/FocusLock.tsx +++ b/packages/lib/src/utils/FocusLock.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { KeyboardEvent, MutableRefObject, ReactNode, useCallback, useEffect, useRef, useState } from "react"; const not = { negTabIndex: ':not([tabindex^="-"])', @@ -59,7 +59,7 @@ const radixPortalContains = (activeElement: Node): boolean => { * @param ref: React.MutableRefObject<HTMLDivElement> * @returns */ -const useFocusableElements = (ref: React.MutableRefObject<HTMLDivElement | null>): HTMLElement[] | null => { +const useFocusableElements = (ref: MutableRefObject<HTMLDivElement | null>): HTMLElement[] | null => { const [focusableElements, setFocusableElements] = useState<HTMLElement[] | null>(null); useEffect(() => { @@ -87,7 +87,7 @@ const useFocusableElements = (ref: React.MutableRefObject<HTMLDivElement | null> * @param children: React.ReactNode * @returns */ -const FocusLock = ({ children }: { children: React.ReactNode }): JSX.Element => { +const FocusLock = ({ children }: { children: ReactNode }): JSX.Element => { const childrenContainerRef = useRef<HTMLDivElement | null>(null); const focusableElements = useFocusableElements(childrenContainerRef); const initialFocus = useRef(false); @@ -106,7 +106,7 @@ const FocusLock = ({ children }: { children: React.ReactNode }): JSX.Element => .some((element) => attemptFocus(element)); }; - const focusLock = (event: React.KeyboardEvent<HTMLDivElement>) => { + const focusLock = (event: KeyboardEvent<HTMLDivElement>) => { if (event.key === "Tab" && focusableElements?.length === 0) event.preventDefault(); }; From baaf87f291360b7afee77f0abe82549c00636ad4 Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Wed, 18 Dec 2024 15:17:15 +0100 Subject: [PATCH 25/41] Changed story name --- packages/lib/src/spinner/Spinner.stories.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/lib/src/spinner/Spinner.stories.tsx b/packages/lib/src/spinner/Spinner.stories.tsx index c52854fcfc..56c422086c 100644 --- a/packages/lib/src/spinner/Spinner.stories.tsx +++ b/packages/lib/src/spinner/Spinner.stories.tsx @@ -84,7 +84,7 @@ const Spinner = () => ( </> ); -const SpinnerOverlay = () => ( +const SpinnerWithOverlay = () => ( <ExampleContainer> <Title title="Mode overlay" theme="light" level={4} /> <DxcSpinner mode="overlay" value={25}></DxcSpinner> @@ -134,8 +134,8 @@ export const Chromatic: Story = { render: Spinner, }; -export const SpinnerWithOverlay: Story = { - render: SpinnerOverlay, +export const SpinnerOverlay: Story = { + render: SpinnerWithOverlay, }; export const SpinnerOverlayWith100: Story = { render: SpinnerOverlay100, From 2227183db9e65d3f673b4c6bae67df46b015e9f8 Mon Sep 17 00:00:00 2001 From: Mil4n0r <morenocarmonaenrique@gmail.com> Date: Thu, 19 Dec 2024 12:52:13 +0100 Subject: [PATCH 26/41] Added additional fixes for typings after context updates --- packages/lib/src/date-input/Calendar.tsx | 2 +- .../lib/src/date-input/DateInput.stories.tsx | 11 ++-- .../lib/src/dropdown/Dropdown.stories.tsx | 6 +- packages/lib/src/link/Link.tsx | 6 +- packages/lib/src/nav-tabs/NavTabs.tsx | 8 +-- packages/lib/src/paginator/Paginator.tsx | 15 ++--- packages/lib/src/paragraph/Paragraph.tsx | 6 +- .../lib/src/password-input/PasswordInput.tsx | 12 ++-- packages/lib/src/progress-bar/ProgressBar.tsx | 8 +-- packages/lib/src/quick-nav/QuickNav.tsx | 16 +++--- packages/lib/src/radio-group/Radio.tsx | 10 ++-- packages/lib/src/radio-group/RadioGroup.tsx | 17 +++--- .../src/resultset-table/ResultsetTable.tsx | 12 ++-- packages/lib/src/select/Listbox.tsx | 29 ++++++---- packages/lib/src/select/Select.stories.tsx | 8 +-- packages/lib/src/select/Select.tsx | 27 +++++---- packages/lib/src/select/selectUtils.ts | 18 +++--- packages/lib/src/sidenav/Sidenav.tsx | 13 ++--- packages/lib/src/sidenav/SidenavContext.tsx | 8 ++- packages/lib/src/slider/Slider.tsx | 16 +++--- packages/lib/src/spinner/Spinner.tsx | 8 +-- packages/lib/src/switch/Switch.tsx | 15 +++-- packages/lib/src/table/Table.tsx | 24 ++++---- packages/lib/src/tabs/Tabs.tsx | 8 +-- packages/lib/src/tabs/TabsLegacy.tsx | 9 ++- packages/lib/src/tag/Tag.tsx | 8 +-- packages/lib/src/text-input/Suggestions.tsx | 12 ++-- .../lib/src/text-input/TextInput.stories.tsx | 7 +-- packages/lib/src/text-input/TextInput.tsx | 37 ++++++------- packages/lib/src/textarea/Textarea.tsx | 27 +++++---- packages/lib/src/toast/Toast.tsx | 8 +-- packages/lib/src/toast/ToastsQueue.tsx | 4 +- packages/lib/src/toggle-group/ToggleGroup.tsx | 8 +-- packages/lib/src/tooltip/Tooltip.tsx | 3 +- packages/lib/src/tooltip/TooltipContext.tsx | 3 + packages/lib/src/typography/Typography.tsx | 55 +++---------------- .../lib/src/typography/TypographyContext.tsx | 18 ++++++ packages/lib/src/typography/types.ts | 22 +++++++- packages/lib/src/utils/useTheme.tsx | 10 ---- .../lib/src/utils/useTranslatedLabels.tsx | 10 ---- packages/lib/src/utils/useWidth.tsx | 2 +- packages/lib/src/wizard/Wizard.tsx | 6 +- 42 files changed, 265 insertions(+), 287 deletions(-) create mode 100644 packages/lib/src/tooltip/TooltipContext.tsx create mode 100644 packages/lib/src/typography/TypographyContext.tsx delete mode 100644 packages/lib/src/utils/useTheme.tsx delete mode 100644 packages/lib/src/utils/useTranslatedLabels.tsx diff --git a/packages/lib/src/date-input/Calendar.tsx b/packages/lib/src/date-input/Calendar.tsx index 1506fb5e1e..ae2aacdc2d 100644 --- a/packages/lib/src/date-input/Calendar.tsx +++ b/packages/lib/src/date-input/Calendar.tsx @@ -1,5 +1,5 @@ import { Dayjs } from "dayjs"; -import { useState, useMemo, useEffect, useId, memo, KeyboardEvent, FocusEvent, useContext } from "react"; +import { useContext, useState, useMemo, useEffect, useId, memo, KeyboardEvent, FocusEvent } from "react"; import styled from "styled-components"; import { CalendarPropsType, DateType } from "./types"; import { HalstackLanguageContext } from "../HalstackContext"; diff --git a/packages/lib/src/date-input/DateInput.stories.tsx b/packages/lib/src/date-input/DateInput.stories.tsx index 2e5afec065..fd5085979e 100644 --- a/packages/lib/src/date-input/DateInput.stories.tsx +++ b/packages/lib/src/date-input/DateInput.stories.tsx @@ -1,3 +1,4 @@ +import { useContext } from "react"; import { fireEvent, screen, userEvent, within } from "@storybook/test"; import dayjs from "dayjs"; import { ThemeProvider } from "styled-components"; @@ -7,7 +8,7 @@ import preview from "../../.storybook/preview"; import { disabledRules } from "../../test/accessibility/rules/specific/date-input/disabledRules"; import DxcContainer from "../container/Container"; import { HalstackProvider } from "../HalstackContext"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import Calendar from "./Calendar"; import DxcDateInput from "./DateInput"; import DxcDatePicker from "./DatePicker"; @@ -168,7 +169,7 @@ const YearPickerOpinionatedTheme = () => ( ); const DatePickerButtonStates = () => { - const colorsTheme: any = useTheme(); + const colorsTheme: any = useContext(HalstackContext); return ( <> <ExampleContainer> @@ -213,7 +214,7 @@ const DatePickerButtonStates = () => { }; const YearPickerButtonStates = () => { - const colorsTheme: any = useTheme(); + const colorsTheme: any = useContext(HalstackContext); return ( <> <ThemeProvider theme={colorsTheme}> @@ -247,7 +248,7 @@ const YearPickerButtonStates = () => { }; const DatePickerToday = () => { - const colorsTheme: any = useTheme(); + const colorsTheme: any = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme}> <ExampleContainer> @@ -273,7 +274,7 @@ const DatePickerToday = () => { }; const Tooltip = () => { - const colorsTheme: any = useTheme(); + const colorsTheme: any = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme}> <Title title="Default tooltip" theme="light" level={2} /> diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index dc7bd65aaf..bd7a581f40 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -1,10 +1,10 @@ +import { useContext } from "react"; import { userEvent, within } from "@storybook/test"; import { ThemeProvider } from "styled-components"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; -import DxcFlex from "../flex/Flex"; import { HalstackProvider } from "../HalstackContext"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import DxcDropdown from "./Dropdown"; import DropdownMenu from "./DropdownMenu"; import { Option } from "./types"; @@ -240,7 +240,7 @@ const Dropdown = () => ( ); const DropdownListStates = () => { - const colorsTheme: any = useTheme(); + const colorsTheme: any = useContext(HalstackContext); return ( <> diff --git a/packages/lib/src/link/Link.tsx b/packages/lib/src/link/Link.tsx index 2310df5f26..c67677ec62 100644 --- a/packages/lib/src/link/Link.tsx +++ b/packages/lib/src/link/Link.tsx @@ -1,8 +1,8 @@ -import { forwardRef, Ref } from "react"; +import { forwardRef, Ref, useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import { LinkProps } from "./types"; import CoreTokens from "../common/coreTokens"; @@ -101,7 +101,7 @@ const DxcLink = forwardRef( }: LinkProps, ref: Ref<HTMLAnchorElement> ): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.link}> diff --git a/packages/lib/src/nav-tabs/NavTabs.tsx b/packages/lib/src/nav-tabs/NavTabs.tsx index cad7999623..d37a57f620 100644 --- a/packages/lib/src/nav-tabs/NavTabs.tsx +++ b/packages/lib/src/nav-tabs/NavTabs.tsx @@ -1,6 +1,6 @@ -import { Children, KeyboardEvent, ReactElement, ReactNode, useEffect, useMemo, useRef, useState } from "react"; +import { Children, KeyboardEvent, ReactElement, ReactNode, useContext, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import NavTabsPropsType from "./types"; import DxcTab from "./Tab"; import NavTabsContext from "./NavTabsContext"; @@ -49,7 +49,7 @@ const DxcNavTabs = ({ iconPosition = "top", tabIndex = 0, children }: NavTabsPro const [innerFocusIndex, setInnerFocusIndex] = useState<number | null>(null); const [underlineWidth, setUnderlineWidth] = useState<number | null>(null); const refNavTabList = useRef<HTMLDivElement | null>(null); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const childArray = Children.toArray(children).filter( (child) => typeof child === "object" && "props" in child @@ -88,7 +88,7 @@ const DxcNavTabs = ({ iconPosition = "top", tabIndex = 0, children }: NavTabsPro }; return ( - <ThemeProvider theme={colorsTheme?.navTabs}> + <ThemeProvider theme={colorsTheme.navTabs}> <NavTabsContainer onKeyDown={handleOnKeyDown} ref={refNavTabList} role="tablist" aria-label="Navigation tabs"> <NavTabsContext.Provider value={contextValue}>{children}</NavTabsContext.Provider> <Underline underlineWidth={underlineWidth ?? 0} /> diff --git a/packages/lib/src/paginator/Paginator.tsx b/packages/lib/src/paginator/Paginator.tsx index 951c47af14..0ffca5a4ba 100644 --- a/packages/lib/src/paginator/Paginator.tsx +++ b/packages/lib/src/paginator/Paginator.tsx @@ -1,9 +1,10 @@ +import { useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcButton from "../button/Button"; import DxcSelect from "../select/Select"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext from "../HalstackContext"; import PaginatorPropsType from "./types"; +import { HalstackLanguageContext } from "../HalstackContext"; const DxcPaginator = ({ currentPage = 1, @@ -24,8 +25,8 @@ const DxcPaginator = ({ const maxItemsPerPage = minItemsPerPage - 1 + itemsPerPage > totalItems ? totalItems : minItemsPerPage - 1 + itemsPerPage; - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); return ( <ThemeProvider theme={colorsTheme?.paginator}> @@ -51,7 +52,7 @@ const DxcPaginator = ({ </ItemsPageContainer> )} <TotalItemsContainer> - {translatedLabels?.paginator?.minToMaxOfText?.(minItemsPerPage, maxItemsPerPage, totalItems)} + {translatedLabels.paginator.minToMaxOfText(minItemsPerPage, maxItemsPerPage, totalItems)} </TotalItemsContainer> {onPageChange && ( <DxcButton @@ -79,7 +80,7 @@ const DxcPaginator = ({ )} {showGoToPage ? ( <PageToSelectContainer> - <GoToLabel>{translatedLabels?.paginator?.goToPageText} </GoToLabel> + <GoToLabel>{translatedLabels.paginator.goToPageText} </GoToLabel> <SelectContainer> <DxcSelect options={Array.from(Array(totalPages), (e, num) => ({ @@ -96,7 +97,7 @@ const DxcPaginator = ({ </SelectContainer> </PageToSelectContainer> ) : ( - <span>{translatedLabels?.paginator?.pageOfText?.(currentPageInternal, totalPages)}</span> + <span>{translatedLabels.paginator.pageOfText(currentPageInternal, totalPages)}</span> )} {onPageChange && ( <DxcButton diff --git a/packages/lib/src/paragraph/Paragraph.tsx b/packages/lib/src/paragraph/Paragraph.tsx index e512dca324..b031ad46cf 100644 --- a/packages/lib/src/paragraph/Paragraph.tsx +++ b/packages/lib/src/paragraph/Paragraph.tsx @@ -1,6 +1,6 @@ -import { ReactNode } from "react"; +import { ReactNode, useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; const Paragraph = styled.p` display: ${(props) => props.theme.display}; @@ -19,7 +19,7 @@ const Paragraph = styled.p` `; export default function DxcParagraph({ children }: { children: ReactNode }) { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.paragraph}> diff --git a/packages/lib/src/password-input/PasswordInput.tsx b/packages/lib/src/password-input/PasswordInput.tsx index 7d7c33ad37..3908e347f2 100644 --- a/packages/lib/src/password-input/PasswordInput.tsx +++ b/packages/lib/src/password-input/PasswordInput.tsx @@ -1,8 +1,8 @@ -import { forwardRef, useEffect, useRef, useState } from "react"; +import { forwardRef, useContext, useEffect, useRef, useState } from "react"; import styled from "styled-components"; import DxcTextInput from "../text-input/TextInput"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; import PasswordInputPropsType, { RefType } from "./types"; +import { HalstackLanguageContext } from "../HalstackContext"; const setInputType = (type: string, element: HTMLDivElement | null) => { element?.getElementsByTagName("input")[0]?.setAttribute("type", type); @@ -37,18 +37,18 @@ const DxcPasswordInput = forwardRef<RefType, PasswordInputPropsType>( ) => { const [isPasswordVisible, setIsPasswordVisible] = useState(false); const inputRef = useRef<HTMLDivElement | null>(null); - const { passwordInput } = useTranslatedLabels(); + const { passwordInput } = useContext(HalstackLanguageContext); useEffect(() => { (() => { if (isPasswordVisible) { setInputType("text", inputRef.current); - if (passwordInput?.inputHidePasswordTitle) { + if (passwordInput.inputHidePasswordTitle) { setAriaAttributes("true", passwordInput.inputHidePasswordTitle, inputRef.current); } } else { setInputType("password", inputRef.current); - if (passwordInput?.inputShowPasswordTitle) { + if (passwordInput.inputShowPasswordTitle) { setAriaAttributes("false", passwordInput.inputShowPasswordTitle, inputRef.current); } } @@ -67,7 +67,7 @@ const DxcPasswordInput = forwardRef<RefType, PasswordInputPropsType>( setIsPasswordVisible((isPasswordCurrentlyVisible) => !isPasswordCurrentlyVisible); }, icon: isPasswordVisible ? "Visibility_Off" : "Visibility", - title: isPasswordVisible ? passwordInput?.inputHidePasswordTitle : passwordInput?.inputShowPasswordTitle, + title: isPasswordVisible ? passwordInput.inputHidePasswordTitle : passwordInput.inputShowPasswordTitle, }} error={error} clearable={clearable} diff --git a/packages/lib/src/progress-bar/ProgressBar.tsx b/packages/lib/src/progress-bar/ProgressBar.tsx index 49f7d84441..5b0bb23f25 100644 --- a/packages/lib/src/progress-bar/ProgressBar.tsx +++ b/packages/lib/src/progress-bar/ProgressBar.tsx @@ -1,7 +1,7 @@ -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import ProgressBarPropsType from "./types"; const DxcProgressBar = ({ @@ -12,7 +12,7 @@ const DxcProgressBar = ({ showValue = false, margin, }: ProgressBarPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const [valueProgressBar, setValueProgressBar] = useState(0); useEffect(() => { @@ -22,7 +22,7 @@ const DxcProgressBar = ({ }, [value]); return ( - <ThemeProvider theme={colorsTheme?.progressBar}> + <ThemeProvider theme={colorsTheme.progressBar}> <BackgroundProgressBar overlay={overlay}> <ProgressBarContainer overlay={overlay} margin={margin}> <InfoProgressBar> diff --git a/packages/lib/src/quick-nav/QuickNav.tsx b/packages/lib/src/quick-nav/QuickNav.tsx index 794c5b189c..0103d93c03 100644 --- a/packages/lib/src/quick-nav/QuickNav.tsx +++ b/packages/lib/src/quick-nav/QuickNav.tsx @@ -1,28 +1,28 @@ +import { useContext } from "react"; import slugify from "slugify"; import styled, { ThemeProvider } from "styled-components"; import DxcFlex from "../flex/Flex"; import DxcHeading from "../heading/Heading"; import DxcInset from "../inset/Inset"; import DxcTypography from "../typography/Typography"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import QuickNavTypes from "./types"; const DxcQuickNav = ({ title, links }: QuickNavTypes): JSX.Element => { - const translatedLabels = useTranslatedLabels(); - const colorsTheme = useTheme(); + const translatedLabels = useContext(HalstackLanguageContext); + const colorsTheme = useContext(HalstackContext); return ( - <ThemeProvider theme={colorsTheme?.quickNav}> + <ThemeProvider theme={colorsTheme.quickNav}> <QuickNavContainer> <DxcFlex direction="column" gap="0.5rem"> - <DxcHeading level={4} text={(title || translatedLabels?.quickNav?.contentTitle) ?? ""} /> + <DxcHeading level={4} text={(title || translatedLabels.quickNav.contentTitle) ?? ""} /> <ListColumn> {links.map((link) => ( <li key={link.label}> <DxcInset space="0.25rem"> <DxcTypography> - <Link href={`#${slugify(link?.label, { lower: true })}`}>{link?.label}</Link> + <Link href={`#${slugify(link.label, { lower: true })}`}>{link.label}</Link> <ListSecondColumn> {link.links?.map((sublink) => ( <li key={sublink.label}> @@ -33,7 +33,7 @@ const DxcQuickNav = ({ title, links }: QuickNavTypes): JSX.Element => { lower: true, })}`} > - {sublink?.label} + {sublink.label} </Link> </DxcTypography> </DxcInset> diff --git a/packages/lib/src/radio-group/Radio.tsx b/packages/lib/src/radio-group/Radio.tsx index 0913d43426..3b8580601e 100644 --- a/packages/lib/src/radio-group/Radio.tsx +++ b/packages/lib/src/radio-group/Radio.tsx @@ -1,8 +1,8 @@ -import { memo, useEffect, useId, useRef, useState } from "react"; +import { memo, useContext, useEffect, useId, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme } from "../common/variables"; import DxcFlex from "../flex/Flex"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import { RadioProps } from "./types"; const DxcRadio = ({ @@ -17,11 +17,11 @@ const DxcRadio = ({ }: RadioProps): JSX.Element => { const radioLabelId = `radio-${useId()}`; const ref = useRef<HTMLSpanElement>(null); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const handleOnClick = () => { onClick(); - document.activeElement !== ref?.current && ref?.current?.focus(); + document.activeElement !== ref.current && ref.current?.focus(); }; const [firstUpdate, setFirstUpdate] = useState(true); @@ -31,7 +31,7 @@ const DxcRadio = ({ setFirstUpdate(false); return; } - focused && ref?.current?.focus(); + focused && ref.current?.focus(); }, [focused]); return ( diff --git a/packages/lib/src/radio-group/RadioGroup.tsx b/packages/lib/src/radio-group/RadioGroup.tsx index 39b1ed9e49..db782bfb7f 100644 --- a/packages/lib/src/radio-group/RadioGroup.tsx +++ b/packages/lib/src/radio-group/RadioGroup.tsx @@ -1,7 +1,6 @@ -import { FocusEvent, forwardRef, KeyboardEvent, useCallback, useId, useMemo, useState } from "react"; +import { FocusEvent, forwardRef, KeyboardEvent, useCallback, useContext, useId, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import DxcRadio from "./Radio"; import RadioGroupPropsType, { RadioOption, RefType } from "./types"; @@ -38,8 +37,8 @@ const DxcRadioGroup = forwardRef<RefType, RadioGroupPropsType>( const [innerValue, setInnerValue] = useState(defaultValue); const [firstTimeFocus, setFirstTimeFocus] = useState(true); - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const innerOptions = useMemo( () => @@ -47,7 +46,7 @@ const DxcRadioGroup = forwardRef<RefType, RadioGroupPropsType>( ? [ ...options, { - label: optionalItemLabel ?? translatedLabels?.radioGroup?.optionalItemLabelDefault ?? "", + label: optionalItemLabel ?? translatedLabels.radioGroup.optionalItemLabelDefault ?? "", value: "", disabled, }, @@ -78,7 +77,7 @@ const DxcRadioGroup = forwardRef<RefType, RadioGroupPropsType>( if (!optional && !currentValue) { onBlur?.({ value: currentValue, - error: translatedLabels?.formFields?.requiredSelectionErrorMessage, + error: translatedLabels.formFields.requiredSelectionErrorMessage, }); } else { onBlur?.({ value: currentValue }); @@ -145,12 +144,12 @@ const DxcRadioGroup = forwardRef<RefType, RadioGroupPropsType>( }; return ( - <ThemeProvider theme={colorsTheme?.radioGroup}> + <ThemeProvider theme={colorsTheme.radioGroup}> <RadioGroupContainer ref={ref}> {label && ( <Label id={radioGroupLabelId} helperText={helperText} disabled={disabled}> {label} - {optional && <OptionalLabel>{` ${translatedLabels?.formFields?.optionalLabel}`}</OptionalLabel>} + {optional && <OptionalLabel>{` ${translatedLabels.formFields.optionalLabel}`}</OptionalLabel>} </Label> )} {helperText && <HelperText disabled={disabled}>{helperText}</HelperText>} diff --git a/packages/lib/src/resultset-table/ResultsetTable.tsx b/packages/lib/src/resultset-table/ResultsetTable.tsx index 9e77591d3a..e304e05865 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.tsx @@ -1,11 +1,11 @@ -import { ReactNode, useEffect, useMemo, useRef, useState } from "react"; +import { ReactNode, useContext, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import CoreTokens from "../common/coreTokens"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; import DxcPaginator from "../paginator/Paginator"; import DxcTable, { DxcActionsCell } from "../table/Table"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import icons from "./Icons"; import ResultsetTablePropsType, { Column, Row } from "./types"; @@ -16,8 +16,8 @@ const isDateType = (value: ReactNode | Date): boolean => value instanceof Date; const sortArray = (index: number, order: "ascending" | "descending", resultset: { id: string; cells: Row }[]) => resultset.slice().sort((element1, element2) => { - const sortValueA = normalizeSortValue(element1?.cells[index]?.sortValue || element1?.cells[index]?.displayValue); - const sortValueB = normalizeSortValue(element2?.cells[index]?.sortValue || element2?.cells[index]?.displayValue); + const sortValueA = normalizeSortValue(element1.cells[index]?.sortValue || element1.cells[index]?.displayValue); + const sortValueB = normalizeSortValue(element2.cells[index]?.sortValue || element2.cells[index]?.displayValue); let comparison = 0; if (sortValueA != null && sortValueB != null) { if (typeof sortValueA === "object" && !isDateType(sortValueA)) { @@ -61,7 +61,7 @@ const DxcResultsetTable = ({ tabIndex = 0, mode = "default", }: ResultsetTablePropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const [page, changePage] = useState(1); const [sortColumnIndex, changeSortColumnIndex] = useState(-1); const [sortOrder, changeSortOrder] = useState<"ascending" | "descending">("ascending"); @@ -119,7 +119,7 @@ const DxcResultsetTable = ({ }, [rows.length]); return ( - <ThemeProvider theme={colorsTheme?.table}> + <ThemeProvider theme={colorsTheme.table}> <DxcResultsetTableContainer margin={margin}> <DxcTable mode={mode}> <thead> diff --git a/packages/lib/src/select/Listbox.tsx b/packages/lib/src/select/Listbox.tsx index 991ce64e94..2b8962364f 100644 --- a/packages/lib/src/select/Listbox.tsx +++ b/packages/lib/src/select/Listbox.tsx @@ -1,7 +1,7 @@ -import { useLayoutEffect, useRef } from "react"; +import { useContext, useLayoutEffect, useRef } from "react"; import styled from "styled-components"; import DxcIcon from "../icon/Icon"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import { HalstackLanguageContext } from "../HalstackContext"; import ListOption from "./ListOption"; import { groupsHaveOptions } from "./selectUtils"; import { ListboxProps, ListOptionGroupType, ListOptionType } from "./types"; @@ -19,13 +19,18 @@ const Listbox = ({ handleOptionOnClick, styles, }: ListboxProps): JSX.Element => { - const translatedLabels = useTranslatedLabels(); + const translatedLabels = useContext(HalstackLanguageContext); const listboxRef = useRef<HTMLUListElement | null>(null); let globalIndex = optional && !multiple ? 0 : -1; + + const isListOptionGroupType = (option: ListOptionType | ListOptionGroupType): option is ListOptionGroupType => { + return "options" in option && Array.isArray(option.options); + }; + const mapOptionFunc = (option: ListOptionType | ListOptionGroupType, mapIndex: number) => { const groupId = `${id}-group-${mapIndex}`; - if ("options" in option && option.options) { + if (isListOptionGroupType(option)) { return ( option.options.length > 0 && ( <li key={groupId}> @@ -59,17 +64,17 @@ const Listbox = ({ globalIndex++; return ( <ListOption - key={`${id}-option-${(option as ListOptionType).value}`} + key={`${id}-option-${option.value}`} id={`${id}-option-${globalIndex}`} - option={option as ListOptionType} + option={option} onClick={handleOptionOnClick} multiple={multiple} visualFocused={visualFocusIndex === globalIndex} isLastOption={lastOptionIndex === globalIndex} isSelected={ multiple - ? currentValue.includes((option as ListOptionType).value) - : currentValue === (option as ListOptionType).value + ? currentValue.includes(option.value) + : currentValue === option.value } /> ); @@ -81,20 +86,20 @@ const Listbox = ({ const listEl = listboxRef?.current; const selectedListOptionEl = listEl?.querySelector("[aria-selected='true']") as HTMLUListElement; listEl?.scrollTo?.({ - top: (selectedListOptionEl?.offsetTop ?? 0) - (listEl?.clientHeight ?? 0) / 2, + top: (selectedListOptionEl.offsetTop ?? 0) - (listEl.clientHeight ?? 0) / 2, }); } }, [currentValue, multiple]); useLayoutEffect(() => { - const visualFocusedOptionEl = listboxRef?.current?.querySelectorAll("[role='option']")[visualFocusIndex]; + const visualFocusedOptionEl = listboxRef.current?.querySelectorAll("[role='option']")[visualFocusIndex]; visualFocusedOptionEl?.scrollIntoView?.({ block: "nearest", inline: "start", }); }, [visualFocusIndex]); - const hasOptionGroups = options.some((option) => (option as ListOptionGroupType).options?.length > 0); + const hasOptionGroups = options.some((option) => "options" in option && option.options.length > 0); return ( <ListboxContainer @@ -116,7 +121,7 @@ const Listbox = ({ <NoMatchesFoundIcon> <DxcIcon icon="search_off" /> </NoMatchesFoundIcon> - {translatedLabels?.select?.noMatchesErrorMessage} + {translatedLabels.select.noMatchesErrorMessage} </OptionsSystemMessage> ) : ( optional && diff --git a/packages/lib/src/select/Select.stories.tsx b/packages/lib/src/select/Select.stories.tsx index 1bd23f4369..354c6348a0 100644 --- a/packages/lib/src/select/Select.stories.tsx +++ b/packages/lib/src/select/Select.stories.tsx @@ -1,3 +1,4 @@ +import { useContext } from "react"; import { userEvent, within } from "@storybook/test"; import { ThemeProvider } from "styled-components"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; @@ -5,8 +6,7 @@ import Title from "../../.storybook/components/Title"; import preview from "../../.storybook/preview"; import { disabledRules } from "../../test/accessibility/rules/specific/select/disabledRules"; import DxcFlex from "../flex/Flex"; -import { HalstackProvider } from "../HalstackContext"; -import useTheme from "../utils/useTheme"; +import HalstackContext, { HalstackProvider } from "../HalstackContext"; import Listbox from "./Listbox"; import DxcSelect from "./Select"; import { Meta, StoryObj } from "@storybook/react"; @@ -392,7 +392,7 @@ const Opinionated = () => ( ); const SelectListbox = () => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.select}> @@ -643,7 +643,7 @@ const TooltipValue = () => ( ); const TooltipOption = () => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.select}> diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index 63f0354433..320c181738 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -1,12 +1,11 @@ import * as Popover from "@radix-ui/react-popover"; -import { ChangeEvent, FocusEvent, forwardRef, KeyboardEvent, MouseEvent, useCallback, useId, useMemo, useRef, useState } from "react"; +import { ChangeEvent, FocusEvent, forwardRef, KeyboardEvent, MouseEvent, useCallback, useContext, useId, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import { getMargin } from "../common/utils"; import DxcIcon from "../icon/Icon"; import { Tooltip, TooltipWrapper } from "../tooltip/Tooltip"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import useWidth from "../utils/useWidth"; import Listbox from "./Listbox"; import { @@ -57,8 +56,8 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( const selectSearchInputRef = useRef<HTMLInputElement | null>(null); const width = useWidth(selectRef.current); - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const optionalItem = { label: placeholder, value: "" }; const filteredOptions = useMemo(() => filterOptionsBySearchValue(options, searchValue), [options, searchValue]); @@ -99,7 +98,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( onChange?.({ value: newValue as string & string[], ...(notOptionalCheck(newValue, multiple, optional) && { - error: translatedLabels?.formFields?.requiredValueErrorMessage, + error: translatedLabels.formFields.requiredValueErrorMessage, }), }); } @@ -129,7 +128,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( if (notOptionalCheck(currentValue, multiple, optional)) { onBlur?.({ value: currentValue as string & string[], - error: translatedLabels?.formFields?.requiredValueErrorMessage, + error: translatedLabels.formFields.requiredValueErrorMessage, }); } else { onBlur?.({ value: currentValue as string & string[] }); @@ -245,7 +244,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( if (!optional) { onChange?.({ value: [] as string[] as string & string[], - error: translatedLabels?.formFields?.requiredValueErrorMessage, + error: translatedLabels.formFields.requiredValueErrorMessage, }); } else { onChange?.({ value: [] as string[] as string & string[] }); @@ -274,7 +273,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( }; return ( - <ThemeProvider theme={colorsTheme?.select}> + <ThemeProvider theme={colorsTheme.select}> <SelectContainer margin={margin} size={size} ref={ref}> {label && ( <Label @@ -285,7 +284,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( }} helperText={helperText} > - {label} {optional && <OptionalLabel>{translatedLabels?.formFields?.optionalLabel}</OptionalLabel>} + {label} {optional && <OptionalLabel>{translatedLabels.formFields.optionalLabel}</OptionalLabel>} </Label> )} {helperText && <HelperText disabled={disabled}>{helperText}</HelperText>} @@ -315,7 +314,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( {multiple && Array.isArray(selectedOption) && selectedOption.length > 0 && ( <SelectionIndicator> <SelectionNumber disabled={disabled}>{selectedOption.length}</SelectionNumber> - <Tooltip label={translatedLabels?.select?.actionClearSelectionTitle}> + <Tooltip label={translatedLabels.select.actionClearSelectionTitle}> <ClearOptionsAction disabled={disabled} onMouseDown={(event) => { @@ -324,7 +323,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( }} onClick={handleClearOptionsActionOnClick} tabIndex={-1} - aria-label={translatedLabels?.select?.actionClearSelectionTitle} + aria-label={translatedLabels.select.actionClearSelectionTitle} > <DxcIcon icon="clear" /> </ClearOptionsAction> @@ -378,7 +377,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( </ErrorIcon> )} {searchable && searchValue.length > 0 && ( - <Tooltip label={translatedLabels?.select?.actionClearSelectionTitle}> + <Tooltip label={translatedLabels.select.actionClearSelectionTitle}> <ClearSearchAction onMouseDown={(event) => { // Avoid input to lose focus @@ -386,7 +385,7 @@ const DxcSelect = forwardRef<RefType, SelectPropsType>( }} onClick={handleClearSearchActionOnClick} tabIndex={-1} - aria-label={translatedLabels?.select?.actionClearSearchTitle} + aria-label={translatedLabels.select.actionClearSearchTitle} > <DxcIcon icon="clear" /> </ClearSearchAction> diff --git a/packages/lib/src/select/selectUtils.ts b/packages/lib/src/select/selectUtils.ts index 06821f7b95..0b341504f7 100644 --- a/packages/lib/src/select/selectUtils.ts +++ b/packages/lib/src/select/selectUtils.ts @@ -22,13 +22,13 @@ const isArrayOfOptionGroups = (options: ListOptionType[] | ListOptionGroupType[] * Checks if the groups have options. */ const groupsHaveOptions = (options: ListOptionType[] | ListOptionGroupType[]) => - isArrayOfOptionGroups(options) ? options.some((groupOption) => groupOption.options?.length > 0) : true; + isArrayOfOptionGroups(options) ? options.some((groupOption) => groupOption.options.length > 0) : true; /** * Checks if the listbox can be opened. */ const canOpenListbox = (options: ListOptionType[] | ListOptionGroupType[], disabled: boolean) => - !disabled && options?.length > 0 && groupsHaveOptions(options); + !disabled && options.length > 0 && groupsHaveOptions(options); /** * Filters the options by the search value. @@ -37,7 +37,7 @@ const filterOptionsBySearchValue = ( options: ListOptionType[] | ListOptionGroupType[], searchValue: string ): ListOptionType[] | ListOptionGroupType[] => { - if (options?.length > 0) { + if (options.length > 0) { if (isArrayOfOptionGroups(options)) return options.map((optionGroup) => { const group = { @@ -65,15 +65,15 @@ const getLastOptionIndex = ( multiple: boolean ) => { let last = 0; - const reducer = (acc: number, current: ListOptionGroupType) => acc + (current?.options?.length ?? 0); + const reducer = (acc: number, current: ListOptionGroupType) => acc + (current.options.length ?? 0); - if (searchable && filteredOptions?.length > 0) { + if (searchable && filteredOptions.length > 0) { if (isArrayOfOptionGroups(filteredOptions)) { last = filteredOptions.reduce(reducer, 0) - 1; } else { last = filteredOptions.length - 1; } - } else if (options?.length > 0) { + } else if (options.length > 0) { if (isArrayOfOptionGroups(options)) { last = options.reduce(reducer, 0) - 1; } else { @@ -98,7 +98,7 @@ const getSelectedOption = ( let singleSelectionIndex: number | null = null; if (multiple) { - if (options?.length > 0) { + if (options.length > 0) { options.forEach((option: ListOptionType | ListOptionGroupType) => { if (isOptionGroup(option)) { option.options.forEach((singleOption) => { @@ -114,7 +114,7 @@ const getSelectedOption = ( } else if (optional && value === "") { selectedOption = optionalItem; singleSelectionIndex = 0; - } else if (options?.length > 0) { + } else if (options.length > 0) { let groupIndex = 0; options.some((option: ListOptionType | ListOptionGroupType, index: number) => { if (isOptionGroup(option)) { @@ -150,7 +150,7 @@ const getSelectedOptionLabel = (placeholder: string, selectedOption: ListOptionT ? selectedOption.length === 0 ? placeholder : selectedOption.map((option) => option.label).join(", ") - : (selectedOption?.label ?? placeholder); + : (selectedOption.label ?? placeholder); export { isOptionGroup, diff --git a/packages/lib/src/sidenav/Sidenav.tsx b/packages/lib/src/sidenav/Sidenav.tsx index 3f30ae6708..87556a7047 100644 --- a/packages/lib/src/sidenav/Sidenav.tsx +++ b/packages/lib/src/sidenav/Sidenav.tsx @@ -1,12 +1,12 @@ -import { createContext, Dispatch, forwardRef, MouseEvent, SetStateAction, useContext, useEffect, useState } from "react"; +import { forwardRef, MouseEvent, useContext, useEffect, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcBleed from "../bleed/Bleed"; import CoreTokens from "../common/coreTokens"; import { responsiveSizes } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; -import useTheme from "../utils/useTheme"; -import { useResponsiveSidenavVisibility } from "./SidenavContext"; +import HalstackContext from "../HalstackContext"; +import { GroupContext, GroupContextProvider, useResponsiveSidenavVisibility } from "./SidenavContext"; import SidenavPropsType, { SidenavGroupPropsType, SidenavLinkPropsType, @@ -15,7 +15,7 @@ import SidenavPropsType, { } from "./types"; const DxcSidenav = ({ title, children }: SidenavPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.sidenav}> @@ -44,13 +44,12 @@ const Section = ({ children }: SidenavSectionPropsType): JSX.Element => ( </> ); -const GroupContext = createContext<Dispatch<SetStateAction<boolean>> | null>(null); const Group = ({ title, collapsable = false, icon, children }: SidenavGroupPropsType): JSX.Element => { const [collapsed, setCollapsed] = useState(false); const [isSelected, changeIsSelected] = useState(false); return ( - <GroupContext.Provider value={changeIsSelected}> + <GroupContextProvider value={changeIsSelected}> <SidenavGroup> {collapsable && title ? ( <SidenavGroupTitleButton @@ -74,7 +73,7 @@ const Group = ({ title, collapsable = false, icon, children }: SidenavGroupProps )} {!collapsed && children} </SidenavGroup> - </GroupContext.Provider> + </GroupContextProvider> ); }; diff --git a/packages/lib/src/sidenav/SidenavContext.tsx b/packages/lib/src/sidenav/SidenavContext.tsx index 0a5e9426b1..fc8f9d2b3c 100644 --- a/packages/lib/src/sidenav/SidenavContext.tsx +++ b/packages/lib/src/sidenav/SidenavContext.tsx @@ -1,11 +1,15 @@ -import { createContext, useContext } from "react"; +import { createContext, Dispatch, SetStateAction, useContext } from "react"; -type SidenavContextType = (isSidenavVisible: boolean) => void; +type SidenavContextType = (_isSidenavVisible: boolean) => void; const SidenavContext = createContext<SidenavContextType | null>(null); +export const GroupContext = createContext<Dispatch<SetStateAction<boolean>> | null>(null); + export const SidenavContextProvider = SidenavContext.Provider; +export const GroupContextProvider = GroupContext.Provider; + export const useResponsiveSidenavVisibility = () => { const changeResponsiveSidenavVisibility = useContext(SidenavContext); return changeResponsiveSidenavVisibility; diff --git a/packages/lib/src/slider/Slider.tsx b/packages/lib/src/slider/Slider.tsx index a02678fdab..58abe0bf63 100644 --- a/packages/lib/src/slider/Slider.tsx +++ b/packages/lib/src/slider/Slider.tsx @@ -1,9 +1,9 @@ -import { ChangeEvent, forwardRef, MouseEvent, useId, useMemo, useState } from "react"; +import { ChangeEvent, forwardRef, MouseEvent, useContext, useId, useMemo, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import DxcTextInput from "../text-input/TextInput"; import { spaces } from "../common/variables"; import { getMargin } from "../common/utils"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import SliderPropsType, { RefType } from "./types"; const DxcSlider = forwardRef<RefType, SliderPropsType>( @@ -32,8 +32,8 @@ const DxcSlider = forwardRef<RefType, SliderPropsType>( const labelId = `label-${useId()}`; const [innerValue, setInnerValue] = useState(defaultValue ?? 0); const [dragging, setDragging] = useState(false); - const colorsTheme = useTheme(); - const isFirefox = navigator?.userAgent.indexOf("Firefox") !== -1; + const colorsTheme = useContext(HalstackContext); + const isFirefox = navigator.userAgent.indexOf("Firefox") !== -1; const minLabel = useMemo( () => (labelFormatCallback ? labelFormatCallback(minValue) : minValue), @@ -99,7 +99,7 @@ const DxcSlider = forwardRef<RefType, SliderPropsType>( }; return ( - <ThemeProvider theme={colorsTheme?.slider}> + <ThemeProvider theme={colorsTheme.slider}> <Container margin={margin} size={size} ref={ref}> <Label id={labelId} disabled={disabled}> {label} @@ -225,9 +225,9 @@ const SliderInput = styled.input<{ : `linear-gradient(${props.theme.trackLineColor}, ${props.theme.trackLineColor})`}; background-repeat: no-repeat; background-size: ${(props) => - props?.value != null && - props?.min != null && - props?.max != null && + props.value != null && + props.min != null && + props.max != null && `${((props.value - props.min) * 100) / (props.max - props.min)}% 100%`}; border-radius: 5px; cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")}; diff --git a/packages/lib/src/spinner/Spinner.tsx b/packages/lib/src/spinner/Spinner.tsx index 93ecc94dc8..9ebc459b89 100644 --- a/packages/lib/src/spinner/Spinner.tsx +++ b/packages/lib/src/spinner/Spinner.tsx @@ -1,16 +1,16 @@ -import { useId, useMemo } from "react"; +import { useContext, useId, useMemo } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import SpinnerPropsType from "./types"; const DxcSpinner = ({ label, value, showValue = false, mode = "large", margin }: SpinnerPropsType): JSX.Element => { const labelId = useId(); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const determinated = useMemo(() => value != null && value >= 0 && value <= 100, [value]); return ( - <ThemeProvider theme={colorsTheme?.spinner}> + <ThemeProvider theme={colorsTheme.spinner}> <DXCSpinner margin={margin} mode={mode}> <SpinnerContainer mode={mode}> {mode === "overlay" && <BackOverlay />} diff --git a/packages/lib/src/switch/Switch.tsx b/packages/lib/src/switch/Switch.tsx index a0b1b1fba8..ba5167efd4 100644 --- a/packages/lib/src/switch/Switch.tsx +++ b/packages/lib/src/switch/Switch.tsx @@ -1,9 +1,8 @@ -import { forwardRef, KeyboardEvent, useId, useRef, useState } from "react"; +import { forwardRef, KeyboardEvent, useContext, useId, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { AdvancedTheme, spaces } from "../common/variables"; import { getMargin } from "../common/utils"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import SwitchPropsType, { RefType } from "./types"; const DxcSwitch = forwardRef<RefType, SwitchPropsType>( @@ -28,8 +27,8 @@ const DxcSwitch = forwardRef<RefType, SwitchPropsType>( const labelId = `label-${switchId}`; const [innerChecked, setInnerChecked] = useState(defaultChecked ?? false); - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const refTrack = useRef<HTMLSpanElement | null>(null); const handleOnKeyDown = (event: KeyboardEvent<HTMLDivElement>) => { @@ -54,7 +53,7 @@ const DxcSwitch = forwardRef<RefType, SwitchPropsType>( }; return ( - <ThemeProvider theme={colorsTheme?.switch}> + <ThemeProvider theme={colorsTheme.switch}> <SwitchContainer margin={margin} size={size} @@ -65,7 +64,7 @@ const DxcSwitch = forwardRef<RefType, SwitchPropsType>( > {labelPosition === "before" && label && ( <LabelContainer id={labelId} labelPosition={labelPosition} disabled={disabled} label={label}> - {label} {optional && <>{translatedLabels?.formFields?.optionalLabel}</>} + {label} {optional && <>{translatedLabels.formFields.optionalLabel}</>} </LabelContainer> )} <ValueInput @@ -90,7 +89,7 @@ const DxcSwitch = forwardRef<RefType, SwitchPropsType>( </SwitchBase> {labelPosition === "after" && label && ( <LabelContainer id={labelId} labelPosition={labelPosition} disabled={disabled} label={label}> - {optional && <>{translatedLabels?.formFields?.optionalLabel}</>} {label} + {optional && <>{translatedLabels.formFields.optionalLabel}</>} {label} </LabelContainer> )} </SwitchContainer> diff --git a/packages/lib/src/table/Table.tsx b/packages/lib/src/table/Table.tsx index 314605e765..eaceae931d 100644 --- a/packages/lib/src/table/Table.tsx +++ b/packages/lib/src/table/Table.tsx @@ -1,22 +1,22 @@ +import { useContext } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces, AdvancedTheme } from "../common/variables"; import { getMargin } from "../common/utils"; import DxcDropdown from "../dropdown/Dropdown"; import DxcFlex from "../flex/Flex"; -import { DeepPartial, HalstackProvider } from "../HalstackContext"; -import useTheme from "../utils/useTheme"; +import HalstackContext, { DeepPartial, HalstackProvider } from "../HalstackContext"; import dropdownTheme from "./dropdownTheme"; import DxcActionIcon from "../action-icon/ActionIcon"; import TablePropsType, { ActionCellsPropsType } from "./types"; -const overwriteTheme = (theme: DeepPartial<AdvancedTheme>) => { +const overwriteTheme = (theme: AdvancedTheme): DeepPartial<AdvancedTheme> => { const newTheme = dropdownTheme; - newTheme.dropdown.buttonBackgroundColor = theme?.table?.actionBackgroundColor ?? ""; - newTheme.dropdown.hoverButtonBackgroundColor = theme?.table?.hoverActionBackgroundColor ?? ""; - newTheme.dropdown.activeButtonBackgroundColor = theme?.table?.activeActionBackgroundColor ?? ""; - newTheme.dropdown.buttonIconColor = theme?.table?.actionIconColor ?? ""; - newTheme.dropdown.disabledColor = theme?.table?.disabledActionIconColor ?? ""; - newTheme.dropdown.disabledButtonBackgroundColor = theme?.table?.disabledActionBackgroundColor ?? ""; + newTheme.dropdown.buttonBackgroundColor = theme.table.actionBackgroundColor; + newTheme.dropdown.hoverButtonBackgroundColor = theme.table.hoverActionBackgroundColor; + newTheme.dropdown.activeButtonBackgroundColor = theme.table.activeActionBackgroundColor; + newTheme.dropdown.buttonIconColor = theme.table.actionIconColor; + newTheme.dropdown.disabledColor = theme.table.disabledActionIconColor; + newTheme.dropdown.disabledButtonBackgroundColor = theme.table.disabledActionBackgroundColor; return newTheme; }; @@ -24,7 +24,7 @@ export const DxcActionsCell = ({ actions }: ActionCellsPropsType): JSX.Element = const actionIcons = actions.filter((action) => !action.options); const actionDropdown = actions.find((action) => action.options); const maxNumberOfActions = actionDropdown ? 2 : 3; - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( <DxcFlex gap="0.5rem" alignItems="center"> @@ -58,10 +58,10 @@ export const DxcActionsCell = ({ actions }: ActionCellsPropsType): JSX.Element = }; const DxcTable = ({ children, margin, mode = "default" }: TablePropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); return ( - <ThemeProvider theme={colorsTheme?.table}> + <ThemeProvider theme={colorsTheme.table}> <DxcTableContainer margin={margin}> <DxcTableContent mode={mode}>{children}</DxcTableContent> </DxcTableContainer> diff --git a/packages/lib/src/tabs/Tabs.tsx b/packages/lib/src/tabs/Tabs.tsx index 73a60fe232..21d7711b72 100644 --- a/packages/lib/src/tabs/Tabs.tsx +++ b/packages/lib/src/tabs/Tabs.tsx @@ -5,19 +5,19 @@ import { MutableRefObject, ReactElement, useCallback, + useContext, useEffect, useMemo, useRef, useState, } from "react"; import styled, { ThemeProvider } from "styled-components"; -import useTheme from "../utils/useTheme"; import TabsContext from "./TabsContext"; import DxcTab from "./Tab"; import TabsPropsType, { TabProps } from "./types"; import DxcTabsLegacy from "./TabsLegacy"; import { spaces } from "../common/variables"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import DxcIcon from "../icon/Icon"; const useResize = (refTabList: MutableRefObject<HTMLDivElement | null>) => { @@ -96,9 +96,9 @@ const DxcTabs = ({ const [scrollLeftEnabled, setScrollLeftEnabled] = useState(false); const [minHeightTabs, setMinHeightTabs] = useState(0); const refTabList = useRef<HTMLDivElement | null>(null); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const viewWidth = useResize(refTabList); - const translatedLabels = useTranslatedLabels(); + const translatedLabels = useContext(HalstackLanguageContext); const enabledIndicator = useMemo(() => viewWidth < totalTabsWidth, [viewWidth]); useEffect(() => { diff --git a/packages/lib/src/tabs/TabsLegacy.tsx b/packages/lib/src/tabs/TabsLegacy.tsx index 72bb57e8ba..9e66e1a419 100644 --- a/packages/lib/src/tabs/TabsLegacy.tsx +++ b/packages/lib/src/tabs/TabsLegacy.tsx @@ -1,9 +1,8 @@ -import { KeyboardEvent, MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { KeyboardEvent, MutableRefObject, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import Tab from "./TabLegacy"; import TabsPropsType from "./types"; @@ -35,7 +34,7 @@ const DxcTabs = ({ iconPosition = "top", tabIndex = 0, }: TabsPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const hasLabelAndIcon = tabs != null && tabs.filter((tab) => tab.label && tab.icon).length > 0; const firstFocus = tabs != null ? tabs.findIndex((tab) => !tab.isDisabled) : null; const [innerActiveTabIndex, setInnerActiveTabIndex] = useState( @@ -56,7 +55,7 @@ const DxcTabs = ({ const refTabs = useRef<HTMLButtonElement[]>([]); const refTabList = useRef<HTMLDivElement | null>(null); const viewWidth = useResize(refTabList); - const translatedLabels = useTranslatedLabels(); + const translatedLabels = useContext(HalstackLanguageContext); const enabledIndicator = useMemo(() => viewWidth < totalTabsWidth, [viewWidth]); useEffect(() => { diff --git a/packages/lib/src/tag/Tag.tsx b/packages/lib/src/tag/Tag.tsx index 2b62ef25bf..9615ef176f 100644 --- a/packages/lib/src/tag/Tag.tsx +++ b/packages/lib/src/tag/Tag.tsx @@ -1,8 +1,8 @@ -import { ReactNode, useState } from "react"; +import { ReactNode, useContext, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import DxcIcon from "../icon/Icon"; import TagPropsType from "./types"; import CoreTokens from "../common/coreTokens"; @@ -32,7 +32,7 @@ const DxcTag = ({ size = "fitContent", tabIndex = 0, }: TagPropsType): JSX.Element => { - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const [isHovered, changeIsHovered] = useState(false); const wrapperComponent = (children: ReactNode) => { @@ -50,7 +50,7 @@ const DxcTag = ({ }; return ( - <ThemeProvider theme={colorsTheme?.tag}> + <ThemeProvider theme={colorsTheme.tag}> <StyledDxcTag margin={margin} size={size} diff --git a/packages/lib/src/text-input/Suggestions.tsx b/packages/lib/src/text-input/Suggestions.tsx index 946023f0bd..f3fd79e064 100644 --- a/packages/lib/src/text-input/Suggestions.tsx +++ b/packages/lib/src/text-input/Suggestions.tsx @@ -1,6 +1,6 @@ -import { memo, useEffect, useRef } from "react"; +import { memo, useContext, useEffect, useRef } from "react"; import styled from "styled-components"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import { HalstackLanguageContext } from "../HalstackContext"; import Suggestion from "./Suggestion"; import { SuggestionsProps } from "./types"; import DxcIcon from "../icon/Icon"; @@ -16,7 +16,7 @@ const Suggestions = ({ suggestionOnClick, styles, }: SuggestionsProps): JSX.Element => { - const translatedLabels = useTranslatedLabels(); + const translatedLabels = useContext(HalstackLanguageContext); const listboxRef = useRef<HTMLUListElement | null>(null); useEffect(() => { @@ -55,9 +55,7 @@ const Suggestions = ({ /> ))} {isSearching && ( - <SuggestionsSystemMessage role="option"> - {translatedLabels?.textInput?.searchingMessage} - </SuggestionsSystemMessage> + <SuggestionsSystemMessage role="option">{translatedLabels.textInput.searchingMessage}</SuggestionsSystemMessage> )} {searchHasErrors && ( <span role="option"> @@ -65,7 +63,7 @@ const Suggestions = ({ <SuggestionsErrorIcon> <DxcIcon icon="filled_error" /> </SuggestionsErrorIcon> - {translatedLabels?.textInput?.fetchingDataErrorMessage} + {translatedLabels.textInput.fetchingDataErrorMessage} </SuggestionsError> </span> )} diff --git a/packages/lib/src/text-input/TextInput.stories.tsx b/packages/lib/src/text-input/TextInput.stories.tsx index 700d613e65..140b97dcd7 100644 --- a/packages/lib/src/text-input/TextInput.stories.tsx +++ b/packages/lib/src/text-input/TextInput.stories.tsx @@ -1,14 +1,13 @@ +import { useContext } from "react"; import { userEvent, within } from "@storybook/test"; import { ThemeProvider } from "styled-components"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; -import { HalstackProvider } from "../HalstackContext"; +import HalstackContext, { HalstackProvider } from "../HalstackContext"; import DxcFlex from "../flex/Flex"; -import useTheme from "../utils/useTheme"; import Suggestions from "./Suggestions"; import DxcTextInput from "./TextInput"; import { Meta, StoryObj } from "@storybook/react"; - export default { title: "Text Input", component: DxcTextInput, @@ -342,7 +341,7 @@ const TextInput = () => ( ); const AutosuggestListbox = () => { - const colorsTheme: any = useTheme(); + const colorsTheme: any = useContext(HalstackContext); return ( <ThemeProvider theme={colorsTheme.textInput}> diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx index b1c4329aa3..6a508131b0 100644 --- a/packages/lib/src/text-input/TextInput.tsx +++ b/packages/lib/src/text-input/TextInput.tsx @@ -20,8 +20,7 @@ import { spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import NumberInputContext from "../number-input/NumberInputContext"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; import useWidth from "../utils/useWidth"; import Suggestions from "./Suggestions"; import TextInputPropsType, { AutosuggestWrapperProps, RefType } from "./types"; @@ -59,7 +58,7 @@ const makeCancelable = (promise: Promise<string[]>) => { }; const hasSuggestions = (suggestions: TextInputPropsType["suggestions"]) => - typeof suggestions === "function" || (suggestions ? suggestions?.length > 0 : false); + typeof suggestions === "function" || (suggestions ? suggestions.length > 0 : false); const isRequired = (value: string, optional: boolean) => value === "" && !optional; @@ -118,8 +117,8 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( const actionRef = useRef<HTMLButtonElement | null>(null); const width = useWidth(inputContainerRef.current); - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const numberInputContext = useContext(NumberInputContext); // Define the wrapper function outside of the parent component const autosuggestWrapperFunction = (children: ReactNode) => ( @@ -166,9 +165,9 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( ); const getNumberErrorMessage = (checkedValue: number) => numberInputContext?.minNumber != null && checkedValue < numberInputContext?.minNumber - ? translatedLabels?.numberInput?.valueGreaterThanOrEqualToErrorMessage?.(numberInputContext.minNumber) + ? translatedLabels.numberInput.valueGreaterThanOrEqualToErrorMessage?.(numberInputContext.minNumber) : numberInputContext?.maxNumber != null && checkedValue > numberInputContext?.maxNumber - ? translatedLabels?.numberInput?.valueLessThanOrEqualToErrorMessage?.(numberInputContext.maxNumber) + ? translatedLabels.numberInput.valueLessThanOrEqualToErrorMessage?.(numberInputContext.maxNumber) : undefined; const openSuggestions = () => { @@ -193,15 +192,15 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( if (isRequired(formattedValue, optional)) { onChange?.({ value: formattedValue, - error: translatedLabels?.formFields?.requiredValueErrorMessage, + error: translatedLabels.formFields.requiredValueErrorMessage, }); } else if (isLengthIncorrect(formattedValue, minLength, maxLength)) { onChange?.({ value: formattedValue, - error: translatedLabels?.formFields?.lengthErrorMessage?.(minLength, maxLength), + error: translatedLabels.formFields.lengthErrorMessage?.(minLength, maxLength), }); } else if (patternMismatch(pattern, formattedValue)) { - onChange?.({ value: formattedValue, error: translatedLabels?.formFields?.formatRequestedErrorMessage }); + onChange?.({ value: formattedValue, error: translatedLabels.formFields.formatRequestedErrorMessage }); } else if ( numberInputContext?.typeNumber === "number" && isNumberIncorrect(Number(newValue), numberInputContext?.minNumber, numberInputContext?.maxNumber) @@ -290,14 +289,14 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( closeSuggestions(); if (isRequired(event.target.value, optional)) { - onBlur?.({ value: event.target.value, error: translatedLabels?.formFields?.requiredValueErrorMessage }); + onBlur?.({ value: event.target.value, error: translatedLabels.formFields.requiredValueErrorMessage }); } else if (isLengthIncorrect(event.target.value, minLength, maxLength)) { onBlur?.({ value: event.target.value, - error: translatedLabels?.formFields?.lengthErrorMessage?.(minLength, maxLength), + error: translatedLabels.formFields.lengthErrorMessage?.(minLength, maxLength), }); } else if (patternMismatch(pattern, event.target.value)) { - onBlur?.({ value: event.target.value, error: translatedLabels?.formFields?.formatRequestedErrorMessage }); + onBlur?.({ value: event.target.value, error: translatedLabels.formFields.formatRequestedErrorMessage }); } else if ( numberInputContext?.typeNumber === "number" && isNumberIncorrect(Number(event.target.value), numberInputContext?.minNumber, numberInputContext?.maxNumber) @@ -445,7 +444,7 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( cancelablePromise.cancel(); }; } - if (suggestions && suggestions?.length > 0) { + if (suggestions && suggestions.length > 0) { changeFilteredSuggestions( suggestions.filter((suggestion) => suggestion.toUpperCase().startsWith((value ?? innerValue).toUpperCase())) ); @@ -463,11 +462,11 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( }, [value, innerValue, suggestions, numberInputContext]); return ( - <ThemeProvider theme={colorsTheme?.textInput}> + <ThemeProvider theme={colorsTheme.textInput}> <TextInputContainer margin={margin} size={size} ref={ref}> {label && ( <Label htmlFor={inputId} disabled={disabled} hasHelperText={!!helperText}> - {label} {optional && <OptionalLabel>{translatedLabels?.formFields?.optionalLabel}</OptionalLabel>} + {label} {optional && <OptionalLabel>{translatedLabels.formFields.optionalLabel}</OptionalLabel>} </Label> )} {helperText && <HelperText disabled={disabled}>{helperText}</HelperText>} @@ -528,7 +527,7 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( onClick={handleClearActionOnClick} icon="close" tabIndex={tabIndex} - title={translatedLabels?.textInput?.clearFieldActionTitle ?? ""} + title={translatedLabels.textInput.clearFieldActionTitle ?? ""} /> )} {numberInputContext?.typeNumber === "number" && ( @@ -538,7 +537,7 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( icon="remove" tabIndex={tabIndex} ref={actionRef} - title={translatedLabels?.numberInput?.decrementValueTitle ?? ""} + title={translatedLabels.numberInput.decrementValueTitle ?? ""} disabled={disabled} /> <DxcActionIcon @@ -546,7 +545,7 @@ const DxcTextInput = forwardRef<RefType, TextInputPropsType>( icon="add" tabIndex={tabIndex} ref={actionRef} - title={translatedLabels?.numberInput?.incrementValueTitle ?? ""} + title={translatedLabels.numberInput.incrementValueTitle ?? ""} disabled={disabled} /> </> diff --git a/packages/lib/src/textarea/Textarea.tsx b/packages/lib/src/textarea/Textarea.tsx index ed631c58dd..850e0fb1ef 100644 --- a/packages/lib/src/textarea/Textarea.tsx +++ b/packages/lib/src/textarea/Textarea.tsx @@ -1,9 +1,8 @@ -import { ChangeEvent, FocusEvent, forwardRef, useEffect, useId, useRef, useState } from "react"; +import { ChangeEvent, FocusEvent, forwardRef, useContext, useEffect, useId, useRef, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { getMargin } from "../common/utils"; import { spaces } from "../common/variables"; -import useTheme from "../utils/useTheme"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import HalstackContext,{ HalstackLanguageContext } from "../HalstackContext"; import TextareaPropsType, { RefType } from "./types"; const patternMatch = (pattern: string, value: string) => new RegExp(pattern).test(value); @@ -38,8 +37,8 @@ const DxcTextarea = forwardRef<RefType, TextareaPropsType>( const [innerValue, setInnerValue] = useState(defaultValue); const textareaId = `textarea-${useId()}`; - const colorsTheme = useTheme(); - const translatedLabels = useTranslatedLabels(); + const colorsTheme = useContext(HalstackContext); + const translatedLabels = useContext(HalstackLanguageContext); const textareaRef = useRef<HTMLTextAreaElement | null>(null); const prevValueRef = useRef<string | null>(null); @@ -61,17 +60,17 @@ const DxcTextarea = forwardRef<RefType, TextareaPropsType>( if (isNotOptional(newValue)) { onChange?.({ value: newValue, - error: translatedLabels?.formFields?.requiredValueErrorMessage, + error: translatedLabels.formFields.requiredValueErrorMessage, }); } else if (isLengthIncorrect(newValue)) { onChange?.({ value: newValue, - error: translatedLabels?.formFields?.lengthErrorMessage?.(minLength, maxLength), + error: translatedLabels.formFields.lengthErrorMessage?.(minLength, maxLength), }); } else if (newValue && pattern && !patternMatch(pattern, newValue)) { onChange?.({ value: newValue, - error: translatedLabels?.formFields?.formatRequestedErrorMessage, + error: translatedLabels.formFields.formatRequestedErrorMessage, }); } else { onChange?.({ value: newValue }); @@ -93,17 +92,17 @@ const DxcTextarea = forwardRef<RefType, TextareaPropsType>( if (isNotOptional(event.target.value)) { onBlur?.({ value: event.target.value, - error: translatedLabels?.formFields?.requiredValueErrorMessage, + error: translatedLabels.formFields.requiredValueErrorMessage, }); } else if (isLengthIncorrect(event.target.value)) { onBlur?.({ value: event.target.value, - error: translatedLabels?.formFields?.lengthErrorMessage?.(minLength, maxLength), + error: translatedLabels.formFields.lengthErrorMessage?.(minLength, maxLength), }); } else if (event.target.value && pattern && !patternMatch(pattern, event.target.value)) { onBlur?.({ value: event.target.value, - error: translatedLabels?.formFields?.formatRequestedErrorMessage, + error: translatedLabels.formFields.formatRequestedErrorMessage, }); } else { onBlur?.({ value: event.target.value }); @@ -123,18 +122,18 @@ const DxcTextarea = forwardRef<RefType, TextareaPropsType>( const textareaLineHeight = parseInt(computedStyle.lineHeight || "0", 10); const textareaPaddingTopBottom = parseInt(computedStyle.paddingTop || "0", 10) * 2; textareaRef.current.style.height = `${textareaLineHeight * rows}px`; - const newHeight = (textareaRef?.current?.scrollHeight ?? 0) - textareaPaddingTopBottom; + const newHeight = (textareaRef.current.scrollHeight ?? 0) - textareaPaddingTopBottom; textareaRef.current.style.height = `${newHeight}px`; prevValueRef.current = value ?? innerValue; } }, [verticalGrow, value, innerValue, rows]); return ( - <ThemeProvider theme={colorsTheme?.textarea}> + <ThemeProvider theme={colorsTheme.textarea}> <TextareaContainer margin={margin} size={size} ref={ref}> {label && ( <Label htmlFor={textareaId} disabled={disabled} helperText={helperText}> - {label} {optional && <OptionalLabel>{translatedLabels?.formFields?.optionalLabel}</OptionalLabel>} + {label} {optional && <OptionalLabel>{translatedLabels.formFields.optionalLabel}</OptionalLabel>} </Label> )} {helperText && <HelperText disabled={disabled}>{helperText}</HelperText>} diff --git a/packages/lib/src/toast/Toast.tsx b/packages/lib/src/toast/Toast.tsx index 6df2d40fad..f1b27c24cd 100644 --- a/packages/lib/src/toast/Toast.tsx +++ b/packages/lib/src/toast/Toast.tsx @@ -1,4 +1,4 @@ -import { memo, useState } from "react"; +import { memo, useContext, useState } from "react"; import styled, { keyframes } from "styled-components"; import CoreTokens from "../common/coreTokens"; import DxcActionIcon from "../action-icon/ActionIcon"; @@ -9,7 +9,7 @@ import DxcSpinner from "../spinner/Spinner"; import { HalstackProvider } from "../HalstackContext"; import ToastPropsType from "./types"; import useTimeout from "../utils/useTimeout"; -import useTranslatedLabels from "../utils/useTranslatedLabels"; +import { HalstackLanguageContext } from "../HalstackContext"; import { responsiveSizes } from "../common/variables"; const fadeInUp = keyframes` @@ -138,7 +138,7 @@ const DxcToast = ({ semantic, }: ToastPropsType) => { const [isClosing, setIsClosing] = useState(false); - const translatedLabels = useTranslatedLabels(); + const translatedLabels = useContext(HalstackLanguageContext); const clearClosingAnimationTimer = useTimeout( () => { @@ -183,7 +183,7 @@ const DxcToast = ({ onClear(); }, 300); }} - title={translatedLabels?.toast?.clearToastActionTitle ?? ""} + title={translatedLabels.toast.clearToastActionTitle} /> </DxcFlex> </Toast> diff --git a/packages/lib/src/toast/ToastsQueue.tsx b/packages/lib/src/toast/ToastsQueue.tsx index 9d0f9b1712..5b12882bd0 100644 --- a/packages/lib/src/toast/ToastsQueue.tsx +++ b/packages/lib/src/toast/ToastsQueue.tsx @@ -1,9 +1,9 @@ -import { createContext, useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { createPortal } from "react-dom"; import styled from "styled-components"; import CoreTokens from "../common/coreTokens"; import DxcToast from "./Toast"; -import { QueuedToast, Semantic, ToastContextType, ToastsQueuePropsType, ToastType } from "./types"; +import { QueuedToast, Semantic, ToastsQueuePropsType, ToastType } from "./types"; import { responsiveSizes } from "../common/variables"; import ToastContext from "./ToastContext"; diff --git a/packages/lib/src/toggle-group/ToggleGroup.tsx b/packages/lib/src/toggle-group/ToggleGroup.tsx index 0b80b299a3..6ef06f8b4d 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.tsx @@ -1,10 +1,10 @@ -import { KeyboardEvent, useId, useState } from "react"; +import { KeyboardEvent, useContext, useId, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcFlex from "../flex/Flex"; import DxcIcon from "../icon/Icon"; import { Tooltip } from "../tooltip/Tooltip"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import ToggleGroupPropsType, { OptionLabel } from "./types"; const DxcToggleGroup = ({ @@ -22,7 +22,7 @@ const DxcToggleGroup = ({ const toggleGroupLabelId = `label-toggle-group-${useId()}`; const [selectedValue, setSelectedValue] = useState(defaultValue ?? (multiple ? [] : -1)); - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const handleToggleChange = (selectedOption: number) => { let newSelectedOptions: number[] = []; @@ -66,7 +66,7 @@ const DxcToggleGroup = ({ }; return ( - <ThemeProvider theme={colorsTheme?.toggleGroup}> + <ThemeProvider theme={colorsTheme.toggleGroup}> <ToggleGroup margin={margin}> <Label id={toggleGroupLabelId} disabled={disabled}> {label} diff --git a/packages/lib/src/tooltip/Tooltip.tsx b/packages/lib/src/tooltip/Tooltip.tsx index ae46bfdc0e..6afebd6aa2 100644 --- a/packages/lib/src/tooltip/Tooltip.tsx +++ b/packages/lib/src/tooltip/Tooltip.tsx @@ -4,6 +4,7 @@ import TooltipPropsType, { TooltipWrapperProps } from "./types"; import { createContext, 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"; const TooltipTriggerContainer = styled.div` position: relative; @@ -103,8 +104,6 @@ const triangleIcon = ( </svg> ); -const TooltipContext = createContext(false); - export const Tooltip = ({ label, hasAdditionalContainer = false, diff --git a/packages/lib/src/tooltip/TooltipContext.tsx b/packages/lib/src/tooltip/TooltipContext.tsx new file mode 100644 index 0000000000..04e597ce47 --- /dev/null +++ b/packages/lib/src/tooltip/TooltipContext.tsx @@ -0,0 +1,3 @@ +import { createContext } from "react"; + +export const TooltipContext = createContext<boolean>(false); diff --git a/packages/lib/src/typography/Typography.tsx b/packages/lib/src/typography/Typography.tsx index 41a937682e..d8f1d354b8 100644 --- a/packages/lib/src/typography/Typography.tsx +++ b/packages/lib/src/typography/Typography.tsx @@ -1,6 +1,7 @@ -import { createContext, useContext, useMemo } from "react"; +import { useContext, useMemo } from "react"; import styled from "styled-components"; -import TypographyPropsTypes, { TypographyContextProps } from "./types"; +import TypographyPropsTypes from "./types"; +import TypographyContext from "./TypographyContext"; const Typography = styled.span<TypographyPropsTypes>` margin: 0px; @@ -20,57 +21,15 @@ const Typography = styled.span<TypographyPropsTypes>` overflow: ${({ textOverflow }) => (textOverflow !== "unset" ? "hidden" : "visible")}; `; -const TypographyContext = createContext<TypographyContextProps | null>(null); - -export default function DxcTypography({ - as, - color, - children, - display, - fontFamily, - fontSize, - fontStyle, - fontWeight, - letterSpacing, - lineHeight, - textAlign, - textDecoration, - textOverflow, - whiteSpace, -}: TypographyPropsTypes) { +export default function DxcTypography({ children, ...props }: TypographyPropsTypes) { const componentContext = useContext(TypographyContext); const contextValue = useMemo( () => ({ - as: as ?? componentContext?.as ?? "span", - display: display ?? componentContext?.display ?? "inline", - fontFamily: fontFamily ?? componentContext?.fontFamily ?? "Open Sans, sans-serif", - fontSize: fontSize ?? componentContext?.fontSize ?? "1rem", - fontStyle: fontStyle ?? componentContext?.fontStyle ?? "normal", - fontWeight: fontWeight ?? componentContext?.fontWeight ?? "400", - letterSpacing: letterSpacing ?? componentContext?.letterSpacing ?? "0em", - lineHeight: lineHeight ?? componentContext?.lineHeight ?? "1.5em", - textAlign: textAlign ?? componentContext?.textAlign ?? "left", - color: color ?? componentContext?.color ?? "#000000", - textDecoration: textDecoration ?? componentContext?.textDecoration ?? "none", - textOverflow: textOverflow ?? componentContext?.textOverflow ?? "unset", - whiteSpace: whiteSpace ?? componentContext?.whiteSpace ?? "normal", + ...componentContext, + ...props, }), - [ - as, - color, - display, - fontFamily, - fontSize, - fontStyle, - fontWeight, - letterSpacing, - lineHeight, - textAlign, - textDecoration, - textOverflow, - whiteSpace, - ] + [componentContext, props] ); return ( diff --git a/packages/lib/src/typography/TypographyContext.tsx b/packages/lib/src/typography/TypographyContext.tsx new file mode 100644 index 0000000000..66e8473bbc --- /dev/null +++ b/packages/lib/src/typography/TypographyContext.tsx @@ -0,0 +1,18 @@ +import { createContext } from "react"; +import { TypographyContextProps } from "./types"; + +export default createContext<TypographyContextProps>({ + as: "span", + color: "#000000", + display: "inline", + fontFamily: "Open Sans, sans-serif", + fontSize: "1rem", + fontStyle: "normal", + fontWeight: "400", + letterSpacing: "0em", + lineHeight: "1.5em", + textAlign: "left", + textDecoration: "none", + textOverflow: "unset", + whiteSpace: "normal", +}); diff --git a/packages/lib/src/typography/types.ts b/packages/lib/src/typography/types.ts index 5f5c16c3bb..9f4e40c814 100644 --- a/packages/lib/src/typography/types.ts +++ b/packages/lib/src/typography/types.ts @@ -1,7 +1,25 @@ import { ReactNode } from "react"; export type Props = { - as?: "a" | "blockquote" | "cite" | "code" | "div" | "em" | "figcaption" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "pre" | "small" | "span" | "strong"; + as?: + | "a" + | "blockquote" + | "cite" + | "code" + | "div" + | "em" + | "figcaption" + | "h1" + | "h2" + | "h3" + | "h4" + | "h5" + | "h6" + | "p" + | "pre" + | "small" + | "span" + | "strong"; children: ReactNode; color?: string; display?: "inline" | "block"; @@ -19,4 +37,4 @@ export type Props = { export default Props; -export type TypographyContextProps = Omit<Props, "children">; +export type TypographyContextProps = Required<Omit<Props, "children">>; diff --git a/packages/lib/src/utils/useTheme.tsx b/packages/lib/src/utils/useTheme.tsx deleted file mode 100644 index f9ef4f03fc..0000000000 --- a/packages/lib/src/utils/useTheme.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useContext } from "react"; -import { componentTokens } from "../common/variables"; -import HalstackContext from "../HalstackContext"; - -const useTheme = () => { - const colorsTheme = useContext(HalstackContext); - return colorsTheme ?? componentTokens; -}; - -export default useTheme; diff --git a/packages/lib/src/utils/useTranslatedLabels.tsx b/packages/lib/src/utils/useTranslatedLabels.tsx deleted file mode 100644 index 446e2b6486..0000000000 --- a/packages/lib/src/utils/useTranslatedLabels.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useContext } from "react"; -import { defaultTranslatedComponentLabels } from "../common/variables"; -import { HalstackLanguageContext } from "../HalstackContext"; - -const useTranslatedLabels = () => { - const labels = useContext(HalstackLanguageContext); - return labels ?? defaultTranslatedComponentLabels; -}; - -export default useTranslatedLabels; diff --git a/packages/lib/src/utils/useWidth.tsx b/packages/lib/src/utils/useWidth.tsx index 9580a0bab3..96171bc9aa 100644 --- a/packages/lib/src/utils/useWidth.tsx +++ b/packages/lib/src/utils/useWidth.tsx @@ -10,7 +10,7 @@ const useWidth = <T extends Element>(target: T | null) => { const triggerObserver = new ResizeObserver((entries) => { const rect = entries[0]?.target.getBoundingClientRect(); if (rect) { - setWidth(rect?.width); + setWidth(rect.width); } }); triggerObserver.observe(target); diff --git a/packages/lib/src/wizard/Wizard.tsx b/packages/lib/src/wizard/Wizard.tsx index a0cf132f41..ce0a397a06 100644 --- a/packages/lib/src/wizard/Wizard.tsx +++ b/packages/lib/src/wizard/Wizard.tsx @@ -1,8 +1,8 @@ -import { useState } from "react"; +import { useContext, useState } from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import DxcIcon from "../icon/Icon"; -import useTheme from "../utils/useTheme"; +import HalstackContext from "../HalstackContext"; import WizardPropsType, { StepProps } from "./types"; const icons = { @@ -54,7 +54,7 @@ const DxcWizard = ({ }: WizardPropsType): JSX.Element => { const [innerCurrent, setInnerCurrentStep] = useState(currentStep ?? defaultCurrentStep ?? 0); const renderedCurrent = currentStep ?? innerCurrent; - const colorsTheme = useTheme(); + const colorsTheme = useContext(HalstackContext); const handleStepClick = (newValue: number) => { setInnerCurrentStep(newValue); From 06520b9be34cc39f9957381e2670672a410c11b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:22:06 +0100 Subject: [PATCH 27/41] Fixing type errors in affected files --- .../accordion-group/AccordionGroup.test.tsx | 4 - packages/lib/src/accordion/Accordion.test.tsx | 3 - .../lib/src/action-icon/ActionIcon.test.tsx | 2 - packages/lib/src/badge/Badge.test.tsx | 1 - .../src/breadcrumbs/Breadcrumbs.stories.tsx | 2 +- packages/lib/src/breadcrumbs/Item.tsx | 2 +- packages/lib/src/button/Button.test.tsx | 2 - packages/lib/src/card/Card.stories.tsx | 11 +- packages/lib/src/card/Card.test.tsx | 3 - .../lib/src/checkbox/Checkbox.stories.tsx | 5 +- packages/lib/src/checkbox/Checkbox.test.tsx | 10 - packages/lib/src/chip/Chip.test.tsx | 2 - .../src/contextual-menu/ContextualMenu.tsx | 2 +- packages/lib/src/contextual-menu/Section.tsx | 2 +- packages/lib/src/data-grid/DataGrid.test.tsx | 41 +-- .../DateInput.accessibility.test.tsx | 8 +- .../lib/src/date-input/DateInput.stories.tsx | 12 +- .../lib/src/date-input/DateInput.test.tsx | 333 +++++++++--------- packages/lib/src/date-input/DateInput.tsx | 20 +- packages/lib/src/dialog/Dialog.test.tsx | 38 +- packages/lib/src/divider/Divider.test.tsx | 10 +- .../lib/src/dropdown/Dropdown.stories.tsx | 9 +- packages/lib/src/dropdown/Dropdown.test.tsx | 20 -- packages/lib/src/dropdown/Dropdown.tsx | 15 +- .../lib/src/file-input/FileInput.test.tsx | 2 +- packages/lib/src/file-input/FileInput.tsx | 18 +- packages/lib/src/file-input/FileItem.tsx | 4 +- .../src/footer/Footer.accessibility.test.tsx | 7 +- packages/lib/src/footer/Footer.stories.tsx | 4 +- packages/lib/src/footer/Footer.test.tsx | 10 +- .../src/header/Header.accessibility.test.tsx | 7 +- packages/lib/src/header/Header.stories.tsx | 2 +- packages/lib/src/header/Header.test.tsx | 4 - packages/lib/src/header/Header.tsx | 2 +- packages/lib/src/heading/Heading.test.tsx | 11 - packages/lib/src/link/Link.stories.tsx | 1 - packages/lib/src/link/Link.test.tsx | 5 - 37 files changed, 249 insertions(+), 385 deletions(-) diff --git a/packages/lib/src/accordion-group/AccordionGroup.test.tsx b/packages/lib/src/accordion-group/AccordionGroup.test.tsx index 1dd5ebaa24..6d4b87ecf3 100644 --- a/packages/lib/src/accordion-group/AccordionGroup.test.tsx +++ b/packages/lib/src/accordion-group/AccordionGroup.test.tsx @@ -27,7 +27,6 @@ describe("Accordion component tests", () => { expect(getAllByRole("button")[0]?.getAttribute("aria-expanded")).toBe("true"); expect(getAllByRole("button")[1]?.getAttribute("aria-expanded")).toBe("false"); }); - test("Uncontrolled accordion group renders initially with an accordion expanded using defaultIndexActive", () => { const { queryByText, getByText, getAllByRole } = render( <DxcAccordionGroup defaultIndexActive={1}> @@ -44,7 +43,6 @@ describe("Accordion component tests", () => { expect(getByText("Second accordion")).toBeTruthy(); expect(queryByText("First accordion")).toBeFalsy(); }); - test("Controlled accordion with indexActive change", () => { const onActiveChange = jest.fn(); const { queryByText, getByText, getAllByRole, rerender } = render( @@ -65,7 +63,6 @@ describe("Accordion component tests", () => { expect(onActiveChange.mock.calls[1][0]).toBe(1); expect(getAllByRole("button")[0]?.getAttribute("aria-expanded")).toBe("false"); expect(getAllByRole("button")[1]?.getAttribute("aria-expanded")).toBe("true"); - rerender( <DxcAccordionGroup margin="large" indexActive={0} onActiveChange={onActiveChange}> <DxcAccordionGroup.Accordion label="Accordion1"> @@ -79,7 +76,6 @@ describe("Accordion component tests", () => { expect(getAllByRole("button")[0]?.getAttribute("aria-expanded")).toBe("true"); expect(getAllByRole("button")[1]?.getAttribute("aria-expanded")).toBe("false"); }); - test("Disabled uncontrolled accordion group", () => { const onActiveChange = jest.fn(); const { getByText } = render( diff --git a/packages/lib/src/accordion/Accordion.test.tsx b/packages/lib/src/accordion/Accordion.test.tsx index 56252023c1..b2b0b5b263 100644 --- a/packages/lib/src/accordion/Accordion.test.tsx +++ b/packages/lib/src/accordion/Accordion.test.tsx @@ -13,7 +13,6 @@ describe("Accordion component tests", () => { expect(accordion.getAttribute("aria-controls")).toBe(panel.id); expect(panel.getAttribute("aria-labelledby")).toBe(accordion.id); }); - test("Renders expanded by default when it is uncontrolled", () => { const { getByRole } = render( <DxcAccordion label="Accordion" defaultIsExpanded> @@ -23,7 +22,6 @@ describe("Accordion component tests", () => { const accordion = getByRole("button"); expect(accordion.getAttribute("aria-expanded")).toBe("true"); }); - test("Calls correct function on click", () => { const onChange = jest.fn(); const { getByText } = render( @@ -34,7 +32,6 @@ describe("Accordion component tests", () => { fireEvent.click(getByText("Accordion")); expect(onChange).toHaveBeenCalled(); }); - test("Controlled accordion", () => { const onChange = jest.fn(); const { getByText, getByRole } = render( diff --git a/packages/lib/src/action-icon/ActionIcon.test.tsx b/packages/lib/src/action-icon/ActionIcon.test.tsx index e7f52d0f7c..2e3b7a40b9 100644 --- a/packages/lib/src/action-icon/ActionIcon.test.tsx +++ b/packages/lib/src/action-icon/ActionIcon.test.tsx @@ -15,7 +15,6 @@ describe("Action icon component tests", () => { fireEvent.click(action); expect(onClick).toHaveBeenCalled(); }); - test("On click is not called when disabled", () => { const onClick = jest.fn(); const { getByRole } = render(<DxcActionIcon disabled icon={iconSVG} title="favourite" onClick={onClick} />); @@ -23,7 +22,6 @@ describe("Action icon component tests", () => { fireEvent.click(action); expect(onClick).toHaveBeenCalledTimes(0); }); - test("Renders with correct accessibility attributes", () => { const { getByRole } = render(<DxcActionIcon icon={iconSVG} title="favourite" tabIndex={1} />); diff --git a/packages/lib/src/badge/Badge.test.tsx b/packages/lib/src/badge/Badge.test.tsx index c1d1fec4de..fe9105a30f 100644 --- a/packages/lib/src/badge/Badge.test.tsx +++ b/packages/lib/src/badge/Badge.test.tsx @@ -7,7 +7,6 @@ describe("Badge component tests", () => { const { getByText } = render(<DxcBadge label={99} mode="notification" />); expect(getByText("99")).toBeTruthy(); }); - test("Badge renders +99 as label when it is greater than notification limit", () => { const { getByText } = render( <DxcFlex> diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx index 8a40e72c10..828addc0ba 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.stories.tsx @@ -193,6 +193,6 @@ export const Chromatic: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const dropdowns = canvas.getAllByRole("button"); - await userEvent.click(dropdowns[2]); + dropdowns[2] != null && await userEvent.click(dropdowns[2]); }, }; diff --git a/packages/lib/src/breadcrumbs/Item.tsx b/packages/lib/src/breadcrumbs/Item.tsx index ae99c2bb82..1b84a48c65 100644 --- a/packages/lib/src/breadcrumbs/Item.tsx +++ b/packages/lib/src/breadcrumbs/Item.tsx @@ -8,7 +8,7 @@ const Item = ({ isCurrentPage = false, href, label, onClick }: ItemPropsType) => const handleOnMouseEnter = (event: MouseEvent<HTMLAnchorElement>) => { const labelContainer = event.currentTarget; - const optionElement = currentItemRef?.current; + const optionElement = currentItemRef.current; if (optionElement?.title === "" && labelContainer.scrollWidth > labelContainer.clientWidth) { optionElement.title = label; } diff --git a/packages/lib/src/button/Button.test.tsx b/packages/lib/src/button/Button.test.tsx index b3299f6d6c..bb51167def 100644 --- a/packages/lib/src/button/Button.test.tsx +++ b/packages/lib/src/button/Button.test.tsx @@ -9,14 +9,12 @@ describe("Button component tests", () => { fireEvent.click(button); expect(onClick).toHaveBeenCalled(); }); - test("Renders with correct accessibility attributes", () => { const { getByRole } = render(<DxcButton label="Home" title="Go home" tabIndex={1} semantic="error" />); const button = getByRole("button"); expect(button.getAttribute("aria-label")).toBe("Go home"); expect(button.getAttribute("tabindex")).toBe("1"); }); - test("Triggers form submit event on button click", () => { const handleSubmit = jest.fn(); const { getByRole } = render( diff --git a/packages/lib/src/card/Card.stories.tsx b/packages/lib/src/card/Card.stories.tsx index ebc08151ad..5be6aa4e0b 100644 --- a/packages/lib/src/card/Card.stories.tsx +++ b/packages/lib/src/card/Card.stories.tsx @@ -151,11 +151,6 @@ const actionCard = () => ( </> ); -const linkStates = async (focusCard, hoverCard) => { - await focusCard.focus(); - await userEvent.hover(hoverCard); -}; - type Story = StoryObj<typeof DxcCard>; export const ActionCardStates: Story = { @@ -163,7 +158,8 @@ export const ActionCardStates: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); await userEvent.tab(); - await userEvent.hover(canvas.getAllByText("Hovered default with action")[1]); + const card = canvas.getAllByText("Hovered default with action")[1]; + card != null && (await userEvent.hover(card)); }, }; @@ -172,6 +168,7 @@ export const Chromatic: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const linkCards = canvas.getAllByRole("link"); - await linkStates(linkCards[1], linkCards[2]); + linkCards[1] != null && linkCards[1].focus(); + linkCards[2] != null && (await userEvent.hover(linkCards[2])); }, }; diff --git a/packages/lib/src/card/Card.test.tsx b/packages/lib/src/card/Card.test.tsx index 1b26e1cbbe..3ec35fc3cb 100644 --- a/packages/lib/src/card/Card.test.tsx +++ b/packages/lib/src/card/Card.test.tsx @@ -6,19 +6,16 @@ describe("Card component tests", () => { const { getByText } = render(<DxcCard>test-card</DxcCard>); expect(getByText("test-card")).toBeTruthy(); }); - test("Card renders with correct href", () => { const { getByRole } = render(<DxcCard linkHref="/testPage">test-card</DxcCard>); const card = getByRole("link"); expect(card.getAttribute("href")).toEqual("/testPage"); }); - test("Card renders with correct image", () => { const { getByRole } = render(<DxcCard imageSrc="/testImage">test-card</DxcCard>); const card = getByRole("img"); expect(card.getAttribute("src")).toEqual("/testImage"); }); - test("OnClick function is called", () => { const onClick = jest.fn(); const { getByText } = render(<DxcCard onClick={onClick}>test-card</DxcCard>); diff --git a/packages/lib/src/checkbox/Checkbox.stories.tsx b/packages/lib/src/checkbox/Checkbox.stories.tsx index 1f172703ea..3214db89de 100644 --- a/packages/lib/src/checkbox/Checkbox.stories.tsx +++ b/packages/lib/src/checkbox/Checkbox.stories.tsx @@ -8,7 +8,7 @@ import { Meta, StoryObj } from "@storybook/react"; export default { title: "Checkbox", component: DxcCheckbox, -} as Meta<typeof DxcCheckbox>;; +} as Meta<typeof DxcCheckbox>; const opinionatedTheme = { checkbox: { @@ -219,7 +219,6 @@ type Story = StoryObj<typeof DxcCheckbox>; export const Chromatic: Story = { render: Checkbox, play: async () => { - const listEl = document.getElementById("scroll-container"); - listEl?.scrollTo?.({ top: 50 }); + 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 2a8563fcb0..a6979ea019 100644 --- a/packages/lib/src/checkbox/Checkbox.test.tsx +++ b/packages/lib/src/checkbox/Checkbox.test.tsx @@ -11,19 +11,16 @@ describe("Checkbox component tests", () => { expect(getByRole("checkbox").getAttribute("aria-readonly")).toBe("false"); expect(getByRole("checkbox").getAttribute("aria-disabled")).toBe("false"); }); - test("Optional checkbox renders with correct aria-required", () => { const { getByRole } = render(<DxcCheckbox label="Checkbox" optional />); expect(getByRole("checkbox").getAttribute("aria-required")).toBe("false"); }); - test("Calls correct function onChange", () => { const onChange = jest.fn(); const { getByText } = render(<DxcCheckbox label="Checkbox" onChange={onChange} />); fireEvent.click(getByText("Checkbox")); expect(onChange).toHaveBeenCalled(); }); - test("Read-only checkbox does not trigger onChange function", () => { const onChange = jest.fn(); const { getByRole } = render(<DxcCheckbox label="Checkbox" onChange={onChange} readOnly />); @@ -32,7 +29,6 @@ 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) => { e.preventDefault(); @@ -50,7 +46,6 @@ describe("Checkbox component tests", () => { await userEvent.click(submit); expect(handlerOnSubmit).toHaveBeenCalled(); }); - test("Read-only checkbox doesn't change its value with Space key", () => { const onChange = jest.fn(); const { getByRole } = render(<DxcCheckbox label="Checkbox" onChange={onChange} readOnly />); @@ -60,7 +55,6 @@ describe("Checkbox component tests", () => { fireEvent.keyDown(checkbox, { key: " ", code: "Space", keyCode: 32, charCode: 32 }); expect(onChange).not.toHaveBeenCalled(); }); - test("Uncontrolled checkbox", () => { const onChange = jest.fn(); const component = render(<DxcCheckbox label="Checkbox" onChange={onChange} name="test" />); @@ -75,7 +69,6 @@ describe("Checkbox component tests", () => { expect(input.getAttribute("aria-checked")).toBe("true"); expect(submitInput?.checked).toBe(true); }); - test("Controlled checkbox", () => { const onChange = jest.fn(); const component = render(<DxcCheckbox label="Checkbox" checked={false} onChange={onChange} name="test" />); @@ -88,7 +81,6 @@ describe("Checkbox component tests", () => { expect(input.getAttribute("aria-checked")).toBe("false"); expect(submitInput?.checked).toBe(false); }); - test("Renders with correct initial value and initial state when it is uncontrolled", () => { const { getByRole, container } = render( <DxcCheckbox label="Default label" defaultChecked value="test-defaultChecked" name="test" /> @@ -99,7 +91,6 @@ describe("Checkbox component tests", () => { expect(checkbox.getAttribute("aria-checked")).toBe("true"); expect(submitInput?.checked).toBe(true); }); - test("Test disable keyboard and mouse interactions", () => { const onChange = jest.fn(); const { getByRole, getByText, container } = render( @@ -116,7 +107,6 @@ describe("Checkbox component tests", () => { userEvent.tab(); expect(document.activeElement === input).toBeFalsy(); }); - test("Test keyboard interactions", () => { const onChange = jest.fn(); const { getByRole } = render(<DxcCheckbox label="Checkbox" name="test" onChange={onChange} />); diff --git a/packages/lib/src/chip/Chip.test.tsx b/packages/lib/src/chip/Chip.test.tsx index 17d874e8e6..e324487347 100644 --- a/packages/lib/src/chip/Chip.test.tsx +++ b/packages/lib/src/chip/Chip.test.tsx @@ -6,7 +6,6 @@ describe("Chip component tests", () => { const { getByText } = render(<DxcChip label="Chip" />); expect(getByText("Chip")).toBeTruthy(); }); - test("Calls correct function when clicking on prefix icon", () => { const onClick = jest.fn(); const { getByText, getByRole } = render(<DxcChip label="Chip" prefixIcon="nutrition" onClickPrefix={onClick} />); @@ -14,7 +13,6 @@ describe("Chip component tests", () => { fireEvent.click(getByRole("button")); expect(onClick).toHaveBeenCalled(); }); - test("Calls correct function when clicking on suffix icon", () => { const onClick = jest.fn(); const { getByText, getByRole } = render(<DxcChip label="Chip" suffixIcon="nutrition" onClickSuffix={onClick} />); diff --git a/packages/lib/src/contextual-menu/ContextualMenu.tsx b/packages/lib/src/contextual-menu/ContextualMenu.tsx index ec064ad1e6..98b3c6a629 100644 --- a/packages/lib/src/contextual-menu/ContextualMenu.tsx +++ b/packages/lib/src/contextual-menu/ContextualMenu.tsx @@ -84,7 +84,7 @@ export default function DxcContextualMenu({ items }: ContextualMenuPropsType) { const [firstUpdate, setFirstUpdate] = useState(true); useLayoutEffect(() => { if (selectedItemId !== -1 && firstUpdate) { - const contextualMenuEl = contextualMenuRef?.current; + const contextualMenuEl = contextualMenuRef.current; const selectedItemEl = contextualMenuEl?.querySelector("[aria-pressed='true']") as HTMLButtonElement; contextualMenuEl?.scrollTo?.({ top: (selectedItemEl?.offsetTop ?? 0) - (contextualMenuEl?.clientHeight ?? 0) / 2, diff --git a/packages/lib/src/contextual-menu/Section.tsx b/packages/lib/src/contextual-menu/Section.tsx index 6b4ef8534e..07d8029f50 100644 --- a/packages/lib/src/contextual-menu/Section.tsx +++ b/packages/lib/src/contextual-menu/Section.tsx @@ -11,7 +11,7 @@ const Section = ({ section, index, length }: SectionProps) => { const id = `section-${useId()}`; return ( - <section aria-label={section?.title ?? id} aria-labelledby={id}> + <section aria-label={section.title ?? id} aria-labelledby={id}> {section.title && <Title id={id}>{section.title}} {section.items.map((item, index) => ( diff --git a/packages/lib/src/data-grid/DataGrid.test.tsx b/packages/lib/src/data-grid/DataGrid.test.tsx index 54889eaec1..61a0a8376f 100644 --- a/packages/lib/src/data-grid/DataGrid.test.tsx +++ b/packages/lib/src/data-grid/DataGrid.test.tsx @@ -48,49 +48,10 @@ const expandableRows = [ }, ]; -const rowsWithChildren: HierarchyGridRow[] = [ - { - id: 1, - complete: 46, - childRows: [ - { - id: 1.1, - complete: 46, - childRows: [ - { - id: "1.1a", - complete: 46, - }, - ], - }, - { - complete: 46, - value: 1.2, - }, - ], - }, - { - id: 2, - complete: 51, - }, - { - id: 3, - complete: 40, - }, - { - id: 4, - complete: 10, - }, - { - id: 5, - complete: 1, - }, -] as HierarchyGridRow[]; - describe("Data grid component tests", () => { beforeAll(() => { (global as any).CSS = { - escape: (str) => str, + escape: (str: string): string => str, }; window.HTMLElement.prototype.scrollIntoView = jest.fn; }); diff --git a/packages/lib/src/date-input/DateInput.accessibility.test.tsx b/packages/lib/src/date-input/DateInput.accessibility.test.tsx index 64d14b5a79..21ff46dfc0 100644 --- a/packages/lib/src/date-input/DateInput.accessibility.test.tsx +++ b/packages/lib/src/date-input/DateInput.accessibility.test.tsx @@ -1,5 +1,5 @@ import { render } from "@testing-library/react"; -import { axe } from "../../test/accessibility/axe-helper"; +import { axe, formatRules } from "../../test/accessibility/axe-helper"; import DxcDateInput from "./DateInput"; // Mocking DOMRect for Radix Primitive Popover @@ -17,12 +17,10 @@ import DxcDateInput from "./DateInput"; import { disabledRules as rules } from "../../test/accessibility/rules/specific/date-input/disabledRules"; const disabledRules = { - rules: rules.reduce((rulesObj, rule) => { - rulesObj[rule] = { enabled: false }; - return rulesObj; - }, {}), + rules: formatRules(rules), }; + describe("DateInput component accessibility tests", () => { it("Should not have basic accessibility issues", async () => { // baseElement is needed when using React Portals diff --git a/packages/lib/src/date-input/DateInput.stories.tsx b/packages/lib/src/date-input/DateInput.stories.tsx index fd5085979e..42f6761cf6 100644 --- a/packages/lib/src/date-input/DateInput.stories.tsx +++ b/packages/lib/src/date-input/DateInput.stories.tsx @@ -291,7 +291,8 @@ export const Chromatic: Story = { render: DateInputChromatic, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[0]); + const firstDateInput = canvas.getAllByRole("combobox")[0]; + firstDateInput != null && (await userEvent.click(firstDateInput)); await fireEvent.click(screen.getByText("April 1905")); }, }; @@ -300,7 +301,8 @@ export const DateInputOpinionated: Story = { render: DateInputOpinionatedTheme, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[3]); + const dateInput = canvas.getAllByRole("combobox")[3]; + dateInput != null && (await userEvent.click(dateInput)); }, }; @@ -318,7 +320,7 @@ export const DatePickerStates: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const dateBtn = canvas.getAllByRole("combobox")[0]; - await userEvent.click(dateBtn); + dateBtn != null && (await userEvent.click(dateBtn)); }, }; @@ -335,7 +337,7 @@ export const DatePickerTooltipPrevious: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const previousMonthButton = canvas.getAllByRole("button")[0]; - await userEvent.hover(previousMonthButton); + previousMonthButton != null && (await userEvent.hover(previousMonthButton)); }, }; @@ -344,6 +346,6 @@ export const DatePickerTooltipAfter: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const afterMonthButton = canvas.getAllByRole("button")[2]; - await userEvent.hover(afterMonthButton); + afterMonthButton != null && (await userEvent.hover(afterMonthButton)); }, }; diff --git a/packages/lib/src/date-input/DateInput.test.tsx b/packages/lib/src/date-input/DateInput.test.tsx index bf7fcdf51f..1975837246 100644 --- a/packages/lib/src/date-input/DateInput.test.tsx +++ b/packages/lib/src/date-input/DateInput.test.tsx @@ -15,7 +15,7 @@ import DxcDateInput from "./DateInput"; }; describe("DateInput component tests", () => { - test("Renders with correct label, helper text, optional, placeholder and clearable action", async () => { + test("Renders with correct label, helper text, optional, placeholder and clearable action", () => { const { getByText, getByRole, getAllByRole } = render( ); @@ -26,69 +26,63 @@ describe("DateInput component tests", () => { expect(input.getAttribute("placeholder")).toBe("DD-MM-YYYY"); userEvent.type(input, "10/10/2010"); const closeAction = getAllByRole("button")[0]; - await userEvent.click(closeAction); + closeAction != null && userEvent.click(closeAction); expect(input.value).toBe(""); }); - test("Renders with custom error", () => { const { getByText } = render(); expect(getByText("Personalized error.")).toBeTruthy(); }); - - test("Read-only variant doesn't open the calendar", async () => { + test("Read-only variant doesn't open the calendar", () => { const { getByRole, queryByRole } = render(); const calendarAction = getByRole("combobox"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect(queryByRole("dialog")).toBeFalsy(); }); - - test("Renders with an initial value when it is uncontrolled", async () => { + test("Renders with an initial value when it is uncontrolled", () => { const { getByText, getByRole } = render(); const input = getByRole("textbox") as HTMLInputElement; const calendarAction = getByRole("combobox"); expect(input.value).toBe("21-10-2015"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect(getByText("21").getAttribute("aria-selected")).toBe("true"); expect(getByText("October 2015")).toBeTruthy(); }); - - test("Renders with correct format: user typed date but it's invalid, onBlur error", async () => { + test("Renders with correct format: user typed date but it's invalid, onBlur error", () => { const onBlur = jest.fn(({ value, error }) => { expect(value).toBe("10/90/2010"); expect(error).toBe("Invalid date."); }); const { getByRole } = render(); const input = getByRole("textbox"); - await userEvent.click(input); - await userEvent.keyboard("10"); - await userEvent.keyboard("/"); - await userEvent.keyboard("90"); - await userEvent.keyboard("/"); - await userEvent.keyboard("2010"); + userEvent.click(input); + userEvent.keyboard("10"); + userEvent.keyboard("/"); + userEvent.keyboard("90"); + userEvent.keyboard("/"); + userEvent.keyboard("2010"); fireEvent.blur(input); }); - - test("Renders with correct format: user typed date but it's invalid, onChange error", async () => { + test("Renders with correct format: user typed date but it's invalid, onChange error", () => { const onChange = jest.fn(); const { getByRole } = render( ); const input = getByRole("textbox"); - await userEvent.click(input); - await userEvent.keyboard("10"); - await userEvent.keyboard("/"); - await userEvent.keyboard("90"); - await userEvent.keyboard("/"); - await userEvent.keyboard("2010"); + userEvent.click(input); + userEvent.keyboard("10"); + userEvent.keyboard("/"); + userEvent.keyboard("90"); + userEvent.keyboard("/"); + userEvent.keyboard("2010"); expect(onChange).toHaveBeenCalledTimes(10); expect(onChange).toHaveBeenCalledWith({ value: "10/90/2010", error: "Invalid date." }); }); - - test("Calendar renders with correct date: today's date", async () => { + test("Calendar renders with correct date: today's date", () => { const { getByText, getByRole, getAllByText } = render(); const calendarAction = getByRole("combobox"); const d = dayjs(); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect( document.activeElement === (getAllByText(d.get("date")).length === 2 && +d.get("date") > 20 @@ -97,28 +91,25 @@ describe("DateInput component tests", () => { ).toBeTruthy(); expect(getByText(d.format("MMMM YYYY"))).toBeTruthy(); }); - - test("Calendar renders with correct date: value prop", async () => { + test("Calendar renders with correct date: value prop", () => { const { getByText, getByRole, getAllByText } = render(); const calendarAction = getByRole("combobox"); const d = dayjs("2019-10-20"); - await userEvent.click(calendarAction); - expect(getAllByText(d.get("date"))[0].getAttribute("aria-selected")).toBe("true"); + userEvent.click(calendarAction); + expect(getAllByText(d.get("date"))[0]?.getAttribute("aria-selected")).toBe("true"); expect(getByText(d.format("MMMM YYYY"))).toBeTruthy(); }); - - test("Calendar renders with correct date: user typed value", async () => { + test("Calendar renders with correct date: user typed value", () => { const { getByText, getByRole, getAllByText } = render(); const calendarAction = getByRole("combobox"); const d = dayjs("2010-1-1"); const input = getByRole("textbox"); userEvent.type(input, "01-01-2010"); - await userEvent.click(calendarAction); - expect(getAllByText(d.get("date"))[0].getAttribute("aria-selected")).toBe("true"); + userEvent.click(calendarAction); + expect(getAllByText(d.get("date"))[0]?.getAttribute("aria-selected")).toBe("true"); expect(getByText(d.format("MMMM YYYY"))).toBeTruthy(); }); - - test("Calendar renders with correct date: invalid date, renders with today's date", async () => { + test("Calendar renders with correct date: invalid date, renders with today's date", () => { const onBlur = jest.fn(); const { getByText, getByRole, getAllByText } = render(); const calendarAction = getByRole("combobox"); @@ -128,7 +119,7 @@ describe("DateInput component tests", () => { fireEvent.blur(input); expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: "01-01-xxxx", error: "Invalid date." }); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect( document.activeElement === (getAllByText(d.get("date")).length === 2 && +d.get("date") > 20 @@ -137,74 +128,73 @@ describe("DateInput component tests", () => { ).toBeTruthy(); expect(getByText(d.format("MMMM YYYY"))).toBeTruthy(); }); - - test("Selecting a date from the calendar with an specific format", async () => { + test("Selecting a date from the calendar with an specific format", () => { const { getAllByText, getByText, getByRole } = render( ); const input = getByRole("textbox") as HTMLInputElement; const calendarAction = getByRole("combobox"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); const dayButton = getAllByText("10")[0]; - fireEvent.click(dayButton); + dayButton != null && fireEvent.click(dayButton); let d = dayjs(); d = d.set("date", 10); - expect(getAllByText(d.get("date"))[0].getAttribute("aria-selected")).toBe("true"); + expect(getAllByText(d.get("date"))[0]?.getAttribute("aria-selected")).toBe("true"); expect(getByText(d.format("MMMM YYYY"))).toBeTruthy(); fireEvent.keyDown(document, { key: "Escape", code: "Escape", keyCode: 27, charCode: 27 }); expect(input.value).toBe(d.format("M-DD-YYYY")); }); - - test("Changing months using the arrows", async () => { + test("Changing months using the arrows", () => { const { getByText, getByRole, getAllByRole } = render( ); const calendarAction = getByRole("combobox"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); let d = dayjs("10-10-2000", "DD-MM-YYYY", true); d = d.set("date", 10); expect(getByText(d.format("MMMM YYYY"))).toBeTruthy(); const previousMonthButton = getAllByRole("button")[0]; - expect(previousMonthButton.getAttribute("aria-label")).toBe("Previous month"); - await userEvent.click(previousMonthButton); + if (previousMonthButton != null) { + expect(previousMonthButton.getAttribute("aria-label")).toBe("Previous month"); + userEvent.click(previousMonthButton); + } expect(getByText(d.set("month", d.get("month") - 1).format("MMMM YYYY"))).toBeTruthy(); const nextMonthButton = getAllByRole("button")[2]; - expect(nextMonthButton.getAttribute("aria-label")).toBe("Next month"); - await userEvent.click(nextMonthButton); + if (nextMonthButton != null) { + expect(nextMonthButton.getAttribute("aria-label")).toBe("Next month"); + userEvent.click(nextMonthButton); + } expect(getByText(d.format("MMMM YYYY"))).toBeTruthy(); }); - - test("Selecting a date from the calendar from another month", async () => { + test("Selecting a date from the calendar from another month", () => { const { getAllByText, getByText, getByRole } = render( ); const input = getByRole("textbox") as HTMLInputElement; const calendarAction = getByRole("combobox"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); const dayButton = getAllByText("31")[0]; - fireEvent.click(dayButton); + dayButton != null && fireEvent.click(dayButton); let d = dayjs("10-08-2021", "DD-MM-YYYY", true); d = d.set("date", 31).set("month", 6); - expect(getAllByText(d.get("date"))[0].getAttribute("aria-selected")).toBe("true"); + expect(getAllByText(d.get("date"))[0]?.getAttribute("aria-selected")).toBe("true"); expect(getByText(d.format("MMMM YYYY"))).toBeTruthy(); fireEvent.keyDown(document, { key: "Escape", code: "Escape", keyCode: 27, charCode: 27 }); expect(input.value).toBe(d.format("DD-MM-YYYY")); }); - - test("Selecting a year from the calendar year picker", async () => { + test("Selecting a year from the calendar year picker", () => { const { getByText, getByRole } = render(); const input = getByRole("textbox") as HTMLInputElement; const calendarAction = getByRole("combobox"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); const d = dayjs("10-08-2021", "DD-MM-YYYY", true); - await userEvent.click(getByText(d.format("MMMM YYYY"))); + userEvent.click(getByText(d.format("MMMM YYYY"))); expect(getByText("2024")).toBeTruthy(); - await userEvent.click(getByText("2024")); - await userEvent.click(getByText(d.set("year", 2024).format("MMMM YYYY"))); + userEvent.click(getByText("2024")); + userEvent.click(getByText(d.set("year", 2024).format("MMMM YYYY"))); fireEvent.keyDown(document, { key: "Escape", code: "Escape", keyCode: 27, charCode: 27 }); expect(input.value).toBe(d.format("DD-MM-YYYY")); }); - test("Selecting a date from the calendar (using keyboard presses)", async () => { const { getByRole, getAllByText, getByText } = render(); const calendarAction = getByRole("combobox"); @@ -212,99 +202,121 @@ describe("DateInput component tests", () => { userEvent.type(input, "01-01-2010"); expect(input.value).toBe("01-01-2010"); await userEvent.click(calendarAction); - expect(document.activeElement === getAllByText("1")[0]).toBeTruthy(); - fireEvent.keyDown(getAllByText("1")[0], { - key: "ArrowRight", - code: "ArrowRight", - keyCode: 39, - charCode: 39, - }); - expect(document.activeElement === getAllByText("2")[0]).toBeTruthy(); - fireEvent.keyDown(getAllByText("2")[0], { - key: "PageUp", - code: "PageUp", - keyCode: 33, - charCode: 33, - }); - expect(document.activeElement === getAllByText("2")[0]).toBeTruthy(); + const day1 = getAllByText("1")[0]; + expect(document.activeElement === day1).toBeTruthy(); + day1 != null && + fireEvent.keyDown(day1, { + key: "ArrowRight", + code: "ArrowRight", + keyCode: 39, + charCode: 39, + }); + let day2 = getAllByText("2")[0]; + expect(document.activeElement === day2).toBeTruthy(); + day2 != null && + fireEvent.keyDown(day2, { + key: "PageUp", + code: "PageUp", + keyCode: 33, + charCode: 33, + }); + day2 = getAllByText("2")[0]; + expect(document.activeElement === day2).toBeTruthy(); expect(getByText("December 2009")).toBeTruthy(); - fireEvent.keyDown(getAllByText("2")[0], { - key: "PageDown", - code: "PageDown", - keyCode: 34, - charCode: 34, - }); - expect(document.activeElement === getAllByText("2")[0]).toBeTruthy(); + day2 != null && + fireEvent.keyDown(day2, { + key: "PageDown", + code: "PageDown", + keyCode: 34, + charCode: 34, + }); + day2 = getAllByText("2")[0]; + expect(document.activeElement === day2).toBeTruthy(); expect(getByText("January 2010")).toBeTruthy(); - fireEvent.keyDown(getAllByText("2")[0], { - key: "PageDown", - code: "PageDown", - keyCode: 34, - charCode: 34, - shiftKey: true, - }); + day2 != null && + fireEvent.keyDown(day2, { + key: "PageDown", + code: "PageDown", + keyCode: 34, + charCode: 34, + shiftKey: true, + }); + day2 = getAllByText("2")[0]; expect(getByText("January 2011")).toBeTruthy(); - fireEvent.keyDown(getAllByText("2")[0], { - key: "PageUp", - code: "PageUp", - keyCode: 33, - charCode: 33, - shiftKey: true, - }); + day2 != null && + fireEvent.keyDown(day2, { + key: "PageUp", + code: "PageUp", + keyCode: 33, + charCode: 33, + shiftKey: true, + }); + day2 = getAllByText("2")[0]; expect(getByText("January 2010")).toBeTruthy(); - expect(document.activeElement === getAllByText("2")[0]).toBeTruthy(); - fireEvent.click(getAllByText("2")[0], { key: " ", code: "Space", keyCode: 32, charCode: 32 }); - expect(getAllByText("2")[0].getAttribute("aria-selected")).toBe("true"); + expect(document.activeElement === day2).toBeTruthy(); + day2 != null && fireEvent.click(day2, { key: " ", code: "Space", keyCode: 32, charCode: 32 }); + expect(day2?.getAttribute("aria-selected")).toBe("true"); fireEvent.keyDown(document, { key: "Escape", code: "Escape", keyCode: 27, charCode: 27 }); expect(input.value).toBe("02-01-2010"); }); - - test("Selecting a date from the calendar (using keyboard presses) part II", async () => { + test("Selecting a date from the calendar (using keyboard presses) part II", () => { const { getByRole, getAllByText } = render(); const calendarAction = getByRole("combobox"); const input = getByRole("textbox") as HTMLInputElement; userEvent.type(input, "01-01-2010"); expect(input.value).toBe("01-01-2010"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect(document.activeElement === getAllByText("1")[0]).toBeTruthy(); - fireEvent.keyDown(getAllByText("1")[0], { - key: "ArrowDown", - code: "ArrowDown", - keyCode: 40, - charCode: 40, - }); - expect(document.activeElement === getAllByText("8")[0]).toBeTruthy(); - fireEvent.keyDown(getAllByText("8")[0], { - key: "ArrowDown", - code: "ArrowDown", - keyCode: 40, - charCode: 40, - }); - expect(document.activeElement === getAllByText("15")[0]).toBeTruthy(); - fireEvent.keyDown(getAllByText("15")[0], { - key: "ArrowUp", - code: "ArrowUp", - keyCode: 38, - charCode: 38, - }); - expect(document.activeElement === getAllByText("8")[0]).toBeTruthy(); - fireEvent.keyDown(getAllByText("8")[0], { - key: "End", - code: "End", - keyCode: 35, - charCode: 35, - }); - expect(document.activeElement === getAllByText("10")[0]).toBeTruthy(); - fireEvent.keyDown(getAllByText("10")[0], { - key: "Home", - code: "Home", - keyCode: 36, - charCode: 36, - }); - fireEvent.keyDown(getAllByText("10")[0], { key: " ", code: "Space", keyCode: 32, charCode: 32 }); + const day1 = getAllByText("1")[0]; + const day8 = getAllByText("8")[0]; + const day10 = getAllByText("10")[0]; + const day15 = getAllByText("15")[0]; + day1 != null && + fireEvent.keyDown(day1, { + key: "ArrowDown", + code: "ArrowDown", + keyCode: 40, + charCode: 40, + }); + if (day8 != null) { + expect(document.activeElement === day8).toBeTruthy(); + fireEvent.keyDown(day8, { + key: "ArrowDown", + code: "ArrowDown", + keyCode: 40, + charCode: 40, + }); + } + if (day15 != null) { + expect(document.activeElement === day15).toBeTruthy(); + fireEvent.keyDown(day15, { + key: "ArrowUp", + code: "ArrowUp", + keyCode: 38, + charCode: 38, + }); + } + if (day8 != null) { + expect(document.activeElement === day8).toBeTruthy(); + fireEvent.keyDown(day8, { + key: "End", + code: "End", + keyCode: 35, + charCode: 35, + }); + } + if (day10 != null) { + expect(document.activeElement === day10).toBeTruthy(); + fireEvent.keyDown(day10, { + key: "Home", + code: "Home", + keyCode: 36, + charCode: 36, + }); + fireEvent.keyDown(day10, { key: " ", code: "Space", keyCode: 32, charCode: 32 }); + } expect(input.value).toBe("10-01-2010"); }); - test("onChange & onBlur functions are called correctly", () => { const onBlur = jest.fn(); const onChange = jest.fn(); @@ -319,7 +331,6 @@ describe("DateInput component tests", () => { expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: "10-10-2011", date: d }); }); - test("onChange & onBlur functions are called correctly, also with errors", () => { const onBlur = jest.fn(); const onChange = jest.fn(); @@ -333,7 +344,6 @@ describe("DateInput component tests", () => { expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: "10-10-", error: "Invalid date." }); }); - test("onBlur function removes the error when it is fixed", () => { const onBlur = jest.fn(); const { getByRole } = render(); @@ -351,7 +361,6 @@ describe("DateInput component tests", () => { expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: "20-02-2002", date: d }); }); - test("onBlur function removes the error when the input is empty", () => { const onBlur = jest.fn(); const { getByRole } = render(); @@ -366,7 +375,6 @@ describe("DateInput component tests", () => { expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: "" }); }); - test("onBlur & onChange functions error: required field (not optional)", () => { const onBlur = jest.fn(); const onChange = jest.fn(); @@ -388,19 +396,17 @@ describe("DateInput component tests", () => { error: "This field is required. Please, enter a value.", }); }); - - test("Disabled date input (calendar action must be shown but not clickable)", async () => { + test("Disabled date input (calendar action must be shown but not clickable)", () => { const { getByRole, queryByText } = render(); const calendarAction = getByRole("button"); const d = new Date(); const options: Intl.DateTimeFormatOptions = { weekday: "short", month: "short", day: "numeric" }; const input = getByRole("textbox") as HTMLInputElement; expect(input.disabled).toBeTruthy(); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect(queryByText(d.toLocaleString("en-US", options))).toBeFalsy(); }); - - test("Input has correct accessibility attributes", async () => { + test("Input has correct accessibility attributes", () => { const { getByRole } = render(); const input = getByRole("textbox"); expect(input.getAttribute("aria-autocomplete")).toBeNull(); @@ -411,39 +417,40 @@ describe("DateInput component tests", () => { expect(calendarAction.getAttribute("aria-controls")).toBeTruthy(); expect(calendarAction.getAttribute("aria-describedby")).toBeFalsy(); expect(calendarAction.getAttribute("aria-expanded")).toBe("false"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); const datePicker = getByRole("dialog"); expect(datePicker.getAttribute("aria-modal")).toBe("true"); expect(calendarAction.getAttribute("aria-expanded")).toBe("true"); - expect(document.getElementById(calendarAction.getAttribute("aria-describedby"))).toBeTruthy(); + const ariaDescribedBy = calendarAction.getAttribute("aria-describedby"); + ariaDescribedBy != null && expect(document.getElementById(ariaDescribedBy)).toBeTruthy(); expect( calendarAction.getAttribute("aria-describedby") === calendarAction.getAttribute("aria-controls") ).toBeTruthy(); - await userEvent.type(calendarAction, "{esc}"); + userEvent.type(calendarAction, "{esc}"); expect(calendarAction.getAttribute("aria-expanded")).toBe("false"); }); - - test("Chooses the correct year when two digit format", async () => { + test("Chooses the correct year when two digit format", () => { const { getByText, getByRole, getAllByText } = render( ); const input = getByRole("textbox") as HTMLInputElement; const calendarAction = getByRole("combobox"); expect(input.value).toBe("21-10-80"); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect(getByText("21").getAttribute("aria-selected")).toBe("true"); expect(getByText("October 1980")).toBeTruthy(); - await userEvent.type(calendarAction, "{esc}"); + userEvent.type(calendarAction, "{esc}"); fireEvent.change(input, { target: { value: "21-10-10" } }); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect(getByText("October 1910")).toBeTruthy(); - await userEvent.click(getByText("October 1910")); - await userEvent.click(getByText("2010")); - await userEvent.click(getAllByText("1")[0]); + userEvent.click(getByText("October 1910")); + userEvent.click(getByText("2010")); + const day1 = getAllByText("1")[0]; + day1 != null && userEvent.click(day1); expect(input.value).toBe("01-10-10"); - await userEvent.type(calendarAction, "{esc}"); + userEvent.type(calendarAction, "{esc}"); fireEvent.change(input, { target: { value: "21-10-80" } }); - await userEvent.click(calendarAction); + userEvent.click(calendarAction); expect(getByText("October 2080")).toBeTruthy(); }); }); diff --git a/packages/lib/src/date-input/DateInput.tsx b/packages/lib/src/date-input/DateInput.tsx index 332de4cbfd..20d13c9067 100644 --- a/packages/lib/src/date-input/DateInput.tsx +++ b/packages/lib/src/date-input/DateInput.tsx @@ -100,7 +100,7 @@ const DxcDateInput = forwardRef( setInnerValue(newValue); } setLastValidYear(newDate.get("year")); - if (newDate?.set("day", newDate.get("date")).toJSON()) { + if (newDate.set("day", newDate.get("date")).toJSON()) { onChange?.({ value: newValue, date: newDate.toDate(), @@ -131,7 +131,7 @@ const DxcDateInput = forwardRef( }); } else { onChange?.(callbackParams); - setLastValidYear((validYear) => dayjsDate?.get("year") ?? validYear); + setLastValidYear((validYear) => dayjsDate.get("year") ?? validYear); setDayjsDate(dayjs(null)); } }; @@ -189,11 +189,11 @@ const DxcDateInput = forwardRef( event.stopPropagation(); } closeCalendar(); - dateRef?.current?.getElementsByTagName("input")[0]?.focus(); + dateRef.current?.getElementsByTagName("input")[0]?.focus(); } }; const handleDatePickerOnBlur = (event: FocusEvent) => { - if (!event?.currentTarget.contains(event.relatedTarget)) { + if (!event.currentTarget.contains(event.relatedTarget)) { closeCalendar(); } }; @@ -213,13 +213,13 @@ const DxcDateInput = forwardRef( useEffect(() => { if (!disabled) { - const actionButtonRef = dateRef?.current?.querySelector("[aria-label='Select date']"); - actionButtonRef?.setAttribute("aria-haspopup", "true"); - actionButtonRef?.setAttribute("role", "combobox"); - actionButtonRef?.setAttribute("aria-expanded", `${isOpen}`); - actionButtonRef?.setAttribute("aria-controls", calendarId); + const actionButtonElement = dateRef.current?.querySelector("[aria-label='Select date']"); + actionButtonElement?.setAttribute("aria-haspopup", "true"); + actionButtonElement?.setAttribute("role", "combobox"); + actionButtonElement?.setAttribute("aria-expanded", `${isOpen}`); + actionButtonElement?.setAttribute("aria-controls", calendarId); if (isOpen) { - actionButtonRef?.setAttribute("aria-describedby", calendarId); + actionButtonElement?.setAttribute("aria-describedby", calendarId); } } }, [isOpen, disabled, calendarId]); diff --git a/packages/lib/src/dialog/Dialog.test.tsx b/packages/lib/src/dialog/Dialog.test.tsx index 829ac4aa42..7909c8d977 100644 --- a/packages/lib/src/dialog/Dialog.test.tsx +++ b/packages/lib/src/dialog/Dialog.test.tsx @@ -39,12 +39,10 @@ describe("Dialog component tests", () => { expect(getByRole("dialog").getAttribute("aria-modal")).toBe("true"); expect(getByText("dialog-text")).toBeTruthy(); }); - test("Dialog renders without close button", () => { const { queryByRole } = render(dialog-text); expect(queryByRole("button")).toBeFalsy(); }); - test("Dialog renders with aria-modal false when overlay is not used", () => { const { getByRole } = render( @@ -54,7 +52,6 @@ describe("Dialog component tests", () => { expect(getByRole("dialog")).toBeTruthy(); expect(getByRole("dialog").getAttribute("aria-modal")).toBe("false"); }); - test("Calls correct function onCloseClick", () => { const onCloseClick = jest.fn(); const { getByRole } = render(dialog-text); @@ -62,15 +59,13 @@ describe("Dialog component tests", () => { fireEvent.click(closeButton); expect(onCloseClick).toHaveBeenCalled(); }); - test("Calls correct function onCloseClick when 'Escape' key is pressed", () => { const onCloseClick = jest.fn(); const { getByRole } = render(dialog-text); fireEvent.keyDown(getByRole("dialog"), { key: "Escape", code: "Escape", keyCode: 27, charCode: 27 }); expect(onCloseClick).toHaveBeenCalled(); }); - - test("Does not call function onCloseClick when 'Escape' key is pressed while a child popover is opened", async () => { + test("Does not call function onCloseClick when 'Escape' key is pressed while a child popover is opened", () => { const onCloseClick = jest.fn(); const { getByRole } = render( @@ -78,8 +73,9 @@ describe("Dialog component tests", () => { ); const calendarAction = getByRole("combobox"); - await userEvent.click(calendarAction); - fireEvent.keyDown(document.activeElement, { key: "Escape", code: "Escape", keyCode: 27, charCode: 27 }); + userEvent.click(calendarAction); + document.activeElement != null && + fireEvent.keyDown(document.activeElement, { key: "Escape", code: "Escape", keyCode: 27, charCode: 27 }); expect(onCloseClick).not.toHaveBeenCalled(); }); }); @@ -96,7 +92,6 @@ describe("Dialog component: Focus lock tests", () => { fireEvent.keyDown(dialog, { key: "Tab", shiftKey: true }); expect(document.activeElement).toEqual(button); }); - test("Autofocus with Button component", () => { const { getAllByRole } = render( @@ -105,9 +100,8 @@ describe("Dialog component: Focus lock tests", () => { ); const button = getAllByRole("button")[0]; expect(document.activeElement).toEqual(button); - expect(button.getAttribute("aria-label")).not.toBe("Close dialog"); + expect(button?.getAttribute("aria-label")).not.toBe("Close dialog"); }); - test("Autofocus with Card component", () => { const { getByRole } = render( @@ -117,7 +111,6 @@ describe("Dialog component: Focus lock tests", () => { const card = getByRole("link"); expect(document.activeElement).toEqual(card); }); - test("Autofocus with Checkbox component", () => { const { getByRole } = render( @@ -127,7 +120,6 @@ describe("Dialog component: Focus lock tests", () => { const checkbox = getByRole("checkbox"); expect(document.activeElement).toEqual(checkbox); }); - test("Autofocus with Link component", () => { const { getByRole } = render( @@ -137,7 +129,6 @@ describe("Dialog component: Focus lock tests", () => { const link = getByRole("link"); expect(document.activeElement).toEqual(link); }); - test("Autofocus with RadioGroup component", () => { const { getAllByRole } = render( @@ -147,7 +138,6 @@ describe("Dialog component: Focus lock tests", () => { const checkedRadio = getAllByRole("radio")[0]; expect(document.activeElement).toEqual(checkedRadio); }); - test("Autofocus with Select component", () => { const { getByRole } = render( @@ -157,7 +147,6 @@ describe("Dialog component: Focus lock tests", () => { const select = getByRole("combobox"); expect(document.activeElement).toEqual(select); }); - test("Autofocus with Slider component", () => { const { getByRole } = render( @@ -167,7 +156,6 @@ describe("Dialog component: Focus lock tests", () => { const slider = getByRole("slider"); expect(document.activeElement).toEqual(slider); }); - test("Autofocus with Switch component", () => { const { getByRole } = render( @@ -177,7 +165,6 @@ describe("Dialog component: Focus lock tests", () => { const switchButton = getByRole("switch"); expect(document.activeElement).toEqual(switchButton); }); - test("Autofocus with Text Input component", () => { const { getByRole } = render( @@ -187,7 +174,6 @@ describe("Dialog component: Focus lock tests", () => { const input = getByRole("textbox"); expect(document.activeElement).toEqual(input); }); - test("Autofocus with Textarea component", () => { const { getByRole } = render( @@ -197,7 +183,6 @@ describe("Dialog component: Focus lock tests", () => { const textarea = getByRole("textbox"); expect(document.activeElement).toEqual(textarea); }); - test("Negative tabindex elements are not automatically focused, even if it is enabled and a valid focusable item (programatically and by click)", () => { const { getAllByRole, getByRole } = render( @@ -213,7 +198,6 @@ describe("Dialog component: Focus lock tests", () => { userEvent.tab(); expect(document.activeElement).toEqual(inputs[1]); }); - test("Focus jumps disabled components and negative tabIndexes when autofocusing first item", () => { const { getAllByRole } = render( @@ -233,7 +217,6 @@ describe("Dialog component: Focus lock tests", () => { const textarea = getAllByRole("textbox")[2]; expect(document.activeElement).toEqual(textarea); }); - test("Focus jumps from last element to the first", () => { const { getByRole } = render( @@ -251,7 +234,6 @@ describe("Dialog component: Focus lock tests", () => { userEvent.tab(); expect(document.activeElement).toEqual(textarea); }); - test("'display: none;', 'visibility: hidden;' and 'type = 'hidden'' elements are never autofocused", () => { const { getByRole } = render( @@ -265,7 +247,6 @@ describe("Dialog component: Focus lock tests", () => { userEvent.tab(); expect(document.activeElement).toEqual(closeAction); }); - test("Focus gets trapped in the Dialog when there are not focusable elements inside until it is closed", () => { const { getAllByRole, getByRole } = render( <> @@ -286,7 +267,7 @@ describe("Dialog component: Focus lock tests", () => { fireEvent.keyDown(dialog, { key: "Tab", shiftKey: true }); expect(document.activeElement).not.toEqual(inputs[0]); }); - test("Focus travels correctly in a complex tab sequence", async () => { + test("Focus travels correctly in a complex tab sequence", () => { const { getAllByRole, queryByRole, getByRole } = render( @@ -307,13 +288,14 @@ describe("Dialog component: Focus lock tests", () => { ); const select = getAllByRole("combobox")[0]; expect(document.activeElement).toEqual(select); - fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 }); + select != null && fireEvent.keyDown(select, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 }); expect(queryByRole("listbox")).toBeTruthy(); userEvent.tab(); userEvent.tab(); - await userEvent.keyboard('{Enter}'); + userEvent.keyboard("{Enter}"); expect(getAllByRole("dialog")[1]).toBeTruthy(); - userEvent.click(getAllByRole("dialog")[0]); + const dialog = getAllByRole("dialog")[0]; + dialog != null && userEvent.click(dialog); userEvent.tab(); userEvent.tab(); userEvent.tab(); diff --git a/packages/lib/src/divider/Divider.test.tsx b/packages/lib/src/divider/Divider.test.tsx index cfc7708314..762a19dce8 100644 --- a/packages/lib/src/divider/Divider.test.tsx +++ b/packages/lib/src/divider/Divider.test.tsx @@ -5,23 +5,19 @@ describe("Divider Component", () => { test("Default renders horizontal divider correctly", () => { const { container } = render(); const divider = container.querySelector("hr"); - - expect(divider.getAttribute("aria-orientation")).toBe("horizontal"); + expect(divider?.getAttribute("aria-orientation")).toBe("horizontal"); }); - test("Renders vertical divider correctly", () => { const { container } = render(); const divider = container.querySelector("hr"); - expect(divider.getAttribute("aria-orientation")).toBe("vertical"); + expect(divider?.getAttribute("aria-orientation")).toBe("vertical"); }); - test("Renders divider as a decorative resource by default", () => { const { container } = render(); const divider = container.querySelector("hr"); expect(divider).toBeTruthy(); - expect(divider.getAttribute("aria-hidden")).toBe("true"); + expect(divider?.getAttribute("aria-hidden")).toBe("true"); }); - test("Renders divider as a separator if it is not decorative", () => { const { getByRole } = render(); const divider = getByRole("separator"); diff --git a/packages/lib/src/dropdown/Dropdown.stories.tsx b/packages/lib/src/dropdown/Dropdown.stories.tsx index bd7a581f40..2f1f9c57d5 100644 --- a/packages/lib/src/dropdown/Dropdown.stories.tsx +++ b/packages/lib/src/dropdown/Dropdown.stories.tsx @@ -410,7 +410,8 @@ export const Chromatic: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const buttonList = canvas.getAllByRole("button"); - await userEvent.click(buttonList[buttonList.length - 1]); + const lastButton = buttonList[buttonList.length - 1]; + lastButton != null && (await userEvent.click(lastButton)); }, }; @@ -419,7 +420,8 @@ export const OpinionatedThemed: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const buttonList = canvas.getAllByRole("button"); - await userEvent.click(buttonList[buttonList.length - 1]); + const lastButton = buttonList[buttonList.length - 1]; + lastButton != null && (await userEvent.click(lastButton)); }, }; @@ -427,7 +429,8 @@ export const MenuStates: Story = { render: DropdownListStates, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("button")[0]); + const dropdownTrigger = canvas.getAllByRole("button")[0]; + dropdownTrigger != null && (await userEvent.click(dropdownTrigger)); }, }; diff --git a/packages/lib/src/dropdown/Dropdown.test.tsx b/packages/lib/src/dropdown/Dropdown.test.tsx index 061af92d8e..5ea0c8c273 100644 --- a/packages/lib/src/dropdown/Dropdown.test.tsx +++ b/packages/lib/src/dropdown/Dropdown.test.tsx @@ -51,7 +51,6 @@ describe("Dropdown component tests", () => { expect(menu.getAttribute("aria-labelledby")).toBe(dropdown.id); expect(getAllByRole("menuitem").length).toBe(4); }); - test("Button trigger opens and closes the menu options when clicked", async () => { const onSelectOption = jest.fn(); const { getByRole, queryByRole, getByText } = render( @@ -68,7 +67,6 @@ describe("Dropdown component tests", () => { await userEvent.click(dropdown); expect(queryByRole("menu")).toBeFalsy(); }); - test("Button trigger is not interactive when disabled", async () => { const onSelectOption = jest.fn(); const { getByRole, queryByRole, queryByText } = render( @@ -83,7 +81,6 @@ describe("Dropdown component tests", () => { expect(queryByRole("menu")).toBeFalsy(); expect(dropdown.getAttribute("aria-expanded")).toBeNull(); }); - test("onSelectOption function is called correctly when an option is clicked", async () => { const onSelectOption = jest.fn(); const { getByText } = render( @@ -95,7 +92,6 @@ describe("Dropdown component tests", () => { await userEvent.click(option); expect(onSelectOption).toHaveBeenCalledWith("4"); }); - test("When expandOnHover is true, the dropdown trigger shows and hides the menu when it is hovered", () => { const onSelectOption = jest.fn(); const { queryByText, getByRole, queryByRole } = render( @@ -109,7 +105,6 @@ describe("Dropdown component tests", () => { expect(document.activeElement === menu).toBeTruthy(); expect(menu.getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-0`); }); - test("The menu is closed when the dropdown loses the focus (blur)", async () => { const onSelectOption = jest.fn(); const { getByRole, queryByRole } = render( @@ -121,7 +116,6 @@ describe("Dropdown component tests", () => { fireEvent.blur(getByRole("menu")); expect(queryByRole("menu")).toBeFalsy(); }); - test("Menu button key events - Arrow up opens the list and moves the focus to the last menu item", () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -134,7 +128,6 @@ describe("Dropdown component tests", () => { expect(document.activeElement === menu).toBeTruthy(); expect(getByRole("menu").getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-3`); }); - test("Menu button key events - Arrow down opens the list and moves the focus to the first menu item", () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -147,7 +140,6 @@ describe("Dropdown component tests", () => { expect(document.activeElement === menu).toBeTruthy(); expect(getByRole("menu").getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-0`); }); - test("Menu button key events - Enter opens the list and moves the focus to the first menu item", () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -160,7 +152,6 @@ describe("Dropdown component tests", () => { expect(document.activeElement === menu).toBeTruthy(); expect(getByRole("menu").getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-0`); }); - test("Menu button key events - Space opens the list and moves the focus to the first menu item", () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -173,7 +164,6 @@ describe("Dropdown component tests", () => { expect(document.activeElement === menu).toBeTruthy(); expect(getByRole("menu").getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-0`); }); - test("Menu key events - Arrow up moves the focus to the previous menu item", () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -187,7 +177,6 @@ describe("Dropdown component tests", () => { fireEvent.keyDown(menu, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 }); expect(onSelectOption).toHaveBeenCalledWith("3"); }); - test("Menu key events - Arrow up, if focus is on the first menu item, moves focus to the last menu item.", async () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -201,7 +190,6 @@ describe("Dropdown component tests", () => { fireEvent.keyDown(menu, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 }); expect(onSelectOption).toHaveBeenCalledWith("4"); }); - test("Menu key events - Arrow down moves the focus to the next menu item", async () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -216,7 +204,6 @@ describe("Dropdown component tests", () => { fireEvent.keyDown(menu, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 }); expect(onSelectOption).toHaveBeenCalledWith("3"); }); - test("Menu key events - Arrow down, if focus is on the last menu item, moves focus to the first menu item. ", () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -230,7 +217,6 @@ describe("Dropdown component tests", () => { fireEvent.keyDown(menu, { key: "Enter", code: "Enter", keyCode: 13, charCode: 13 }); expect(onSelectOption).toHaveBeenCalledWith("1"); }); - test("Menu key events - Enter key selects the current focused item and closes the menu", async () => { const onSelectOption = jest.fn(); const { getByRole, queryByRole } = render( @@ -242,7 +228,6 @@ describe("Dropdown component tests", () => { expect(queryByRole("menu")).toBeFalsy(); expect(document.activeElement === getByRole("button")).toBeTruthy(); }); - test("Menu key events - Esc closes the menu and sets focus on the menu button", async () => { const onSelectOption = jest.fn(); const { getByRole, queryByRole } = render( @@ -253,7 +238,6 @@ describe("Dropdown component tests", () => { expect(queryByRole("menu")).toBeFalsy(); expect(document.activeElement === getByRole("button")).toBeTruthy(); }); - test("Menu key events - Home moves the focus to the first menu item", () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -265,7 +249,6 @@ describe("Dropdown component tests", () => { fireEvent.keyDown(menu, { key: "Home", code: "Home", keyCode: 36, charCode: 36 }); expect(menu.getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-0`); }); - test("Menu key events - End moves the focus to the last menu item", async () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -277,7 +260,6 @@ describe("Dropdown component tests", () => { fireEvent.keyDown(menu, { key: "End", code: "End", keyCode: 35, charCode: 35 }); expect(menu.getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-3`); }); - test("Menu key events - PageUp moves the focus to the first menu item", () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -289,7 +271,6 @@ describe("Dropdown component tests", () => { fireEvent.keyDown(menu, { key: "PageUp", code: "PageUp", keyCode: 33, charCode: 33 }); expect(menu.getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-0`); }); - test("Menu key events - PageDown moves the focus to the last menu item", async () => { const onSelectOption = jest.fn(); const { getByRole } = render( @@ -301,7 +282,6 @@ describe("Dropdown component tests", () => { fireEvent.keyDown(menu, { key: "PageDown", code: "PageDown", keyCode: 34, charCode: 34 }); expect(menu.getAttribute("aria-activedescendant")).toBe(`${menu.id}-option-3`); }); - test("Menu key events - Tab closes the menu and sets focus to the next element", async () => { const onSelectOption = jest.fn(); const { getByRole, queryByRole } = render( diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index cd55a8c4a4..070005679d 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -45,11 +45,9 @@ const DxcDropdown = ({ }; const handleMenuItemOnClick = useCallback( (value?: string) => { - if (value) { - onSelectOption(value); - handleOnCloseMenu(); - triggerRef.current?.focus(); - } + if (value) onSelectOption(value); + handleOnCloseMenu(); + triggerRef.current?.focus(); }, [onSelectOption] ); @@ -143,7 +141,7 @@ const DxcDropdown = ({ ); useLayoutEffect(() => { - const visualFocusedMenuItem = menuRef?.current?.querySelectorAll("[role='menuitem']")[visualFocusIndex]; + const visualFocusedMenuItem = menuRef.current?.querySelectorAll("[role='menuitem']")[visualFocusIndex]; visualFocusedMenuItem?.scrollIntoView?.({ block: "nearest", inline: "start", @@ -231,9 +229,10 @@ const sizes = { }; const calculateWidth = (margin: DropdownPropsType["margin"], size: DropdownPropsType["size"]) => - size === "fillParent" + size != null && + (size === "fillParent" ? `calc(${sizes[size]} - ${getMargin(margin, "left")} - ${getMargin(margin, "right")})` - : size && sizes[size]; + : sizes[size]); const DropdownContainer = styled.div<{ margin: DropdownPropsType["margin"]; diff --git a/packages/lib/src/file-input/FileInput.test.tsx b/packages/lib/src/file-input/FileInput.test.tsx index c2d477ff3e..c38911d7a3 100644 --- a/packages/lib/src/file-input/FileInput.test.tsx +++ b/packages/lib/src/file-input/FileInput.test.tsx @@ -279,7 +279,7 @@ describe("FileInput component tests", () => { expect(getByText("file2.txt")).toBeTruthy(); expect(getByText("Error message")).toBeTruthy(); const removeBtn = getAllByRole("button")[1]; - userEvent.click(removeBtn); + removeBtn != null && userEvent.click(removeBtn); expect(callbackFile).toHaveBeenCalledWith([ { error: "Error message", diff --git a/packages/lib/src/file-input/FileInput.tsx b/packages/lib/src/file-input/FileInput.tsx index 460b30dd30..0a8d1a64ec 100644 --- a/packages/lib/src/file-input/FileInput.tsx +++ b/packages/lib/src/file-input/FileInput.tsx @@ -7,14 +7,14 @@ import FileInputPropsType, { FileData, RefType } from "./types"; import HalstackContext, { HalstackLanguageContext } from "../HalstackContext"; const getFilePreview = async (file: File): Promise => { - if (file?.type?.includes("video")) return "filled_movie"; - else if (file?.type?.includes("audio")) return "music_video"; - else if (file?.type?.includes("image")) { + if (file.type.includes("video")) return "filled_movie"; + else if (file.type.includes("audio")) return "music_video"; + else if (file.type.includes("image")) { return new Promise((resolve) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = (e) => { - resolve(e?.target?.result as string); + resolve(e.target?.result as string); }; }); } else return "draft"; @@ -110,20 +110,20 @@ const DxcFileInput = forwardRef( ); const handleClick = () => { - document?.getElementById(fileInputId)?.click(); + document.getElementById(fileInputId)?.click(); }; const handleDrag = (e: DragEvent) => { e.preventDefault(); e.stopPropagation(); }; const handleDragIn = (e: DragEvent) => { - if (e?.dataTransfer?.items?.length > 0) { + if (e.dataTransfer.items.length > 0) { setIsDragging(true); } }; const handleDragOut = (e: DragEvent) => { // only if dragged items leave container (outside, not to children) - if (!e.currentTarget?.contains(e.relatedTarget as HTMLDivElement)) { + if (!e.currentTarget.contains(e.relatedTarget as HTMLDivElement)) { setIsDragging(false); } }; @@ -132,7 +132,7 @@ const DxcFileInput = forwardRef( e.stopPropagation(); setIsDragging(false); const filesObject = e.dataTransfer.files; - if (filesObject?.length > 0) { + if (filesObject.length > 0) { const filesArray = Array.from(filesObject); addFile(filesArray); } @@ -268,7 +268,7 @@ const DxcFileInput = forwardRef( )} {mode === "file" && !multiple && files.length === 1 && files[0]?.error && ( - {files[0]?.error} + {files[0].error} )} diff --git a/packages/lib/src/file-input/FileItem.tsx b/packages/lib/src/file-input/FileItem.tsx index 18fe6d2a43..1175f483a9 100644 --- a/packages/lib/src/file-input/FileItem.tsx +++ b/packages/lib/src/file-input/FileItem.tsx @@ -19,13 +19,13 @@ const FileItem = ({ const colorsTheme = useContext(HalstackContext); const translatedLabels = useContext(HalstackLanguageContext); - const getIconAriaLabel = () => (type?.includes("video") ? "video" : type?.includes("audio") ? "audio" : "file"); + const getIconAriaLabel = () => (type.includes("video") ? "video" : type.includes("audio") ? "audio" : "file"); return ( {showPreview && - (type?.includes("image") ? ( + (type.includes("image") ? ( ) : ( diff --git a/packages/lib/src/footer/Footer.accessibility.test.tsx b/packages/lib/src/footer/Footer.accessibility.test.tsx index 1dd5cec8a0..f72c023040 100644 --- a/packages/lib/src/footer/Footer.accessibility.test.tsx +++ b/packages/lib/src/footer/Footer.accessibility.test.tsx @@ -1,13 +1,10 @@ import { render } from "@testing-library/react"; -import { axe } from "../../test/accessibility/axe-helper"; +import { axe, formatRules } from "../../test/accessibility/axe-helper"; import { disabledRules as rules } from "../../test/accessibility/rules/specific/footer/disabledRules"; import DxcFooter from "./Footer"; const disabledRules = { - rules: rules.reduce((rulesObj, rule) => { - rulesObj[rule] = { enabled: false }; - return rulesObj; - }, {}), + rules: formatRules(rules), }; const social = [ diff --git a/packages/lib/src/footer/Footer.stories.tsx b/packages/lib/src/footer/Footer.stories.tsx index 7df8d635c2..f7d86038d6 100644 --- a/packages/lib/src/footer/Footer.stories.tsx +++ b/packages/lib/src/footer/Footer.stories.tsx @@ -228,7 +228,7 @@ export const FooterTooltipFirst: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const link = canvas.getAllByRole("link")[0]; - await userEvent.hover(link); + link != null && (await userEvent.hover(link)); }, }; @@ -237,6 +237,6 @@ export const FooterTooltipSecond: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const link = canvas.getAllByRole("link")[1]; - await userEvent.hover(link); + link != null && (await userEvent.hover(link)); }, }; diff --git a/packages/lib/src/footer/Footer.test.tsx b/packages/lib/src/footer/Footer.test.tsx index bd8695bcbf..94e6a2226b 100644 --- a/packages/lib/src/footer/Footer.test.tsx +++ b/packages/lib/src/footer/Footer.test.tsx @@ -20,24 +20,20 @@ describe("Footer component tests", () => { const { getByTitle } = render(); expect(getByTitle("DXC Logo")).toBeTruthy(); }); - test("Footer renders with social links", () => { const { getByRole } = render(); const socialIcon = getByRole("link"); expect(socialIcon.getAttribute("href")).toBe("https://www.test.com/social"); }); - test("Footer renders with bottom links", () => { const { getByText } = render(); const link = getByText("bottom-link-text"); expect(link.getAttribute("href")).toBe("https://www.test.com/bottom"); }); - test("Footer renders with copyright text", () => { const { getByText } = render(); expect(getByText("test-copyright")).toBeTruthy(); }); - test("Footer renders with correct children", () => { // We need to force the offsetWidth value Object.defineProperty(HTMLElement.prototype, "offsetWidth", { configurable: true, value: 1024 }); @@ -48,7 +44,6 @@ describe("Footer component tests", () => { ); expect(getByText("footer-child-text")).toBeTruthy(); }); - test("Footer renders with children in mobile", () => { // 425 is mobile width Object.defineProperty(HTMLElement.prototype, "offsetWidth", { configurable: true, value: 425 }); @@ -61,7 +56,6 @@ describe("Footer component tests", () => { expect(queryByText("footer-child-text")).toBeTruthy(); }); - test("Footer is fully rendered", () => { Object.defineProperty(HTMLElement.prototype, "offsetWidth", { configurable: true, value: 1024 }); @@ -71,8 +65,8 @@ describe("Footer component tests", () => { ); const socialIcon = getAllByRole("link")[0]; - expect(socialIcon.getAttribute("href")).toBe("https://www.test.com/social"); - expect(socialIcon.getAttribute("aria-label")).toBe("test"); + expect(socialIcon?.getAttribute("href")).toBe("https://www.test.com/social"); + expect(socialIcon?.getAttribute("aria-label")).toBe("test"); const bottomLink = getByText("bottom-link-text"); expect(bottomLink.getAttribute("href")).toBe("https://www.test.com/bottom"); expect(getByText("test-copyright")).toBeTruthy(); diff --git a/packages/lib/src/header/Header.accessibility.test.tsx b/packages/lib/src/header/Header.accessibility.test.tsx index da40b763ad..de590f5913 100644 --- a/packages/lib/src/header/Header.accessibility.test.tsx +++ b/packages/lib/src/header/Header.accessibility.test.tsx @@ -1,15 +1,12 @@ import { render } from "@testing-library/react"; -import { axe } from "../../test/accessibility/axe-helper"; +import { axe, formatRules } from "../../test/accessibility/axe-helper"; import { disabledRules as rules } from "../../test/accessibility/rules/specific/header/disabledRules"; import DxcFlex from "../flex/Flex"; import DxcLink from "../link/Link"; import DxcHeader from "./Header"; const disabledRules = { - rules: rules.reduce((rulesObj, rule) => { - rulesObj[rule] = { enabled: false }; - return rulesObj; - }, {}), + rules: formatRules(rules), }; const iconSVG = ( diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx index eff9f0d3e2..e7b0ccf14a 100644 --- a/packages/lib/src/header/Header.stories.tsx +++ b/packages/lib/src/header/Header.stories.tsx @@ -298,6 +298,6 @@ export const ResponsiveHeaderTooltip: Story = { await waitFor(() => canvas.findByText("Menu")); await userEvent.click(canvas.getByText("Menu")); const closeButton = canvas.getAllByRole("button")[1]; - await userEvent.hover(closeButton); + closeButton != null && (await userEvent.hover(closeButton)); }, }; diff --git a/packages/lib/src/header/Header.test.tsx b/packages/lib/src/header/Header.test.tsx index b109c59dbf..b6f15d0e57 100644 --- a/packages/lib/src/header/Header.test.tsx +++ b/packages/lib/src/header/Header.test.tsx @@ -10,12 +10,10 @@ describe("Header component tests", () => { })), }); }); - test("Header renders with default logo", () => { const { getByTitle } = render(); expect(getByTitle("DXC Logo")).toBeTruthy(); }); - test("Call correct function on logo click", () => { const onClick = jest.fn(); const { getByTitle } = render(); @@ -23,7 +21,6 @@ describe("Header component tests", () => { fireEvent.click(logo); expect(onClick).toHaveBeenCalled(); }); - test("Header renders with correct children", () => { // We need to force the offsetWidth value Object.defineProperty(HTMLElement.prototype, "offsetWidth", { configurable: true, value: 1024 }); @@ -31,7 +28,6 @@ describe("Header component tests", () => { const { getByText } = render(header-child-text

    }>
    ); expect(getByText("header-child-text")).toBeTruthy(); }); - test("Header renders menu button in mobile", () => { Object.defineProperty(HTMLElement.prototype, "offsetWidth", { configurable: true, value: 425 }); Object.defineProperty(window, "matchMedia", { diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx index 55bf25d3ac..ba23eec1de 100644 --- a/packages/lib/src/header/Header.tsx +++ b/packages/lib/src/header/Header.tsx @@ -82,7 +82,7 @@ const DxcHeader = ({ ); const headerResponsiveLogo = useMemo( - () => getLogoElement(colorsTheme.header?.logoResponsive, translatedLabels.formFields.logoAlternativeText), + () => getLogoElement(colorsTheme.header.logoResponsive, translatedLabels.formFields.logoAlternativeText), [colorsTheme, translatedLabels] ); diff --git a/packages/lib/src/heading/Heading.test.tsx b/packages/lib/src/heading/Heading.test.tsx index d56871da09..de11cb70b8 100644 --- a/packages/lib/src/heading/Heading.test.tsx +++ b/packages/lib/src/heading/Heading.test.tsx @@ -7,67 +7,56 @@ describe("Heading component tests", () => { expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 1 })).toBeTruthy(); }); - test("Heading renders with level 1", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 1 })).toBeTruthy(); }); - test("Heading renders with level 2", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 2 })).toBeTruthy(); }); - test("Heading renders with level 3", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 3 })).toBeTruthy(); }); - test("Heading renders with level 4", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 4 })).toBeTruthy(); }); - test("Heading renders with level 5", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 5 })).toBeTruthy(); }); - test("Heading renders with default level and as h5", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 5 })).toBeTruthy(); }); - test("Heading renders with level 1 and as h5", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 5 })).toBeTruthy(); }); - test("Heading renders with level 2 and as h4", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 4 })).toBeTruthy(); }); - test("Heading renders with level 3 and as h2", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 2 })).toBeTruthy(); }); - test("Heading renders with level 4 and as h3", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); expect(getByRole("heading", { level: 3 })).toBeTruthy(); }); - test("Heading renders with level 5 as h4", () => { const { getByText, getByRole } = render(); expect(getByText("my-heading-test")).toBeTruthy(); diff --git a/packages/lib/src/link/Link.stories.tsx b/packages/lib/src/link/Link.stories.tsx index 8565fb03ec..a6ea36ce92 100644 --- a/packages/lib/src/link/Link.stories.tsx +++ b/packages/lib/src/link/Link.stories.tsx @@ -1,4 +1,3 @@ -import { fireEvent, userEvent, within } from "@storybook/test"; import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; diff --git a/packages/lib/src/link/Link.test.tsx b/packages/lib/src/link/Link.test.tsx index 7f5ce9f3a0..a8edcb733a 100644 --- a/packages/lib/src/link/Link.test.tsx +++ b/packages/lib/src/link/Link.test.tsx @@ -6,12 +6,10 @@ describe("Link component tests", () => { const { getByText } = render(Link); expect(getByText("Link")).toBeTruthy(); }); - test("Link renders with correct href", () => { const { getByRole } = render(Link); expect(getByRole("link").getAttribute("href")).toEqual("/testPage"); }); - test("Link renders with correct disabled state", () => { const { getByText } = render( @@ -26,7 +24,6 @@ describe("Link component tests", () => { ); expect(getByTextLinkButton("LinkButton").hasAttribute("onclick")).toBeFalsy(); }); - test("Link open new tab", () => { const { getByRole } = render( @@ -35,7 +32,6 @@ describe("Link component tests", () => { ); expect(getByRole("link").getAttribute("target")).toEqual("_blank"); }); - test("Link onClick called", () => { const onClick = jest.fn(); const { getByText } = render(Link); @@ -43,7 +39,6 @@ describe("Link component tests", () => { fireEvent.click(link); expect(onClick).toHaveBeenCalled(); }); - test("Disabled link onClick not called", () => { const onClick = jest.fn(); const { getByText } = render( From c36d9f611c8d65b50e07d1b1fb230174f198c30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:43:21 +0100 Subject: [PATCH 28/41] Small fixes --- packages/lib/src/breadcrumbs/Item.tsx | 6 +++--- packages/lib/src/image/Image.tsx | 24 ++++++++++-------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/packages/lib/src/breadcrumbs/Item.tsx b/packages/lib/src/breadcrumbs/Item.tsx index 1b84a48c65..472e027690 100644 --- a/packages/lib/src/breadcrumbs/Item.tsx +++ b/packages/lib/src/breadcrumbs/Item.tsx @@ -15,9 +15,9 @@ const Item = ({ isCurrentPage = false, href, label, onClick }: ItemPropsType) => }; const handleOnClick = (event: MouseEvent) => { - if (onClick && href) { - event.preventDefault(); - onClick(href); + event.preventDefault(); + if (href) { + onClick?.(href); } }; diff --git a/packages/lib/src/image/Image.tsx b/packages/lib/src/image/Image.tsx index 1cd0298014..01c1d9a898 100644 --- a/packages/lib/src/image/Image.tsx +++ b/packages/lib/src/image/Image.tsx @@ -21,9 +21,15 @@ const CaptionContainer = styled.figcaption` line-height: ${(props) => props.theme.captionLineHeight}; `; -const CaptionWrapper = ({ condition, wrapper, children }: CaptionWrapperProps) => ( - <>{condition ? wrapper(children) : children} -); +const CaptionWrapper = ({ caption, children }: { caption: ImagePropsType["caption"]; children: ReactNode }) => + caption != null ? ( +
    + {children} + {caption} +
    + ) : ( + children + ); export default function DxcImage({ alt, @@ -41,19 +47,9 @@ export default function DxcImage({ }: ImagePropsType) { const colorsTheme = useContext(HalstackContext); - const figureWrapper = useCallback( - (children: ReactNode) => ( -
    - {children} - {caption} -
    - ), - [caption] - ); - return ( - + {alt} Date: Mon, 23 Dec 2024 12:18:18 +0100 Subject: [PATCH 29/41] Fixed TypeScript problems in stories and tests --- packages/lib/.eslintrc.js | 3 + packages/lib/src/BackgroundColorContext.tsx | 48 ++--- .../lib/src/data-grid/DataGrid.stories.tsx | 65 ++++--- packages/lib/src/data-grid/types.ts | 2 +- packages/lib/src/data-grid/utils.tsx | 27 ++- packages/lib/src/nav-tabs/NavTabs.test.tsx | 18 +- .../lib/src/number-input/NumberInput.test.tsx | 100 +++++------ packages/lib/src/paginator/Paginator.test.tsx | 42 ++--- .../src/password-input/PasswordInput.test.tsx | 4 +- .../lib/src/radio-group/RadioGroup.test.tsx | 135 +++++++------- .../ResultsetTable.accessibility.test.tsx | 7 +- .../ResultsetTable.stories.tsx | 18 +- .../resultset-table/ResultsetTable.test.tsx | 40 +++-- .../src/select/Select.accessibility.test.tsx | 7 +- packages/lib/src/select/Select.stories.tsx | 12 +- packages/lib/src/select/Select.test.tsx | 164 ++++++++++-------- packages/lib/src/sidenav/Sidenav.stories.tsx | 2 +- packages/lib/src/sidenav/Sidenav.test.tsx | 8 +- packages/lib/src/slider/Slider.stories.tsx | 2 +- .../src/switch/Switch.accessibility.test.tsx | 7 +- packages/lib/src/switch/Switch.test.tsx | 2 +- .../src/table/Table.accessibility.test.tsx | 7 +- packages/lib/src/table/Table.stories.tsx | 2 +- packages/lib/src/table/Table.test.tsx | 4 +- packages/lib/src/tabs/Tabs.stories.tsx | 2 +- packages/lib/src/tabs/Tabs.test.tsx | 145 ++++++++-------- packages/lib/src/tabs/TabsLegacy.stories.tsx | 6 +- packages/lib/src/tabs/TabsLegacy.test.tsx | 137 +++++++-------- .../lib/src/text-input/TextInput.test.tsx | 10 +- .../src/toast/Toast.accessibility.test.tsx | 2 +- packages/lib/src/toast/Toast.stories.tsx | 2 +- packages/lib/src/toast/Toast.test.tsx | 2 +- .../lib/src/toggle-group/ToggleGroup.test.tsx | 18 +- packages/lib/src/wizard/Wizard.test.tsx | 10 +- 34 files changed, 564 insertions(+), 496 deletions(-) diff --git a/packages/lib/.eslintrc.js b/packages/lib/.eslintrc.js index 0b6e47dee4..2a2798a955 100644 --- a/packages/lib/.eslintrc.js +++ b/packages/lib/.eslintrc.js @@ -7,4 +7,7 @@ module.exports = { project: "./tsconfig.lint.json", tsconfigRootDir: __dirname, }, + env: { + "jest/globals": true, + }, }; diff --git a/packages/lib/src/BackgroundColorContext.tsx b/packages/lib/src/BackgroundColorContext.tsx index 57d27557d0..df3e94060f 100644 --- a/packages/lib/src/BackgroundColorContext.tsx +++ b/packages/lib/src/BackgroundColorContext.tsx @@ -1,26 +1,28 @@ -import { createContext, ReactNode, useMemo } from "react"; -import Color from "color"; +// import { createContext, ReactNode, useMemo } from "react"; +// import Color from "color"; -type BackgroundColors = "dark" | "light"; -const BackgroundColorContext = createContext(null); +// type BackgroundColors = "dark" | "light"; +// const BackgroundColorContext = createContext(null); -const getColorType = (hexColor: string): BackgroundColors => { - try { - if (hexColor) { - const hslColor = Color(hexColor).hsl(); - const lightnessColor = hslColor.lightness(); - return lightnessColor <= 30 ? "dark" : "light"; - } - } catch (e) { - return "light"; - } -}; +// const getColorType = (hexColor: string): BackgroundColors => { +// try { +// if (hexColor) { +// const hslColor = Color(hexColor).hsl(); +// const lightnessColor = hslColor.lightness(); +// return lightnessColor <= 30 ? "dark" : "light"; +// } else { +// return "light"; +// } +// } catch (e) { +// return "light"; +// } +// }; -type BackgroundColorProviderPropsType = { - color: string; - children: ReactNode; -}; -const BackgroundColorProvider = ({ color, children }: BackgroundColorProviderPropsType): JSX.Element => { - const colorType = useMemo(() => getColorType(color), [color]); - return {children}; -}; +// type BackgroundColorProviderPropsType = { +// color: string; +// children: ReactNode; +// }; +// const BackgroundColorProvider = ({ color, children }: BackgroundColorProviderPropsType): JSX.Element => { +// const colorType = useMemo(() => getColorType(color), [color]); +// return {children}; +// }; diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index 89901617ca..1243285d95 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -3,13 +3,14 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import DxcDataGrid from "./DataGrid"; import DxcContainer from "../container/Container"; import { GridColumn, HierarchyGridRow } from "./types"; -import { useState } from "react"; +import { isValidElement, useState } from "react"; import { disabledRules } from "../../test/accessibility/rules/specific/data-grid/disabledRules"; import preview from "../../.storybook/preview"; import { userEvent, within } from "@storybook/test"; import DxcBadge from "../badge/Badge"; import { ActionsPropsType } from "../table/types"; import { Meta, StoryObj } from "@storybook/react"; +import { isKeyOfRow } from "./utils"; export default { title: "Data Grid", @@ -588,8 +589,12 @@ const customSortColumns: GridColumn[] = [ alignment: "center", summaryKey: "total", sortable: true, - sortFn: (a: JSX.Element, b: JSX.Element) => - a.props.label < b.props.label ? -1 : a.props.label > b.props.label ? 1 : 0, + sortFn: (a, b) => { + if (isValidElement(a) && isValidElement(b)) { + return a.props.label < b.props.label ? -1 : a.props.label > b.props.label ? 1 : 0; + } + return 0; + }, }, ]; @@ -729,10 +734,20 @@ const DataGrid = () => { console.log(`Sorting the column '${columnKey}' by '${direction}' direction`); setRowsControlled((currentRows) => { return currentRows.sort((a, b) => { - if (direction === "ASC") { - return a[columnKey] < b[columnKey] ? -1 : a[columnKey] > b[columnKey] ? 1 : 0; + if (isKeyOfRow(columnKey, a) && isKeyOfRow(columnKey, b)) { + const valueA = a[columnKey]; + const valueB = b[columnKey]; + if (valueA != null && valueB != null) { + if (direction === "ASC") { + return valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + } else { + return valueA < valueB ? 1 : valueA > valueB ? -1 : 0; + } + } else { + return 0; + } } else { - return a[columnKey] < b[columnKey] ? 1 : a[columnKey] > b[columnKey] ? -1 : 0; + return 0; } }); }); @@ -925,23 +940,25 @@ export const DataGridSortedWithChildren: Story = { render: DataGridSortedChildren, play: async ({ canvasElement }) => { const canvas = within(canvasElement); + const checkboxes = canvas.getAllByRole("checkbox"); + const columnheaders = canvas.getAllByRole("columnheader"); - await userEvent.click(canvas.getAllByRole("checkbox")[0]); + checkboxes[0] && (await userEvent.click(checkboxes[0])); 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")); - await userEvent.click(canvas.getAllByRole("columnheader")[1]); - await userEvent.click(canvas.getAllByRole("columnheader")[1]); - await userEvent.click(canvas.getAllByRole("checkbox")[5]); + columnheaders[1] && (await userEvent.click(columnheaders[1])); + columnheaders[1] && (await userEvent.click(columnheaders[1])); + checkboxes[5] && (await userEvent.click(checkboxes[5])); - await userEvent.click(canvas.getAllByRole("checkbox")[13]); + checkboxes[13] && (await userEvent.click(checkboxes[13])); 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")); - await userEvent.click(canvas.getAllByRole("columnheader")[4]); - await userEvent.click(canvas.getAllByRole("checkbox")[18]); + columnheaders[4] && (await userEvent.click(columnheaders[4])); + checkboxes[18] && (await userEvent.click(checkboxes[18])); }, }; @@ -949,15 +966,17 @@ export const DataGridSortedExpanded: Story = { render: DataGridSortedExpandable, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("button")[0]); - await userEvent.click(canvas.getAllByRole("button")[1]); - await userEvent.click(canvas.getAllByRole("columnheader")[4]); - await userEvent.click(canvas.getAllByRole("button")[9]); - await userEvent.click(canvas.getAllByRole("button")[10]); - await userEvent.click(canvas.getAllByRole("columnheader")[10]); - await userEvent.click(canvas.getAllByRole("button")[16]); - await userEvent.click(canvas.getAllByRole("button")[43]); - await userEvent.click(canvas.getAllByRole("button")[36]); - await userEvent.click(canvas.getAllByRole("button")[37]); + const buttons = canvas.getAllByRole("button"); + const columnHeaders = canvas.getAllByRole("columnheader"); + buttons[0] && (await userEvent.click(buttons[0])); + buttons[1] && (await userEvent.click(buttons[1])); + columnHeaders[4] && (await userEvent.click(columnHeaders[4])); + buttons[9] && (await userEvent.click(buttons[9])); + buttons[10] && (await userEvent.click(buttons[10])); + columnHeaders[10] && (await userEvent.click(columnHeaders[10])); + buttons[16] && (await userEvent.click(buttons[16])); + buttons[43] && (await userEvent.click(buttons[43])); + buttons[36] && (await userEvent.click(buttons[36])); + buttons[37] && (await userEvent.click(buttons[37])); }, }; diff --git a/packages/lib/src/data-grid/types.ts b/packages/lib/src/data-grid/types.ts index e21b15179f..6376f01ec0 100644 --- a/packages/lib/src/data-grid/types.ts +++ b/packages/lib/src/data-grid/types.ts @@ -21,7 +21,7 @@ export type GridColumn = { /** * Custom criteria for sorting the column. */ - sortFn?: (_a: ReactNode, _b: ReactNode) => number; + sortFn?: (_a: ReactNode, _b: ReactNode) => 0 | 1 | -1; /** * Whether the column is draggable or not. */ diff --git a/packages/lib/src/data-grid/utils.tsx b/packages/lib/src/data-grid/utils.tsx index 6fe3619eaf..7c08c2df88 100644 --- a/packages/lib/src/data-grid/utils.tsx +++ b/packages/lib/src/data-grid/utils.tsx @@ -49,12 +49,13 @@ export const convertToRDGColumns = ( headerCellClass: gridColumn.alignment ? `header-align-${gridColumn.alignment}` : `header-align-left`, renderEditCell: gridColumn.textEditable ? textEditor : undefined, renderCell: ({ row }) => ( -
    - {row[gridColumn.key]} -
    - ), - renderSummaryCell: () => gridColumn.summaryKey ? ( -
    +
    + {row[gridColumn.key]} +
    + ), + renderSummaryCell: () => + gridColumn.summaryKey ? ( +
    {summaryRow?.[gridColumn.summaryKey]}
    ) : undefined, @@ -591,3 +592,17 @@ export const getPaginatedNodes = ( ) ); }; + +/** + * Type guard to ensure `key` is a valid key of the `row` object. + * This function checks if the given `key` exists within the provided object `obj`. + * + * @template T - The type of the row object. + * @param {string} key - The key to check if it exists in the row object. + * @param {T} obj - The row object to check the key against. + * @returns {boolean} - Returns `true` if `key` is a valid key of `obj`, otherwise `false`. + * + */ +export const isKeyOfRow = (key: string, obj: T): key is Extract => { + return key in obj; +}; diff --git a/packages/lib/src/nav-tabs/NavTabs.test.tsx b/packages/lib/src/nav-tabs/NavTabs.test.tsx index 9be06cf1df..849c7ceea7 100644 --- a/packages/lib/src/nav-tabs/NavTabs.test.tsx +++ b/packages/lib/src/nav-tabs/NavTabs.test.tsx @@ -25,12 +25,12 @@ describe("Tabs component tests", () => { }); const anchors = getAllByRole("tab"); expect(anchors.length).toBe(3); - expect(anchors[0].getAttribute("href")).toBe("/test1"); - expect(anchors[1].getAttribute("href")).toBeFalsy(); - expect(anchors[2].getAttribute("href")).toBe("/test3"); - expect(anchors[0].getAttribute("tabindex")).toBe("0"); - expect(anchors[1].getAttribute("tabindex")).toBe("-1"); - expect(anchors[2].getAttribute("tabindex")).toBe("-1"); + expect(anchors[0]?.getAttribute("href")).toBe("/test1"); + expect(anchors[1]?.getAttribute("href")).toBeFalsy(); + expect(anchors[2]?.getAttribute("href")).toBe("/test3"); + expect(anchors[0]?.getAttribute("tabindex")).toBe("0"); + expect(anchors[1]?.getAttribute("tabindex")).toBe("-1"); + expect(anchors[2]?.getAttribute("tabindex")).toBe("-1"); }); test("Tabs render with correct labels, badges and icons", () => { @@ -65,8 +65,8 @@ describe("Tabs component tests", () => { ); const tabs = getAllByRole("tab"); - expect(tabs[0].getAttribute("tabindex")).toBe("-1"); - expect(tabs[1].getAttribute("tabindex")).toBe("-1"); - expect(tabs[2].getAttribute("tabindex")).toBe("3"); + expect(tabs[0]?.getAttribute("tabindex")).toBe("-1"); + expect(tabs[1]?.getAttribute("tabindex")).toBe("-1"); + expect(tabs[2]?.getAttribute("tabindex")).toBe("3"); }); }); diff --git a/packages/lib/src/number-input/NumberInput.test.tsx b/packages/lib/src/number-input/NumberInput.test.tsx index 53fb2b2195..7e755de0e5 100644 --- a/packages/lib/src/number-input/NumberInput.test.tsx +++ b/packages/lib/src/number-input/NumberInput.test.tsx @@ -36,10 +36,10 @@ describe("Number input component tests", () => { const number = getByLabelText("Number label") as HTMLInputElement; expect(number.readOnly).toBeTruthy(); const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe(""); const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe(""); }); @@ -121,7 +121,7 @@ describe("Number input component tests", () => { fireEvent.blur(number); expect(number.value).toBe("1"); const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("1"); }); @@ -132,7 +132,7 @@ describe("Number input component tests", () => { fireEvent.blur(number); expect(number.value).toBe("1"); const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("5"); }); @@ -158,7 +158,7 @@ describe("Number input component tests", () => { fireEvent.blur(number); expect(number.value).toBe("12"); const decrement = getAllByRole("button")[1]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("12"); }); @@ -169,7 +169,7 @@ describe("Number input component tests", () => { fireEvent.blur(number); expect(number.value).toBe("120"); const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("10"); }); @@ -180,18 +180,18 @@ describe("Number input component tests", () => { fireEvent.blur(number); expect(number.value).toBe("1"); const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("1"); const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("5"); - await userEvent.click(increment); - await userEvent.click(increment); - await userEvent.click(increment); - await userEvent.click(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)); + increment && (await userEvent.click(increment)); expect(number.value).toBe("10"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("10"); }); @@ -202,14 +202,14 @@ describe("Number input component tests", () => { fireEvent.blur(number); expect(number.value).toBe("10"); const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("15"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("20"); const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("15"); - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("10"); }); @@ -220,16 +220,16 @@ describe("Number input component tests", () => { fireEvent.blur(number); expect(number.value).toBe("-9"); const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("-8.5"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("-8"); const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); - await userEvent.click(decrement); - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); + decrement && (await userEvent.click(decrement)); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("-9.5"); - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("-10"); }); @@ -243,20 +243,20 @@ describe("Number input component tests", () => { fireEvent.blur(number); expect(onBlur).toHaveBeenCalledWith({ value: "1", error: "Value must be greater than or equal to 5." }); const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("5"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("13"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("13"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("13"); const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("5"); - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("5"); - await userEvent.click(decrement); + decrement && (await 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 () => { @@ -266,9 +266,9 @@ describe("Number input component tests", () => { ); const number = getByLabelText("Number input label") as HTMLInputElement; const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("1"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("2"); }); @@ -278,9 +278,9 @@ describe("Number input component tests", () => { ); const number = getByLabelText("Number input label") as HTMLInputElement; const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("0"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("0"); }); @@ -290,9 +290,9 @@ describe("Number input component tests", () => { ); const number = getByLabelText("Number input label") as HTMLInputElement; const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("2"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("2.5"); }); @@ -302,9 +302,9 @@ describe("Number input component tests", () => { ); const number = getByLabelText("Number input label") as HTMLInputElement; const increment = getAllByRole("button")[1]; - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("-1"); - await userEvent.click(increment); + increment && (await userEvent.click(increment)); expect(number.value).toBe("-1"); }); @@ -314,7 +314,7 @@ describe("Number input component tests", () => { ); const number = getByLabelText("Number input label") as HTMLInputElement; const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("-1"); }); @@ -324,7 +324,7 @@ describe("Number input component tests", () => { ); const number = getByLabelText("Number input label") as HTMLInputElement; const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("0"); }); @@ -334,9 +334,9 @@ describe("Number input component tests", () => { ); const number = getByLabelText("Number input label") as HTMLInputElement; const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("2"); - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("2"); }); @@ -346,9 +346,9 @@ describe("Number input component tests", () => { ); const number = getByLabelText("Number input label") as HTMLInputElement; const decrement = getAllByRole("button")[0]; - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("-1"); - await userEvent.click(decrement); + decrement && (await userEvent.click(decrement)); expect(number.value).toBe("-1.5"); }); @@ -468,9 +468,9 @@ describe("Number input component tests", () => { expect(number.getAttribute("aria-controls")).toBeNull(); expect(number.getAttribute("aria-expanded")).toBeNull(); const decrement = getAllByRole("button")[0]; - expect(decrement.getAttribute("aria-label")).toBe("Decrement value"); + expect(decrement?.getAttribute("aria-label")).toBe("Decrement value"); const increment = getAllByRole("button")[1]; - expect(increment.getAttribute("aria-label")).toBe("Increment value"); + 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 () => { @@ -489,11 +489,11 @@ describe("Number input component tests", () => { const less = getAllByRole("button")[0]; const more = getAllByRole("button")[1]; const submit = getByText("Submit"); - await userEvent.click(more); + more && (await userEvent.click(more)); expect(handlerOnSubmit).not.toHaveBeenCalled(); - await userEvent.click(less); + less && (await userEvent.click(less)); expect(handlerOnSubmit).not.toHaveBeenCalled(); - await userEvent.click(submit); + submit && (await userEvent.click(submit)); expect(handlerOnSubmit).toHaveBeenCalled(); }); }); diff --git a/packages/lib/src/paginator/Paginator.test.tsx b/packages/lib/src/paginator/Paginator.test.tsx index 5ade61b8c3..4902eba8b9 100644 --- a/packages/lib/src/paginator/Paginator.test.tsx +++ b/packages/lib/src/paginator/Paginator.test.tsx @@ -65,9 +65,9 @@ describe("Paginator component tests", () => { ); const goToPageSelect = getAllByRole("combobox")[0]; - await userEvent.click(goToPageSelect); + goToPageSelect && (await userEvent.click(goToPageSelect)); const goToPageOption = getByText("2"); - await userEvent.click(goToPageOption); + goToPageOption && (await userEvent.click(goToPageOption)); expect(onClick).toHaveBeenCalledWith(2); }); @@ -77,7 +77,7 @@ describe("Paginator component tests", () => { ); const nextButton = getAllByRole("button")[2]; - await userEvent.click(nextButton); + nextButton && (await userEvent.click(nextButton)); expect(onClick).toHaveBeenCalled(); }); @@ -95,9 +95,9 @@ describe("Paginator component tests", () => { /> ); const select = getAllByText("10")[0]; - await userEvent.click(select); + select && (await userEvent.click(select)); const itemPerPageOption = getByText("15"); - await userEvent.click(itemPerPageOption); + itemPerPageOption && (await userEvent.click(itemPerPageOption)); expect(onClick).toHaveBeenCalledWith(15); }); @@ -107,8 +107,8 @@ describe("Paginator component tests", () => { ); const nextButton = getAllByRole("button")[2]; - expect(nextButton.hasAttribute("disabled")).toBeTruthy(); - await userEvent.click(nextButton); + expect(nextButton?.hasAttribute("disabled")).toBeTruthy(); + nextButton && (await userEvent.click(nextButton)); expect(onClick).toHaveBeenCalledTimes(0); }); @@ -118,8 +118,8 @@ describe("Paginator component tests", () => { ); const lastButton = getAllByRole("button")[3]; - expect(lastButton.hasAttribute("disabled")).toBeTruthy(); - await userEvent.click(lastButton); + expect(lastButton?.hasAttribute("disabled")).toBeTruthy(); + lastButton && (await userEvent.click(lastButton)); expect(onClick).toHaveBeenCalledTimes(0); }); @@ -129,8 +129,8 @@ describe("Paginator component tests", () => { ); const lastButton = getAllByRole("button")[0]; - expect(lastButton.hasAttribute("disabled")).toBeTruthy(); - await userEvent.click(lastButton); + expect(lastButton?.hasAttribute("disabled")).toBeTruthy(); + lastButton && (await userEvent.click(lastButton)); expect(onClick).toHaveBeenCalledTimes(0); }); @@ -140,8 +140,8 @@ describe("Paginator component tests", () => { ); const lastButton = getAllByRole("button")[1]; - expect(lastButton.hasAttribute("disabled")).toBeTruthy(); - await userEvent.click(lastButton); + expect(lastButton?.hasAttribute("disabled")).toBeTruthy(); + lastButton && (await userEvent.click(lastButton)); expect(onClick).toHaveBeenCalledTimes(0); }); @@ -154,10 +154,10 @@ describe("Paginator component tests", () => { const prevButton = getAllByRole("button")[1]; const nextButton = getAllByRole("button")[2]; const lastButton = getAllByRole("button")[3]; - expect(firstButton.hasAttribute("disabled")).toBeFalsy(); - expect(prevButton.hasAttribute("disabled")).toBeFalsy(); - expect(nextButton.hasAttribute("disabled")).toBeTruthy(); - expect(lastButton.hasAttribute("disabled")).toBeTruthy(); + expect(firstButton?.hasAttribute("disabled")).toBeFalsy(); + expect(prevButton?.hasAttribute("disabled")).toBeFalsy(); + expect(nextButton?.hasAttribute("disabled")).toBeTruthy(); + expect(lastButton?.hasAttribute("disabled")).toBeTruthy(); }); test("First and previous buttons are disable in first page", () => { @@ -169,10 +169,10 @@ describe("Paginator component tests", () => { const prevButton = getAllByRole("button")[1]; const nextButton = getAllByRole("button")[2]; const lastButton = getAllByRole("button")[3]; - expect(firstButton.hasAttribute("disabled")).toBeTruthy(); - expect(prevButton.hasAttribute("disabled")).toBeTruthy(); - expect(nextButton.hasAttribute("disabled")).toBeFalsy(); - expect(lastButton.hasAttribute("disabled")).toBeFalsy(); + expect(firstButton?.hasAttribute("disabled")).toBeTruthy(); + expect(prevButton?.hasAttribute("disabled")).toBeTruthy(); + expect(nextButton?.hasAttribute("disabled")).toBeFalsy(); + expect(lastButton?.hasAttribute("disabled")).toBeFalsy(); }); test("itemsPerPage is 0 and showGoToPage is true", () => { diff --git a/packages/lib/src/password-input/PasswordInput.test.tsx b/packages/lib/src/password-input/PasswordInput.test.tsx index 97e7e2bc1f..06b82f8583 100644 --- a/packages/lib/src/password-input/PasswordInput.test.tsx +++ b/packages/lib/src/password-input/PasswordInput.test.tsx @@ -50,7 +50,7 @@ describe("Password input component tests", () => { userEvent.type(passwordInput, "Pa$$w0rd"); expect(passwordInput.value).toBe("Pa$$w0rd"); const clearButton = getAllByRole("button")[0]; - await userEvent.click(clearButton); + clearButton && await userEvent.click(clearButton); expect(passwordInput.value).toBe(""); }); @@ -70,7 +70,7 @@ describe("Password input component tests", () => { expect(passwordInput.value).toBe("Pa$$w0rd"); expect(passwordInput.type).toBe("password"); const showButton = getAllByRole("button")[1]; - await userEvent.click(showButton); + showButton && await userEvent.click(showButton); expect(passwordInput.type).toBe("text"); }); diff --git a/packages/lib/src/radio-group/RadioGroup.test.tsx b/packages/lib/src/radio-group/RadioGroup.test.tsx index df9b879041..e14aa3db98 100644 --- a/packages/lib/src/radio-group/RadioGroup.test.tsx +++ b/packages/lib/src/radio-group/RadioGroup.test.tsx @@ -64,8 +64,9 @@ describe("Radio Group component tests", () => { ); const radioGroup = getByRole("radiogroup"); const submit = getByText("Submit"); + const radio = getAllByRole("radio")[4]; await userEvent.click(radioGroup); - await userEvent.click(getAllByRole("radio")[4]); + radio && (await userEvent.click(radio)); await userEvent.click(submit); }); @@ -93,15 +94,15 @@ describe("Radio Group component tests", () => { ); const radioGroup = getByRole("radiogroup"); const radios = getAllByRole("radio"); - expect(radios[2].getAttribute("aria-disabled")).toBe("true"); - expect(radios[0].tabIndex).toBe(0); - expect(radios[1].tabIndex).toBe(-1); - expect(radios[2].tabIndex).toBe(-1); + expect(radios[2]?.getAttribute("aria-disabled")).toBe("true"); + expect(radios[0]?.tabIndex).toBe(0); + expect(radios[1]?.tabIndex).toBe(-1); + expect(radios[2]?.tabIndex).toBe(-1); fireEvent.keyDown(radioGroup, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 }); fireEvent.keyDown(radioGroup, { key: "ArrowDown", code: "ArrowDown", keyCode: 40, charCode: 40 }); - expect(radios[0].tabIndex).toBe(0); - expect(radios[1].tabIndex).toBe(-1); - expect(radios[2].tabIndex).toBe(-1); + expect(radios[0]?.tabIndex).toBe(0); + expect(radios[1]?.tabIndex).toBe(-1); + expect(radios[2]?.tabIndex).toBe(-1); }); test("Disabled radio group doesn't send its value when submitted", async () => { @@ -139,11 +140,12 @@ describe("Radio Group component tests", () => { ); const radioGroup = getByRole("radiogroup"); + 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); - await userEvent.click(getAllByRole("radio")[0]); + radio && (await userEvent.click(radio)); expect(onChange).toHaveBeenCalledWith("1"); fireEvent.blur(radioGroup); expect(onBlur).toHaveBeenCalledWith({ value: "1" }); @@ -156,10 +158,11 @@ describe("Radio Group component tests", () => { ); const radioGroup = getByRole("radiogroup"); + const radio = getAllByRole("radio")[0]; expect(radioGroup.getAttribute("aria-required")).toBe("true"); fireEvent.blur(radioGroup); expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, choose an option." }); - await userEvent.click(getAllByRole("radio")[0]); + radio && (await userEvent.click(radio)); expect(onChange).toHaveBeenCalledWith("1"); }); @@ -177,9 +180,9 @@ describe("Radio Group component tests", () => { ); const radio = getAllByRole("radio")[1]; const submitInput = container.querySelector(`input[name="test"]`); - expect(radio.tabIndex).toBe(0); - expect(radio.getAttribute("aria-checked")).toBe("true"); - expect(submitInput.value).toBe("2"); + expect(radio?.tabIndex).toBe(0); + expect(radio?.getAttribute("aria-checked")).toBe("true"); + 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 () => { @@ -206,7 +209,7 @@ describe("Radio Group component tests", () => { const submitInput = container.querySelector(`input[name="test"]`); await userEvent.click(optionalLabel); expect(onChange).toHaveBeenCalledWith(""); - expect(submitInput.value).toBe(""); + expect(submitInput?.value).toBe(""); }); test("Controlled radio group", async () => { @@ -226,10 +229,10 @@ describe("Radio Group component tests", () => { const radioGroup = getByRole("radiogroup"); const radios = getAllByRole("radio"); const submitInput = container.querySelector(`input[name="test"]`); - expect(submitInput.value).toBe("2"); - expect(radios[1].tabIndex).toBe(0); - expect(radios[1].getAttribute("aria-checked")).toBe("true"); - await userEvent.click(radios[6]); + 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])); expect(onChange).toHaveBeenCalledWith("7"); fireEvent.blur(radioGroup); expect(onBlur).toHaveBeenCalledWith({ value: "2" }); @@ -249,13 +252,13 @@ describe("Radio Group component tests", () => { const radioLabel = getByText("Option 09"); const checkedRadio = getAllByRole("radio")[8]; const submitInput = container.querySelector(`input[name="test"]`); - expect(checkedRadio.tabIndex).toBe(-1); + expect(checkedRadio?.tabIndex).toBe(-1); await userEvent.click(radioLabel); expect(onChange).toHaveBeenCalledWith("9"); - expect(checkedRadio.getAttribute("aria-checked")).toBe("true"); - expect(checkedRadio.tabIndex).toBe(0); + expect(checkedRadio?.getAttribute("aria-checked")).toBe("true"); + expect(checkedRadio?.tabIndex).toBe(0); expect(document.activeElement).toEqual(checkedRadio); - expect(submitInput.value).toBe("9"); + expect(submitInput?.value).toBe("9"); }); test("Select an option by clicking on its radio input", async () => { @@ -271,13 +274,13 @@ describe("Radio Group component tests", () => { ); const checkedRadio = getAllByRole("radio")[6]; const submitInput = container.querySelector(`input[name="test"]`); - expect(checkedRadio.tabIndex).toBe(-1); - await userEvent.click(checkedRadio); + expect(checkedRadio?.tabIndex).toBe(-1); + checkedRadio && (await userEvent.click(checkedRadio)); expect(onChange).toHaveBeenCalledWith("7"); - expect(checkedRadio.getAttribute("aria-checked")).toBe("true"); - expect(checkedRadio.tabIndex).toBe(0); + expect(checkedRadio?.getAttribute("aria-checked")).toBe("true"); + expect(checkedRadio?.tabIndex).toBe(0); expect(document.activeElement).toEqual(checkedRadio); - expect(submitInput.value).toBe("7"); + expect(submitInput?.value).toBe("7"); }); test("Select an option that is already checked does not call onChange event but gives the focus", async () => { @@ -293,9 +296,9 @@ describe("Radio Group component tests", () => { /> ); const checkedRadio = getAllByRole("radio")[1]; - expect(checkedRadio.tabIndex).toBe(0); - expect(checkedRadio.getAttribute("aria-checked")).toBe("true"); - await userEvent.click(checkedRadio); + expect(checkedRadio?.tabIndex).toBe(0); + expect(checkedRadio?.getAttribute("aria-checked")).toBe("true"); + checkedRadio && (await userEvent.click(checkedRadio)); expect(onChange).not.toHaveBeenCalled(); expect(document.activeElement).toEqual(checkedRadio); }); @@ -316,9 +319,9 @@ describe("Radio Group component tests", () => { const submitInput = container.querySelector(`input[name="test"]`); 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); - expect(submitInput.value).toBe("1"); + expect(checkedRadio?.getAttribute("aria-checked")).toBe("true"); + expect(checkedRadio?.tabIndex).toBe(0); + expect(submitInput?.value).toBe("1"); }); test("When the radio group gains the focus by keyboard ('tab' key), it goes to the first option (if no one was previously selected), without selecting it", () => { @@ -340,17 +343,17 @@ describe("Radio Group component tests", () => { const checkedRadio = getAllByRole("radio")[0]; userEvent.tab(); expect(onChange).not.toHaveBeenCalled(); - expect(submitInput.value).toBe(""); - expect(checkedRadio.tabIndex).toBe(0); - expect(checkedRadio.getAttribute("aria-checked")).toBe("false"); + expect(submitInput?.value).toBe(""); + 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 }); expect(onBlur).not.toHaveBeenCalled(); expect(onChange).toHaveBeenCalledTimes(1); - expect(radios[1].getAttribute("aria-checked")).toBe("true"); + expect(radios[1]?.getAttribute("aria-checked")).toBe("true"); expect(document.activeElement).toEqual(radios[1]); - expect(radios[1].tabIndex).toBe(0); - expect(submitInput.value).toBe("2"); + expect(radios[1]?.tabIndex).toBe(0); + expect(submitInput?.value).toBe("2"); }); test("The 'arrowDown' and 'arrowRight' keys move the selection to the next radio. When the last radio is reached, moves the selection to the first one", () => { @@ -373,17 +376,17 @@ describe("Radio Group component tests", () => { 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"); + expect(radios[8]?.getAttribute("aria-checked")).toBe("true"); expect(document.activeElement).toEqual(radios[8]); - expect(radios[8].tabIndex).toBe(0); - expect(submitInput.value).toBe("9"); + expect(radios[8]?.tabIndex).toBe(0); + expect(submitInput?.value).toBe("9"); fireEvent.keyDown(radioGroup, { key: "ArrowRight", code: "ArrowRight", keyCode: 39, charCode: 39 }); expect(onBlur).not.toHaveBeenCalled(); expect(onChange).toHaveBeenCalledTimes(2); - expect(radios[0].getAttribute("aria-checked")).toBe("true"); + expect(radios[0]?.getAttribute("aria-checked")).toBe("true"); expect(document.activeElement).toEqual(radios[0]); - expect(radios[0].tabIndex).toBe(0); - expect(submitInput.value).toBe("1"); + expect(radios[0]?.tabIndex).toBe(0); + expect(submitInput?.value).toBe("1"); }); test("The 'arrowUp' and 'arrowLeft' keys move the selection to the previous radio. When the first radio is reached, moves the selection to the last one", () => { @@ -406,17 +409,17 @@ describe("Radio Group component tests", () => { 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"); + expect(radios[0]?.getAttribute("aria-checked")).toBe("true"); expect(document.activeElement).toEqual(radios[0]); - expect(radios[0].tabIndex).toBe(0); - expect(submitInput.value).toBe("1"); + expect(radios[0]?.tabIndex).toBe(0); + expect(submitInput?.value).toBe("1"); fireEvent.keyDown(radioGroup, { key: "ArrowLeft", code: "ArrowLeft", keyCode: 37, charCode: 37 }); expect(onBlur).not.toHaveBeenCalled(); expect(onChange).toHaveBeenCalledTimes(2); - expect(radios[8].getAttribute("aria-checked")).toBe("true"); + expect(radios[8]?.getAttribute("aria-checked")).toBe("true"); expect(document.activeElement).toEqual(radios[8]); - expect(radios[8].tabIndex).toBe(0); - expect(submitInput.value).toBe("9"); + expect(radios[8]?.tabIndex).toBe(0); + expect(submitInput?.value).toBe("9"); }); test("Keyboard focus movement continues from the last radio input clicked", async () => { @@ -433,20 +436,20 @@ describe("Radio Group component tests", () => { const radioGroup = getByRole("radiogroup"); const radios = getAllByRole("radio"); const submitInput = container.querySelector(`input[name="test"]`); - await userEvent.click(radios[3]); + radios[3] && (await 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(radios[4]?.getAttribute("aria-checked")).toBe("true"); expect(document.activeElement).toEqual(radios[4]); - expect(radios[4].tabIndex).toBe(0); - expect(submitInput.value).toBe("5"); - await userEvent.click(radios[8]); + expect(radios[4]?.tabIndex).toBe(0); + expect(submitInput?.value).toBe("5"); + radios[8] && (await 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"); + expect(radios[7]?.getAttribute("aria-checked")).toBe("true"); expect(document.activeElement).toEqual(radios[7]); - expect(radios[7].tabIndex).toBe(0); - expect(submitInput.value).toBe("8"); + expect(radios[7]?.tabIndex).toBe(0); + 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 () => { @@ -464,18 +467,18 @@ describe("Radio Group component tests", () => { const radioGroup = getByRole("radiogroup"); const radios = getAllByRole("radio"); const submitInput = container.querySelector(`input[name="test"]`); - await userEvent.click(radios[5]); + radios[5] && (await userEvent.click(radios[5])); expect(onChange).not.toHaveBeenCalled(); - expect(radios[5].getAttribute("aria-checked")).toBe("false"); + expect(radios[5]?.getAttribute("aria-checked")).toBe("false"); expect(document.activeElement).toEqual(radios[5]); - expect(radios[5].tabIndex).toBe(0); - expect(submitInput.value).toBe(""); + expect(radios[5]?.tabIndex).toBe(0); + expect(submitInput?.value).toBe(""); fireEvent.keyDown(radioGroup, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 }); expect(onChange).not.toHaveBeenCalled(); - expect(radios[4].getAttribute("aria-checked")).toBe("false"); + expect(radios[4]?.getAttribute("aria-checked")).toBe("false"); expect(document.activeElement).toEqual(radios[4]); - expect(radios[4].tabIndex).toBe(0); - expect(submitInput.value).toBe(""); + expect(radios[4]?.tabIndex).toBe(0); + expect(submitInput?.value).toBe(""); }); test("Read-only radio group sends its value on submit", async () => { diff --git a/packages/lib/src/resultset-table/ResultsetTable.accessibility.test.tsx b/packages/lib/src/resultset-table/ResultsetTable.accessibility.test.tsx index 3e681d9d2c..cd245c9e66 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.accessibility.test.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.accessibility.test.tsx @@ -1,5 +1,5 @@ import { render } from "@testing-library/react"; -import { axe } from "../../test/accessibility/axe-helper"; +import { axe, formatRules } from "../../test/accessibility/axe-helper"; import DxcResultsetTable from "./ResultsetTable"; // TODO: REMOVE @@ -7,10 +7,7 @@ import { disabledRules as rules } from "../../test/accessibility/rules/specific/ import { ActionCellsPropsType } from "../table/types"; const disabledRules = { - rules: rules.reduce((rulesObj, rule) => { - rulesObj[rule] = { enabled: false }; - return rulesObj; - }, {}), + rules: formatRules(rules), }; const deleteIcon = ( diff --git a/packages/lib/src/resultset-table/ResultsetTable.stories.tsx b/packages/lib/src/resultset-table/ResultsetTable.stories.tsx index 8f003c64f2..92d02d1cbe 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.stories.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.stories.tsx @@ -410,8 +410,8 @@ export const AscendentSorting: Story = { const canvas = within(canvasElement); const idHeader = canvas.getAllByRole("button")[0]; const idHeader2 = canvas.getAllByRole("button")[6]; - await userEvent.click(idHeader); - await userEvent.click(idHeader2); + idHeader && (await userEvent.click(idHeader)); + idHeader2 && (await userEvent.click(idHeader2)); }, }; @@ -421,10 +421,10 @@ export const DescendantSorting: Story = { const canvas = within(canvasElement); const nameHeader = canvas.getAllByRole("button")[1]; const nameHeader2 = canvas.getAllByRole("button")[7]; - await userEvent.click(nameHeader); - await userEvent.click(nameHeader); - await userEvent.click(nameHeader2); - await userEvent.click(nameHeader2); + nameHeader && (await userEvent.click(nameHeader)); + nameHeader && (await userEvent.click(nameHeader)); + nameHeader2 && (await userEvent.click(nameHeader2)); + nameHeader2 && (await userEvent.click(nameHeader2)); }, }; @@ -433,7 +433,7 @@ export const MiddlePage: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const nextButton = canvas.getAllByRole("button")[2]; - await userEvent.click(nextButton); + nextButton && (await userEvent.click(nextButton)); }, }; @@ -442,7 +442,7 @@ export const LastPage: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const nextButton = canvas.getAllByRole("button")[3]; - await userEvent.click(nextButton); + nextButton && (await userEvent.click(nextButton)); }, }; @@ -451,6 +451,6 @@ export const DropdownAction: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const dropdown = canvas.getAllByRole("button")[5]; - userEvent.click(dropdown); + dropdown && userEvent.click(dropdown); }, }; diff --git a/packages/lib/src/resultset-table/ResultsetTable.test.tsx b/packages/lib/src/resultset-table/ResultsetTable.test.tsx index 64d8c069aa..a55e05dd4e 100644 --- a/packages/lib/src/resultset-table/ResultsetTable.test.tsx +++ b/packages/lib/src/resultset-table/ResultsetTable.test.tsx @@ -361,7 +361,7 @@ describe("Resultset table component tests", () => { expect(getByText("Lana")).toBeTruthy(); expect(getAllByRole("row").length - 1).toEqual(3); const nextButton = getAllByRole("button")[3]; - fireEvent.click(nextButton); + nextButton && fireEvent.click(nextButton); expect(getByText("4 to 6 of 10")).toBeTruthy(); expect(getByText("Rick")).toBeTruthy(); expect(getByText("Mark")).toBeTruthy(); @@ -380,7 +380,7 @@ describe("Resultset table component tests", () => { expect(getByText("Lana")).toBeTruthy(); expect(getAllByRole("row").length - 1).toEqual(3); const goToPageSelect = getAllByRole("button")[3]; - await userEvent.click(goToPageSelect); + goToPageSelect && (await userEvent.click(goToPageSelect)); const goToPageOption = getByText("2"); await userEvent.click(goToPageOption); expect(getByText("4 to 6 of 10")).toBeTruthy(); @@ -393,7 +393,7 @@ describe("Resultset table component tests", () => { test("Resultset table going to the last page shows only one row", () => { const { getByText, getAllByRole } = render(); const lastButton = getAllByRole("button")[4]; - fireEvent.click(lastButton); + lastButton && fireEvent.click(lastButton); expect(getByText("10 to 10 of 10")).toBeTruthy(); expect(getAllByRole("row")).toHaveLength(2); expect(getByText("Cosmin")).toBeTruthy(); @@ -401,24 +401,26 @@ describe("Resultset table component tests", () => { test("Resultset table sort rows by column", () => { const component = render(); + const name = component.queryByText("Name"); expect(component.queryByText("Peter")).toBeTruthy(); - fireEvent.click(component.queryByText("Name")); + name && fireEvent.click(name); expect(component.queryByText("Tina")).not.toBeTruthy(); expect(component.queryByText("Cosmin")).toBeTruthy(); - fireEvent.click(component.queryByText("Name")); + name && fireEvent.click(name); expect(component.queryByText("Tina")).toBeTruthy(); expect(component.queryByText("Cosmin")).not.toBeTruthy(); }); test("Resultset table sort rows by column even if they are missing sortValues", () => { const component = render(); + const name = component.queryByText("Name"); expect(component.queryByText("Peter")).toBeTruthy(); - fireEvent.click(component.queryByText("Name")); + name && fireEvent.click(name); expect(component.queryByText("Tina")).not.toBeTruthy(); expect(component.queryByText("Cosmin")).toBeTruthy(); - fireEvent.click(component.queryByText("Name")); + name && fireEvent.click(name); expect(component.queryByText("Tina")).toBeTruthy(); expect(component.queryByText("Cosmin")).not.toBeTruthy(); }); @@ -430,7 +432,7 @@ describe("Resultset table component tests", () => { expect(queryByText("1 to 3 of 10")).toBeTruthy(); const lastButton = getAllByRole("button")[4]; expect(queryByText("Peter")).toBeTruthy(); - fireEvent.click(lastButton); + lastButton && fireEvent.click(lastButton); expect(queryByText("10 to 10 of 10")).toBeTruthy(); rerender(); expect(queryByText("7 to 9 of 9")).toBeTruthy(); @@ -443,7 +445,7 @@ describe("Resultset table component tests", () => { expect(queryByText("1 to 2 of 10")).toBeTruthy(); const lastButton = getAllByRole("button")[4]; expect(queryByText("Peter")).toBeTruthy(); - fireEvent.click(lastButton); + lastButton && fireEvent.click(lastButton); expect(queryByText("9 to 10 of 10")).toBeTruthy(); rerender(); expect(queryByText("9 to 9 of 9")).toBeTruthy(); @@ -456,19 +458,19 @@ describe("Resultset table component tests", () => { const columnHeader = getAllByRole("columnheader")[0]; const sortButton = getAllByRole("button")[0]; - expect(getAllByRole("checkbox")[0].getAttribute("aria-checked")).toBe("true"); + expect(getAllByRole("checkbox")[0]?.getAttribute("aria-checked")).toBe("true"); - expect(columnHeader.getAttribute("aria-sort")).toBe("none"); + expect(columnHeader?.getAttribute("aria-sort")).toBe("none"); - fireEvent.click(sortButton); + sortButton && fireEvent.click(sortButton); - expect(columnHeader.getAttribute("aria-sort")).toBe("ascending"); + expect(columnHeader?.getAttribute("aria-sort")).toBe("ascending"); - fireEvent.click(sortButton); + sortButton && fireEvent.click(sortButton); - expect(columnHeader.getAttribute("aria-sort")).toBe("descending"); + expect(columnHeader?.getAttribute("aria-sort")).toBe("descending"); - expect(getAllByRole("checkbox")[0].getAttribute("aria-checked")).toBe("false"); + expect(getAllByRole("checkbox")[0]?.getAttribute("aria-checked")).toBe("false"); }); test("Resultset table change itemsPerPage should go to first page", () => { @@ -477,7 +479,7 @@ describe("Resultset table component tests", () => { ); const lastButton = getAllByRole("button")[4]; expect(getAllByRole("row").length - 1).toEqual(3); - fireEvent.click(lastButton); + lastButton && fireEvent.click(lastButton); expect(getAllByRole("row").length - 1).toEqual(1); }); @@ -536,14 +538,14 @@ describe("Resultset table component tests", () => { ); const dropdown = getAllByRole("button")[2]; act(() => { - userEvent.click(dropdown); + dropdown && userEvent.click(dropdown); }); expect(getByRole("menu")).toBeTruthy(); const option = getByText("Aliexpress"); userEvent.click(option); expect(onSelectOption).toHaveBeenCalledWith("3"); const action = getAllByRole("button")[1]; - userEvent.click(action); + action && userEvent.click(action); expect(onClick).toHaveBeenCalled(); }); }); diff --git a/packages/lib/src/select/Select.accessibility.test.tsx b/packages/lib/src/select/Select.accessibility.test.tsx index 5a6af9c00f..9b374297f8 100644 --- a/packages/lib/src/select/Select.accessibility.test.tsx +++ b/packages/lib/src/select/Select.accessibility.test.tsx @@ -1,5 +1,5 @@ import { render } from "@testing-library/react"; -import { axe } from "../../test/accessibility/axe-helper"; +import { axe, formatRules } from "../../test/accessibility/axe-helper"; import DxcFlex from "../flex/Flex"; import DxcSelect from "./Select"; @@ -7,10 +7,7 @@ import DxcSelect from "./Select"; import { disabledRules as rules } from "../../test/accessibility/rules/specific/select/disabledRules"; const disabledRules = { - rules: rules.reduce((rulesObj, rule) => { - rulesObj[rule] = { enabled: false }; - return rulesObj; - }, {}), + rules: formatRules(rules), }; const iconSVG = ( diff --git a/packages/lib/src/select/Select.stories.tsx b/packages/lib/src/select/Select.stories.tsx index 354c6348a0..07f3ed7c32 100644 --- a/packages/lib/src/select/Select.stories.tsx +++ b/packages/lib/src/select/Select.stories.tsx @@ -680,7 +680,8 @@ export const Chromatic: Story = { render: Select, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[24]); + const combobox = canvas.getAllByRole("combobox")[24]; + combobox && (await userEvent.click(combobox)); }, }; @@ -688,7 +689,8 @@ export const OpinionatedTheme: Story = { render: Opinionated, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[2]); + const combobox = canvas.getAllByRole("combobox")[2]; + combobox && await userEvent.click(combobox); }, }; @@ -721,7 +723,8 @@ export const MultipleSearchableWithValue: Story = { render: MultipleSearchable, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[0]); + const combobox = canvas.getAllByRole("combobox")[0]; + combobox && await userEvent.click(combobox); }, }; @@ -747,7 +750,8 @@ export const MultipleOptionsDisplayed: Story = { render: MultipleSelect, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await userEvent.click(canvas.getAllByRole("combobox")[0]); + const combobox = canvas.getAllByRole("combobox")[0]; + combobox && await userEvent.click(combobox); }, }; diff --git a/packages/lib/src/select/Select.test.tsx b/packages/lib/src/select/Select.test.tsx index 6f9ea2955f..dc09a36a9e 100644 --- a/packages/lib/src/select/Select.test.tsx +++ b/packages/lib/src/select/Select.test.tsx @@ -128,12 +128,13 @@ describe("Select component tests", () => { const submitInput = container.querySelector(`input[name="test"]`); expect(queryByRole("listbox")).toBeFalsy(); expect(getByText("Option 04")).toBeTruthy(); - expect(submitInput.value).toBe("4"); + expect(submitInput?.value).toBe("4"); await userEvent.click(select); - expect(getAllByRole("option")[3].getAttribute("aria-selected")).toBe("true"); - await userEvent.click(getAllByRole("option")[7]); + const options = getAllByRole("option"); + expect(options[3]?.getAttribute("aria-selected")).toBe("true"); + options[7] && (await userEvent.click(options[7])); expect(getByText("Option 08")).toBeTruthy(); - expect(submitInput.value).toBe("8"); + expect(submitInput?.value).toBe("8"); }); test("Multiple selection: Renders with correct default value", async () => { @@ -150,11 +151,12 @@ describe("Select component tests", () => { const submitInput = container.querySelector(`input[name="test"]`); expect(queryByRole("listbox")).toBeFalsy(); expect(getByText("Option 02, Option 04, Option 06")).toBeTruthy(); - expect(submitInput.value).toBe("4,2,6"); + expect(submitInput?.value).toBe("4,2,6"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[2]); + const options = getAllByRole("option"); + options[2] && (await userEvent.click(options[2])); expect(getByText("Option 02, Option 03, Option 04, Option 06")).toBeTruthy(); - expect(submitInput.value).toBe("4,2,6,3"); + expect(submitInput?.value).toBe("4,2,6,3"); }); test("Sends its value when submitted", async () => { @@ -179,7 +181,8 @@ describe("Select component tests", () => { const select = getByRole("combobox"); const submit = getByText("Submit"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[2]); + const options = getAllByRole("option"); + options[2] && (await userEvent.click(options[2])); await userEvent.click(submit); }); @@ -191,7 +194,7 @@ describe("Select component tests", () => { const searchInput = container.querySelectorAll("input")[1]; await userEvent.click(select); await act(async () => { - userEvent.type(searchInput, "test"); + searchInput && userEvent.type(searchInput, "test"); }); expect(queryByRole("listbox")).toBeFalsy(); expect(select.getAttribute("aria-expanded")).toBe("false"); @@ -267,7 +270,8 @@ describe("Select component tests", () => { expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: "", error: "This field is required. Please, enter a value." }); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[0]); + const options = getAllByRole("option"); + options[0] && (await userEvent.click(options[0])); expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalledWith({ value: "1" }); fireEvent.blur(select); @@ -288,16 +292,18 @@ describe("Select component tests", () => { expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: [], error: "This field is required. Please, enter a value." }); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[0]); - await userEvent.click(getAllByRole("option")[1]); + const optionsFirst = getAllByRole("option"); + optionsFirst[0] && (await userEvent.click(optionsFirst[0])); + optionsFirst[1] && (await userEvent.click(optionsFirst[1])); expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalledWith({ value: ["1", "2"] }); fireEvent.blur(select); expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: ["1", "2"] }); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[0]); - await userEvent.click(getAllByRole("option")[1]); + const optionsSecond = getAllByRole("option"); + optionsSecond[0] && (await userEvent.click(optionsSecond[0])); + optionsSecond[1] && (await userEvent.click(optionsSecond[1])); expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalledWith({ value: [], error: "This field is required. Please, enter a value." }); fireEvent.blur(select); @@ -354,13 +360,15 @@ describe("Select component tests", () => { const select = getByRole("combobox"); const submitInput = container.querySelector(`input[name="test"]`); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[2]); + const optionsFirst = getAllByRole("option"); + optionsFirst[2] && (await userEvent.click(optionsFirst[2])); expect(onChange).toHaveBeenCalledWith({ value: "3" }); expect(queryByRole("listbox")).toBeFalsy(); expect(getByText("Option 03")).toBeTruthy(); await userEvent.click(select); - expect(getAllByRole("option")[2].getAttribute("aria-selected")).toBe("true"); - expect(submitInput.value).toBe("3"); + const optionsSecond = getAllByRole("option"); + expect(optionsSecond[2]?.getAttribute("aria-selected")).toBe("true"); + expect(submitInput?.value).toBe("3"); }); test("Non-Grouped Options - Optional renders an empty first option (selected by default) with the placeholder as its label", async () => { @@ -377,8 +385,9 @@ describe("Select component tests", () => { const select = getByRole("combobox"); await userEvent.click(select); expect(getAllByText("Choose an option").length).toBe(2); - expect(getAllByRole("option")[0].getAttribute("aria-selected")).toBe("true"); - await userEvent.click(getAllByRole("option")[0]); + const options = getAllByRole("option"); + expect(options[0]?.getAttribute("aria-selected")).toBe("true"); + options[0] && (await 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 }); @@ -402,12 +411,12 @@ describe("Select component tests", () => { ); const searchInput = container.querySelectorAll("input")[1]; await act(async () => { - userEvent.type(searchInput, "1"); + searchInput && userEvent.type(searchInput, "1"); }); expect(getByText("Placeholder example")).toBeTruthy(); expect(getAllByRole("option").length).toBe(12); await act(async () => { - userEvent.type(searchInput, "123"); + searchInput && userEvent.type(searchInput, "123"); }); expect(queryByText("Placeholder example")).toBeFalsy(); expect(getByText("No matches found")).toBeTruthy(); @@ -462,7 +471,8 @@ describe("Select component tests", () => { expect(queryByRole("listbox")).toBeFalsy(); expect(getByText("Option 20")).toBeTruthy(); await userEvent.click(select); - expect(getAllByRole("option")[20].getAttribute("aria-selected")).toBe("true"); + const options = getAllByRole("option"); + expect(options[20]?.getAttribute("aria-selected")).toBe("true"); }); test("Non-Grouped Options: Searchable - Displays an input for filtering the list of options", async () => { @@ -474,14 +484,15 @@ describe("Select component tests", () => { const searchInput = container.querySelectorAll("input")[1]; await userEvent.click(select); expect(getByRole("listbox")).toBeTruthy(); - await userEvent.type(searchInput, "08"); + searchInput && (await userEvent.type(searchInput, "08")); await userEvent.click(getByRole("option")); expect(onChange).toHaveBeenCalledWith({ value: "8" }); expect(queryByRole("listbox")).toBeFalsy(); expect(getByText("Option 08")).toBeTruthy(); - expect(searchInput.value).toBe(""); + expect(searchInput?.value).toBe(""); await userEvent.click(select); - expect(getAllByRole("option")[7].getAttribute("aria-selected")).toBe("true"); + const options = getAllByRole("option"); + expect(options[7]?.getAttribute("aria-selected")).toBe("true"); }); test("Non-Grouped Options: Searchable - Displays 'No matches found' when there are no filtering results", async () => { @@ -493,7 +504,7 @@ describe("Select component tests", () => { const searchInput = container.querySelectorAll("input")[1]; await userEvent.click(select); expect(getByRole("listbox")).toBeTruthy(); - await userEvent.type(searchInput, "abc"); + searchInput && (await userEvent.type(searchInput, "abc")); expect(getByText("No matches found")).toBeTruthy(); }); @@ -505,7 +516,7 @@ describe("Select component tests", () => { const select = getByRole("combobox"); const searchInput = container.querySelectorAll("input")[1]; await act(async () => { - userEvent.type(searchInput, "2"); + searchInput && userEvent.type(searchInput, "2"); }); expect(getByRole("listbox")).toBeTruthy(); expect(getByText("Option 02")).toBeTruthy(); @@ -515,7 +526,7 @@ describe("Select component tests", () => { await act(async () => { userEvent.click(select); }); - expect(searchInput.value).toBe(""); + expect(searchInput?.value).toBe(""); }); test("Non-Grouped Options: Searchable - Writing displays the listbox, if it was not open", async () => { @@ -528,7 +539,7 @@ describe("Select component tests", () => { await userEvent.click(select); await userEvent.click(select); expect(queryByRole("listbox")).toBeFalsy(); - await userEvent.type(searchInput, "2"); + searchInput && (await userEvent.type(searchInput, "2")); expect(getByRole("listbox")).toBeTruthy(); }); @@ -539,9 +550,9 @@ describe("Select component tests", () => { ); const select = getByRole("combobox"); const searchInput = container.querySelectorAll("input")[1]; - await userEvent.type(searchInput, "Option 02"); + searchInput && (await userEvent.type(searchInput, "Option 02")); fireEvent.keyDown(select, { key: "Esc", code: "Esc", keyCode: 27, charCode: 27 }); - expect(searchInput.value).toBe(""); + expect(searchInput?.value).toBe(""); expect(queryByRole("listbox")).toBeFalsy(); }); @@ -551,7 +562,7 @@ describe("Select component tests", () => { ); const searchInput = container.querySelectorAll("input")[1]; - await userEvent.type(searchInput, "Option 02"); + searchInput && (await userEvent.type(searchInput, "Option 02")); expect(getAllByRole("option").length).toBe(1); const clearSearchButton = getByRole("button"); expect(clearSearchButton.getAttribute("aria-label")).toBe("Clear search"); @@ -570,7 +581,8 @@ describe("Select component tests", () => { const submitInput = container.querySelector(`input[name="test"]`); await userEvent.click(select); expect(getByRole("listbox").getAttribute("aria-multiselectable")).toBe("true"); - await userEvent.click(getAllByRole("option")[10]); + const options = getAllByRole("option"); + options[10] && (await userEvent.click(options[10])); expect(onChange).toHaveBeenCalledWith({ value: ["11"] }); expect(queryByRole("listbox")).toBeTruthy(); expect(getAllByText("Option 11").length).toBe(2); @@ -580,7 +592,7 @@ describe("Select component tests", () => { expect(onChange).toHaveBeenCalledWith({ value: ["11", "19"] }); expect(queryByRole("listbox")).toBeTruthy(); expect(getByText("Option 11, Option 19")).toBeTruthy(); - expect(submitInput.value).toBe("11,19"); + expect(submitInput?.value).toBe("11,19"); }); test("Non-Grouped Options: Multiple selection - Clear action and selection indicator", async () => { @@ -590,9 +602,10 @@ describe("Select component tests", () => { ); const select = getByRole("combobox"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[5]); - await userEvent.click(getAllByRole("option")[8]); - await userEvent.click(getAllByRole("option")[13]); + const options = getAllByRole("option"); + options[5] && (await userEvent.click(options[5])); + options[8] && (await userEvent.click(options[8])); + options[13] && (await userEvent.click(options[13])); expect(onChange).toHaveBeenCalledWith({ value: ["6", "9", "14"] }); expect(queryByRole("listbox")).toBeTruthy(); expect(getByText("Option 06, Option 09, Option 14")).toBeTruthy(); @@ -623,7 +636,8 @@ describe("Select component tests", () => { expect(getByText("(Optional)")).toBeTruthy(); await userEvent.click(select); expect(getAllByText("Choose an option").length).toBe(1); - await userEvent.click(getAllByRole("option")[0]); + const options = getAllByRole("option"); + options[0] && (await userEvent.click(options[0])); expect(onChange).toHaveBeenCalledWith({ value: ["1"] }); expect(getAllByText("Option 01").length).toBe(2); }); @@ -634,7 +648,8 @@ describe("Select component tests", () => { ); const select = getByRole("combobox"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[4]); + const options = getAllByRole("option"); + options[4] && (await userEvent.click(options[4])); expect(getByText("Option 05")).toBeTruthy(); fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 }); expect(select.getAttribute("aria-activedescendant")).toBe("option-4"); @@ -655,7 +670,8 @@ describe("Select component tests", () => { ); const select = getByRole("combobox"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[15]); + const options = getAllByRole("option"); + options[15] && (await userEvent.click(options[15])); expect(queryByRole("listbox")).toBeFalsy(); expect(getByText("Option 16")).toBeTruthy(); await userEvent.click(select); @@ -690,9 +706,9 @@ describe("Select component tests", () => { const groups = getAllByRole("listbox"); expect(groups.length).toBe(3); const groupLabels = getAllByRole("presentation"); - expect(groups[0].getAttribute("aria-labelledby")).toBe(groupLabels[0].id); - expect(groups[1].getAttribute("aria-labelledby")).toBe(groupLabels[1].id); - expect(groups[2].getAttribute("aria-labelledby")).toBe(groupLabels[2].id); + expect(groups[0]?.getAttribute("aria-labelledby")).toBe(groupLabels[0]?.id); + expect(groups[1]?.getAttribute("aria-labelledby")).toBe(groupLabels[1]?.id); + expect(groups[2]?.getAttribute("aria-labelledby")).toBe(groupLabels[2]?.id); expect(getAllByRole("option").length).toBe(18); await userEvent.click(select); expect(queryByRole("list")).toBeFalsy(); @@ -725,13 +741,15 @@ describe("Select component tests", () => { const select = getByRole("combobox"); const submitInput = container.querySelector(`input[name="test"]`); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[8]); + const optionsFirst = getAllByRole("option"); + optionsFirst[8] && (await userEvent.click(optionsFirst[8])); expect(onChange).toHaveBeenCalledWith({ value: "oviedo" }); expect(queryByRole("list")).toBeFalsy(); expect(getByText("Oviedo")).toBeTruthy(); await userEvent.click(select); - expect(getAllByRole("option")[8].getAttribute("aria-selected")).toBe("true"); - expect(submitInput.value).toBe("oviedo"); + const optionsSecond = getAllByRole("option"); + expect(optionsSecond[8]?.getAttribute("aria-selected")).toBe("true"); + expect(submitInput?.value).toBe("oviedo"); }); test("Grouped Options - Optional renders an empty first option (out of any group) with the placeholder as its label", async () => { @@ -748,8 +766,9 @@ describe("Select component tests", () => { const select = getByRole("combobox"); await userEvent.click(select); expect(getAllByText("Placeholder example").length).toBe(2); - expect(getAllByRole("option")[0].getAttribute("aria-selected")).toBe("true"); - await userEvent.click(getAllByRole("option")[0]); + const options = getAllByRole("option"); + expect(options[0]?.getAttribute("aria-selected")).toBe("true"); + options[0] && (await 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 }); @@ -774,10 +793,10 @@ describe("Select component tests", () => { const select = getByRole("combobox"); const searchInput = container.querySelectorAll("input")[1]; await userEvent.click(select); - await userEvent.type(searchInput, "ro"); + searchInput && (await userEvent.type(searchInput, "ro")); expect(getByText("Placeholder example")).toBeTruthy(); expect(getAllByRole("option").length).toBe(6); - await userEvent.type(searchInput, "roro"); + searchInput && (await userEvent.type(searchInput, "roro")); expect(queryByText("Placeholder example")).toBeFalsy(); expect(getByText("No matches found")).toBeTruthy(); }); @@ -831,7 +850,8 @@ describe("Select component tests", () => { expect(queryByRole("list")).toBeFalsy(); expect(getByText("Ebro")).toBeTruthy(); await userEvent.click(select); - expect(getAllByRole("option")[18].getAttribute("aria-selected")).toBe("true"); + const options = getAllByRole("option"); + expect(options[18]?.getAttribute("aria-selected")).toBe("true"); }); test("Grouped Options: Searchable - Displays an input for filtering the list of options", async () => { @@ -843,18 +863,20 @@ describe("Select component tests", () => { const searchInput = container.querySelectorAll("input")[1]; await userEvent.click(select); expect(getByRole("list")).toBeTruthy(); - await userEvent.type(searchInput, "ro"); + searchInput && (await 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(); - await userEvent.click(getAllByRole("option")[4]); + const optionsFirst = getAllByRole("option"); + optionsFirst[4] && (await userEvent.click(optionsFirst[4])); expect(onChange).toHaveBeenCalledWith({ value: "ebro" }); expect(queryByRole("list")).toBeFalsy(); expect(getByText("Ebro")).toBeTruthy(); - expect(searchInput.value).toBe(""); + expect(searchInput?.value).toBe(""); await userEvent.click(select); - expect(getAllByRole("option")[17].getAttribute("aria-selected")).toBe("true"); + const optionsSecond = getAllByRole("option"); + expect(optionsSecond[17]?.getAttribute("aria-selected")).toBe("true"); }); test("Grouped Options: Searchable - Displays 'No matches found' when there are no filtering results", async () => { @@ -866,7 +888,7 @@ describe("Select component tests", () => { const searchInput = container.querySelectorAll("input")[1]; await userEvent.click(select); expect(getByRole("list")).toBeTruthy(); - await userEvent.type(searchInput, "very long string"); + searchInput && (await userEvent.type(searchInput, "very long string")); expect(getByText("No matches found")).toBeTruthy(); }); @@ -878,7 +900,8 @@ describe("Select component tests", () => { const select = getByRole("combobox"); const submitInput = container.querySelector(`input[name="test"]`); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[10]); + const options = getAllByRole("option"); + options[10] && (await userEvent.click(options[10])); expect(onChange).toHaveBeenCalledWith({ value: ["bilbao"] }); expect(queryByRole("list")).toBeTruthy(); expect(getAllByText("Bilbao").length).toBe(2); @@ -888,7 +911,7 @@ describe("Select component tests", () => { expect(onChange).toHaveBeenCalledWith({ value: ["bilbao", "guadalquivir"] }); expect(queryByRole("list")).toBeTruthy(); expect(getByText("Bilbao, Guadalquivir")).toBeTruthy(); - expect(submitInput.value).toBe("bilbao,guadalquivir"); + expect(submitInput?.value).toBe("bilbao,guadalquivir"); }); test("Grouped Options: Multiple selection - Clear action and selection indicator", async () => { @@ -898,10 +921,11 @@ describe("Select component tests", () => { ); const select = getByRole("combobox"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[5]); - await userEvent.click(getAllByRole("option")[8]); - await userEvent.click(getAllByRole("option")[13]); - await userEvent.click(getAllByRole("option")[17]); + const options = getAllByRole("option"); + options[5] && (await userEvent.click(options[5])); + options[8] && (await userEvent.click(options[8])); + options[13] && (await userEvent.click(options[13])); + options[17] && (await userEvent.click(options[17])); expect(onChange).toHaveBeenCalledWith({ value: ["blanco", "oviedo", "duero", "ebro"] }); expect(queryByRole("list")).toBeTruthy(); expect(getByText("Blanco, Oviedo, Duero, Ebro")).toBeTruthy(); @@ -931,7 +955,8 @@ describe("Select component tests", () => { expect(getByText("(Optional)")).toBeTruthy(); await userEvent.click(select); expect(getAllByText("Choose an option").length).toBe(1); - await userEvent.click(getAllByRole("option")[0]); + const options = getAllByRole("option"); + options[0] && (await userEvent.click(options[0])); expect(onChange).toHaveBeenCalledWith({ value: ["azul"] }); expect(getAllByText("Azul").length).toBe(2); }); @@ -942,7 +967,8 @@ describe("Select component tests", () => { ); const select = getByRole("combobox"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[2]); + const options = getAllByRole("option"); + options[2] && (await userEvent.click(options[2])); expect(getByText("Rosa")).toBeTruthy(); fireEvent.keyDown(select, { key: "ArrowUp", code: "ArrowUp", keyCode: 38, charCode: 38 }); expect(select.getAttribute("aria-activedescendant")).toBe("option-2"); @@ -963,7 +989,8 @@ describe("Select component tests", () => { ); const select = getByRole("combobox"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[17]); + const options = getAllByRole("option"); + options[17] && (await userEvent.click(options[17])); expect(getByText("Ebro")).toBeTruthy(); await userEvent.click(select); expect(select.getAttribute("aria-activedescendant")).toBeNull(); @@ -986,9 +1013,10 @@ describe("Select component tests", () => { ); const select = getByRole("combobox"); await userEvent.click(select); - await userEvent.click(getAllByRole("option")[5]); - await userEvent.click(getAllByRole("option")[8]); - await userEvent.click(getAllByRole("option")[13]); + const options = getAllByRole("option"); + options[5] && (await userEvent.click(options[5])); + options[8] && (await userEvent.click(options[8])); + options[13] && (await userEvent.click(options[13])); expect(onChange).toHaveBeenCalledWith({ value: ["6", "9", "14"] }); const clearSelectionButton = getByRole("button"); expect(clearSelectionButton.getAttribute("aria-label")).toBe("Clear selection"); diff --git a/packages/lib/src/sidenav/Sidenav.stories.tsx b/packages/lib/src/sidenav/Sidenav.stories.tsx index 9f30048ffd..a0398bbac5 100644 --- a/packages/lib/src/sidenav/Sidenav.stories.tsx +++ b/packages/lib/src/sidenav/Sidenav.stories.tsx @@ -284,6 +284,6 @@ export const CollapsedActiveGroup: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const collapsableGroups = canvas.getAllByText("Collapsed Group"); - userEvent.click(collapsableGroups[0]); + collapsableGroups[0] && userEvent.click(collapsableGroups[0]); }, }; diff --git a/packages/lib/src/sidenav/Sidenav.test.tsx b/packages/lib/src/sidenav/Sidenav.test.tsx index 72524daf82..26c8733e74 100644 --- a/packages/lib/src/sidenav/Sidenav.test.tsx +++ b/packages/lib/src/sidenav/Sidenav.test.tsx @@ -13,7 +13,7 @@ describe("Sidenav component tests", () => { ); expect(getByText("nav-content-test")).toBeTruthy(); const link = getByText("Link"); - expect(link.closest("a").getAttribute("href")).toBe("#"); + expect(link.closest("a")?.getAttribute("href")).toBe("#"); }); test("Sidenav renders groups correctly", () => { @@ -31,8 +31,10 @@ describe("Sidenav component tests", () => { ); expect(sidenav.getByText("Collapsable")).toBeTruthy(); - expect(sidenav.getAllByRole("button")[0].getAttribute("aria-expanded")).toBe("true"); + const buttonsFirst = sidenav.getAllByRole("button"); + expect(buttonsFirst[0]?.getAttribute("aria-expanded")).toBe("true"); fireEvent.click(sidenav.getByText("Collapsable")); - expect(sidenav.getAllByRole("button")[0].getAttribute("aria-expanded")).toBe("false"); + const buttonsSecond = sidenav.getAllByRole("button"); + expect(buttonsSecond[0]?.getAttribute("aria-expanded")).toBe("false"); }); }); diff --git a/packages/lib/src/slider/Slider.stories.tsx b/packages/lib/src/slider/Slider.stories.tsx index 6165e4cad4..a3deb0d622 100644 --- a/packages/lib/src/slider/Slider.stories.tsx +++ b/packages/lib/src/slider/Slider.stories.tsx @@ -9,7 +9,7 @@ export default { component: DxcSlider, } as Meta; -const labelFormat = (value) => `${value}E100000000000000000000000`; +const labelFormat = (value: number) => `${value}E100000000000000000000000`; const opinionatedTheme = { slider: { diff --git a/packages/lib/src/switch/Switch.accessibility.test.tsx b/packages/lib/src/switch/Switch.accessibility.test.tsx index 0739b21f8e..49445e1c95 100644 --- a/packages/lib/src/switch/Switch.accessibility.test.tsx +++ b/packages/lib/src/switch/Switch.accessibility.test.tsx @@ -1,13 +1,10 @@ import { render } from "@testing-library/react"; -import { axe } from "../../test/accessibility/axe-helper"; +import { axe, formatRules } from "../../test/accessibility/axe-helper"; import { disabledRules as rules } from "../../test/accessibility/rules/specific/switch/disabledRules"; import DxcSwitch from "./Switch"; const disabledRules = { - rules: rules.reduce((rulesObj, rule) => { - rulesObj[rule] = { enabled: false }; - return rulesObj; - }, {}), + rules: formatRules(rules), }; describe("Switch component accessibility tests", () => { diff --git a/packages/lib/src/switch/Switch.test.tsx b/packages/lib/src/switch/Switch.test.tsx index 251854619d..d86b25340f 100644 --- a/packages/lib/src/switch/Switch.test.tsx +++ b/packages/lib/src/switch/Switch.test.tsx @@ -91,7 +91,7 @@ describe("Switch component tests", () => { ); const switchEl = component.getByRole("switch"); const inputEl = component.container.querySelector(`input[name="test"]`); - expect(inputEl.value).toBe("test-defaultChecked"); + expect(inputEl?.value).toBe("test-defaultChecked"); expect(switchEl.getAttribute("aria-checked")).toBe("true"); }); diff --git a/packages/lib/src/table/Table.accessibility.test.tsx b/packages/lib/src/table/Table.accessibility.test.tsx index 4e8582640d..9b59f0dcee 100644 --- a/packages/lib/src/table/Table.accessibility.test.tsx +++ b/packages/lib/src/table/Table.accessibility.test.tsx @@ -1,15 +1,12 @@ import { render } from "@testing-library/react"; -import { axe } from "../../test/accessibility/axe-helper"; +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"; const disabledRules = { - rules: rules.reduce((rulesObj, rule) => { - rulesObj[rule] = { enabled: false }; - return rulesObj; - }, {}), + rules: formatRules(rules), }; // Mocking DOMRect for Radix Primitive Popover diff --git a/packages/lib/src/table/Table.stories.tsx b/packages/lib/src/table/Table.stories.tsx index 230f6fadfd..1eb2fdb6d6 100644 --- a/packages/lib/src/table/Table.stories.tsx +++ b/packages/lib/src/table/Table.stories.tsx @@ -667,6 +667,6 @@ export const DropdownAction: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const nextButton = canvas.getAllByRole("button")[8]; - await userEvent.click(nextButton); + nextButton && (await userEvent.click(nextButton)); }, }; diff --git a/packages/lib/src/table/Table.test.tsx b/packages/lib/src/table/Table.test.tsx index beda12ee5c..874a7d9eae 100644 --- a/packages/lib/src/table/Table.test.tsx +++ b/packages/lib/src/table/Table.test.tsx @@ -112,14 +112,14 @@ describe("Table component tests", () => { const dropdown = getAllByRole("button")[1]; act(() => { - userEvent.click(dropdown); + dropdown && userEvent.click(dropdown); }); expect(getByRole("menu")).toBeTruthy(); const option = getByText("Aliexpress"); userEvent.click(option); expect(onSelectOption).toHaveBeenCalledWith("3"); const action = getAllByRole("button")[0]; - userEvent.click(action); + action && userEvent.click(action); expect(onClick).toHaveBeenCalled(); }); }); diff --git a/packages/lib/src/tabs/Tabs.stories.tsx b/packages/lib/src/tabs/Tabs.stories.tsx index c406afd60c..9b622b17f2 100644 --- a/packages/lib/src/tabs/Tabs.stories.tsx +++ b/packages/lib/src/tabs/Tabs.stories.tsx @@ -3,7 +3,7 @@ import ExampleContainer from "../../.storybook/components/ExampleContainer"; import Title from "../../.storybook/components/Title"; import { HalstackProvider } from "../HalstackContext"; import DxcTabs from "./Tabs"; -import type { Space } from "./types"; +import type { Space } from "../common/utils"; import { Meta, StoryObj } from "@storybook/react/*"; export default { diff --git a/packages/lib/src/tabs/Tabs.test.tsx b/packages/lib/src/tabs/Tabs.test.tsx index fc8dc9c43b..82362b39dd 100644 --- a/packages/lib/src/tabs/Tabs.test.tsx +++ b/packages/lib/src/tabs/Tabs.test.tsx @@ -103,9 +103,9 @@ describe("Tabs component tests", () => { expect(getByText("Tab-2")).toBeTruthy(); expect(getByText("Tab-3")).toBeTruthy(); - 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(tabs[0]?.getAttribute("aria-selected")).toBe("true"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("false"); }); test("Tabs render with correct labels and badges", () => { @@ -118,48 +118,49 @@ describe("Tabs component tests", () => { test("Tabs render with an initially active tab", () => { const { getAllByRole } = render(sampleTabsWithBadge); const tabs = getAllByRole("tab"); - 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(tabs[0]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("true"); }); test("Tabs render with disabled tab", () => { const { getAllByRole } = render(sampleTabsFirstDisabled); - expect(getAllByRole("tab")[0].hasAttribute("disabled")).toBeTruthy(); - expect(getAllByRole("tab")[1].hasAttribute("disabled")).toBeFalsy(); + const tabs = getAllByRole("tab"); + expect(tabs[0]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[1]?.hasAttribute("disabled")).toBeFalsy(); }); test("Tabs interaction", () => { const onTabClick = [jest.fn(), jest.fn(), jest.fn()]; const { getAllByRole } = render(sampleTabsInteraction(onTabClick)); const tabs = getAllByRole("tab"); - fireEvent.click(tabs[0]); + 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"); - fireEvent.click(tabs[1]); + 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]); 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"); - fireEvent.click(tabs[2]); + 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]); expect(onTabClick[2]).toHaveBeenCalled(); - 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(tabs[0]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("true"); }); test("Tabs with active index in disabled tab should not be selectable", () => { const onTabClick = [jest.fn(), jest.fn(), jest.fn()]; const { getAllByRole } = render(sampleTabsLastTabNonDisabledFirstActive(onTabClick)); const tabs = getAllByRole("tab"); - 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(tabs[0].hasAttribute("disabled")).toBeTruthy(); - expect(tabs[1].hasAttribute("disabled")).toBeTruthy(); - expect(tabs[2].hasAttribute("disabled")).toBeFalsy(); + 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(tabs[0]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[1]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[2]?.hasAttribute("disabled")).toBeFalsy(); }); test("Select tabs with keyboard event arrows", () => { @@ -169,51 +170,51 @@ describe("Tabs component tests", () => { const tab1 = getByText("Tab-1"); const tabs = getAllByRole("tab"); fireEvent.click(tab1); - 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(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: "ArrowRight" }); expect(tabs[1]).toHaveFocus(); - 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"); + 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(); - 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"); + 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(); - 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"); + 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(); - 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"); + 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(); - 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"); + 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(); - 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"); + 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(); }); @@ -224,36 +225,36 @@ describe("Tabs component tests", () => { const tab1 = getByText("Tab-1"); const tabs = getAllByRole("tab"); fireEvent.click(tab1); - 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(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: "ArrowRight" }); expect(tabs[2]).toHaveFocus(); - 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"); + 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"); expect(onTabClick[2]).toHaveBeenCalled(); }); test("Controlled tabs interaction", () => { const onTabClick = [jest.fn(), jest.fn(), jest.fn()]; const { getAllByRole } = render(sampleControlledTabsInteraction(onTabClick)); const tabs = getAllByRole("tab"); - fireEvent.click(tabs[0]); + 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"); - fireEvent.click(tabs[1]); + 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]); 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"); - fireEvent.click(tabs[2]); + 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]); expect(onTabClick[2]).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"); + expect(tabs[0]?.getAttribute("aria-selected")).toBe("true"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("false"); }); }); diff --git a/packages/lib/src/tabs/TabsLegacy.stories.tsx b/packages/lib/src/tabs/TabsLegacy.stories.tsx index 11e97a66bc..a5c2968675 100644 --- a/packages/lib/src/tabs/TabsLegacy.stories.tsx +++ b/packages/lib/src/tabs/TabsLegacy.stories.tsx @@ -21,7 +21,7 @@ const iconSVG = ( ); -const tabs: any = [ +const tabs = [ { label: "Tab 1", }, @@ -46,7 +46,7 @@ const tabs: any = [ }, ]; -const disabledTabs: any = [ +const disabledTabs = [ { label: "Tab 1", isDisabled: true, @@ -61,7 +61,7 @@ const disabledTabs: any = [ }, ]; -const firstDisabledTabs: any = [ +const firstDisabledTabs = [ { label: "Tab 1", isDisabled: true, diff --git a/packages/lib/src/tabs/TabsLegacy.test.tsx b/packages/lib/src/tabs/TabsLegacy.test.tsx index 643fc8cb1a..0b130edb3a 100644 --- a/packages/lib/src/tabs/TabsLegacy.test.tsx +++ b/packages/lib/src/tabs/TabsLegacy.test.tsx @@ -59,9 +59,9 @@ describe("Tabs component tests (Legacy)", () => { expect(getByText("Tab-1")).toBeTruthy(); expect(getByText("Tab-2")).toBeTruthy(); expect(getByText("Tab-3")).toBeTruthy(); - 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(tabs[0]?.getAttribute("aria-selected")).toBe("true"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("false"); }); test("Tabs render with correct labels and badges", () => { @@ -74,9 +74,9 @@ describe("Tabs component tests (Legacy)", () => { test("Tabs render with an initially active tab", () => { const { getAllByRole } = render(); const tabs = getAllByRole("tab"); - 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(tabs[0]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("true"); }); test("Tabs render with disabled tab", () => { @@ -93,8 +93,9 @@ describe("Tabs component tests (Legacy)", () => { ]} /> ); - expect(getAllByRole("tab")[0].hasAttribute("disabled")).toBeTruthy(); - expect(getAllByRole("tab")[1].hasAttribute("disabled")).toBeFalsy(); + const tabs = getAllByRole("tab"); + expect(tabs[0]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[1]?.hasAttribute("disabled")).toBeFalsy(); }); test("Uncontrolled tabs", () => { @@ -107,14 +108,14 @@ describe("Tabs component tests (Legacy)", () => { const tab2 = getByText("Tab-2"); fireEvent.click(tab2); expect(onTabClick).toHaveBeenCalledWith(1); - 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(tabs[0]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("true"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("false"); fireEvent.click(tab1); expect(onTabClick).toHaveBeenCalledWith(0); - 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(tabs[0]?.getAttribute("aria-selected")).toBe("true"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("false"); }); test("Controlled tabs", () => { @@ -123,16 +124,16 @@ describe("Tabs component tests (Legacy)", () => { ); const tabs = getAllByRole("tab"); - fireEvent.click(tabs[1]); + tabs[1] && fireEvent.click(tabs[1]); expect(onTabClick).toHaveBeenCalledWith(1); - expect(tabs[0].getAttribute("aria-selected")).toBe("true"); - expect(tabs[1].getAttribute("aria-selected")).toBe("false"); - expect(tabs[2].getAttribute("aria-selected")).toBe("false"); - fireEvent.click(tabs[2]); + 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]); expect(onTabClick).toHaveBeenCalledWith(2); - 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(tabs[0]?.getAttribute("aria-selected")).toBe("true"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("false"); }); test("Uncontrolled tabs should have focus in the first non-disabled tab", () => { @@ -141,12 +142,12 @@ describe("Tabs component tests (Legacy)", () => { ); const tabs = getAllByRole("tab"); - expect(tabs[0].hasAttribute("disabled")).toBeTruthy(); - expect(tabs[1].hasAttribute("disabled")).toBeTruthy(); - expect(tabs[2].hasAttribute("disabled")).toBeFalsy(); - 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(tabs[0]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[1]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[2]?.hasAttribute("disabled")).toBeFalsy(); + expect(tabs[0]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[1]?.getAttribute("aria-selected")).toBe("false"); + expect(tabs[2]?.getAttribute("aria-selected")).toBe("true"); }); test("Controlled tabs with active index in disabled tab should not change focus to the first available tab", () => { @@ -159,20 +160,20 @@ describe("Tabs component tests (Legacy)", () => { > ); const tabs = getAllByRole("tab"); - 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(tabs[0].hasAttribute("disabled")).toBeTruthy(); - expect(tabs[1].hasAttribute("disabled")).toBeTruthy(); - expect(tabs[2].hasAttribute("disabled")).toBeFalsy(); - fireEvent.click(tabs[2]); + 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(tabs[0]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[1]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[2]?.hasAttribute("disabled")).toBeFalsy(); + tabs[2] && fireEvent.click(tabs[2]); expect(onTabClick).toHaveBeenCalledWith(2); - 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(tabs[0].hasAttribute("disabled")).toBeTruthy(); - expect(tabs[1].hasAttribute("disabled")).toBeTruthy(); - expect(tabs[2].hasAttribute("disabled")).toBeFalsy(); + 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(tabs[0]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[1]?.hasAttribute("disabled")).toBeTruthy(); + expect(tabs[2]?.hasAttribute("disabled")).toBeFalsy(); }); test("Select tabs with keyboard event arrows", () => { @@ -184,45 +185,45 @@ describe("Tabs component tests (Legacy)", () => { const tab1 = getByText("Tab-1"); const tabs = getAllByRole("tab"); fireEvent.click(tab1); - 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(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).toHaveBeenCalledWith(0); fireEvent.keyDown(tabList, { key: "ArrowRight" }); fireEvent.keyDown(tabList, { 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(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).toHaveBeenCalledWith(1); fireEvent.keyDown(tabList, { key: "ArrowRight" }); fireEvent.keyDown(tabList, { 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(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).toHaveBeenCalledWith(2); fireEvent.keyDown(tabList, { key: "ArrowLeft" }); fireEvent.keyDown(tabList, { 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(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).toHaveBeenCalledWith(1); fireEvent.keyDown(tabList, { key: "ArrowLeft" }); fireEvent.keyDown(tabList, { 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(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).toHaveBeenCalledWith(0); fireEvent.keyDown(tabList, { key: "ArrowLeft" }); fireEvent.keyDown(tabList, { 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(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).toHaveBeenCalledWith(2); fireEvent.keyDown(tabList, { key: "ArrowRight" }); fireEvent.keyDown(tabList, { 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(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).toHaveBeenCalledWith(0); }); @@ -235,15 +236,15 @@ describe("Tabs component tests (Legacy)", () => { const tab1 = getByText("Tab-1"); const tabs = getAllByRole("tab"); fireEvent.click(tab1); - 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(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).toHaveBeenCalledWith(0); fireEvent.keyDown(tabList, { key: "ArrowRight" }); fireEvent.keyDown(tabList, { 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"); + 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).toHaveBeenCalledWith(2); }); }); diff --git a/packages/lib/src/text-input/TextInput.test.tsx b/packages/lib/src/text-input/TextInput.test.tsx index 9e26dd1b62..ec1994dae1 100644 --- a/packages/lib/src/text-input/TextInput.test.tsx +++ b/packages/lib/src/text-input/TextInput.test.tsx @@ -391,7 +391,7 @@ describe("TextInput component tests", () => { const input = getByRole("textbox") as HTMLInputElement; userEvent.type(input, "test"); expect(input.value).toBe("test"); - await userEvent.click(search); + search && (await userEvent.click(search)); expect(handlerOnSubmit).not.toHaveBeenCalled(); await userEvent.click(submit); expect(handlerOnSubmit).toHaveBeenCalled(); @@ -434,9 +434,9 @@ describe("TextInput component tests", () => { expect(input.getAttribute("aria-required")).toBe("true"); userEvent.type(input, "Text"); const clear = getAllByRole("button")[0]; - expect(clear.getAttribute("aria-label")).toBe("Clear field"); + clear && expect(clear.getAttribute("aria-label")).toBe("Clear field"); const search = getAllByRole("button")[1]; - expect(search.getAttribute("aria-label")).toBe("Search"); + search && expect(search.getAttribute("aria-label")).toBe("Search"); }); test("Autosuggest has correct accessibility attributes", () => { @@ -453,7 +453,7 @@ describe("TextInput component tests", () => { expect(input.getAttribute("aria-controls")).toBe(list.id); expect(input.getAttribute("aria-expanded")).toBe("true"); const options = getAllByRole("option"); - expect(options[0].getAttribute("aria-selected")).toBeNull(); + options[0] && expect(options[0].getAttribute("aria-selected")).toBeNull(); }); test("Mouse wheel interaction does not affect the text value", () => { @@ -549,7 +549,7 @@ describe("TextInput component synchronous autosuggest tests", () => { ); const input = queryByRole("textbox"); - fireEvent.focus(input); + input && fireEvent.focus(input); expect(queryByRole("listbox")).toBeFalsy(); }); diff --git a/packages/lib/src/toast/Toast.accessibility.test.tsx b/packages/lib/src/toast/Toast.accessibility.test.tsx index 00f1763b68..6633a8dfa3 100644 --- a/packages/lib/src/toast/Toast.accessibility.test.tsx +++ b/packages/lib/src/toast/Toast.accessibility.test.tsx @@ -36,7 +36,7 @@ describe("Toast component accessibility tests", () => { const { container } = render(); const results = await axe(container); const button = container.querySelector("button"); - userEvent.click(button); + 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 70835f4898..6a854b9d85 100644 --- a/packages/lib/src/toast/Toast.stories.tsx +++ b/packages/lib/src/toast/Toast.stories.tsx @@ -257,7 +257,7 @@ const ToastsQueue = () => ( ); -const playFunc = async ({ canvasElement }) => { +const playFunc = async ({ canvasElement }: { canvasElement: HTMLElement }) => { const canvas = within(canvasElement); await userEvent.click(canvas.getByText("Show default toast")); await userEvent.click(canvas.getByText("Show info toast")); diff --git a/packages/lib/src/toast/Toast.test.tsx b/packages/lib/src/toast/Toast.test.tsx index c483e471af..ab2f39b3cb 100644 --- a/packages/lib/src/toast/Toast.test.tsx +++ b/packages/lib/src/toast/Toast.test.tsx @@ -12,7 +12,7 @@ const ToastPage = ({ onClick }: { onClick?: () => void }) => { const removeLoadingToast = toast.loading({ message: "Loading process..." }); setTimeout(() => { - removeLoadingToast(); + removeLoadingToast?.(); toast.success({ message: "The process ended successfully." }); }, 5000); }; diff --git a/packages/lib/src/toggle-group/ToggleGroup.test.tsx b/packages/lib/src/toggle-group/ToggleGroup.test.tsx index 7aebc04741..eb8c3be84d 100644 --- a/packages/lib/src/toggle-group/ToggleGroup.test.tsx +++ b/packages/lib/src/toggle-group/ToggleGroup.test.tsx @@ -74,14 +74,14 @@ describe("Toggle group component tests", () => { const onChange = jest.fn(); const { getAllByRole } = render(); const toggleOptions = getAllByRole("button"); - fireEvent.click(toggleOptions[0]); + toggleOptions[0] && fireEvent.click(toggleOptions[0]); expect(onChange).toHaveBeenCalledWith([1]); - fireEvent.click(toggleOptions[1]); - fireEvent.click(toggleOptions[3]); + toggleOptions[1] && fireEvent.click(toggleOptions[1]); + 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"); - expect(toggleOptions[3].getAttribute("aria-pressed")).toBe("true"); + expect(toggleOptions[0]?.getAttribute("aria-pressed")).toBe("true"); + expect(toggleOptions[1]?.getAttribute("aria-pressed")).toBe("true"); + expect(toggleOptions[3]?.getAttribute("aria-pressed")).toBe("true"); }); test("Controlled multiple toggle returns always same values", () => { @@ -98,13 +98,13 @@ describe("Toggle group component tests", () => { test("Single selection: Renders with correct default value", () => { const { getAllByRole } = render(); const toggleOptions = getAllByRole("button"); - expect(toggleOptions[1].getAttribute("aria-pressed")).toBe("true"); + expect(toggleOptions[1]?.getAttribute("aria-pressed")).toBe("true"); }); test("Multiple selection: Renders with correct default value", () => { const { getAllByRole } = render(); const toggleOptions = getAllByRole("button"); - expect(toggleOptions[1].getAttribute("aria-pressed")).toBe("true"); - expect(toggleOptions[3].getAttribute("aria-pressed")).toBe("true"); + expect(toggleOptions[1]?.getAttribute("aria-pressed")).toBe("true"); + expect(toggleOptions[3]?.getAttribute("aria-pressed")).toBe("true"); }); }); diff --git a/packages/lib/src/wizard/Wizard.test.tsx b/packages/lib/src/wizard/Wizard.test.tsx index 85869ec069..d64d101ef7 100644 --- a/packages/lib/src/wizard/Wizard.test.tsx +++ b/packages/lib/src/wizard/Wizard.test.tsx @@ -18,8 +18,8 @@ describe("Wizard components tests", () => { const steps = getAllByRole("button"); expect(getByText("first-step")).toBeTruthy(); expect(getByText("second-step")).toBeTruthy(); - expect(steps[0].getAttribute("aria-current")).toBe("step"); - expect(steps[1].getAttribute("aria-current")).toBe("false"); + expect(steps[0]?.getAttribute("aria-current")).toBe("step"); + expect(steps[1]?.getAttribute("aria-current")).toBe("false"); }); test("Wizard renders with initially selected step", () => { @@ -37,7 +37,7 @@ describe("Wizard components tests", () => { /> ); const steps = getAllByRole("button"); - expect(steps[1].getAttribute("aria-current")).toBe("step"); + expect(steps[1]?.getAttribute("aria-current")).toBe("step"); }); test("Click on step text", () => { @@ -130,8 +130,8 @@ describe("Wizard components tests", () => { /> ); const steps = getAllByRole("button"); - fireEvent.click(steps[1]); - fireEvent.click(steps[0]); + steps[1] && fireEvent.click(steps[1]); + steps[0] && fireEvent.click(steps[0]); expect(onClick).toHaveBeenCalledTimes(2); expect(onClick).toHaveBeenNthCalledWith(1, 1); expect(onClick).toHaveBeenNthCalledWith(2, 0); From 26f9307db23cdcde3ccb9cadca5ff788613c8cd7 Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Mon, 23 Dec 2024 12:27:51 +0100 Subject: [PATCH 30/41] Fixed problem with interaction in DataGrid stories --- .../lib/src/data-grid/DataGrid.stories.tsx | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index 1243285d95..b5a906314b 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -966,17 +966,24 @@ export const DataGridSortedExpanded: Story = { render: DataGridSortedExpandable, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const buttons = canvas.getAllByRole("button"); const columnHeaders = canvas.getAllByRole("columnheader"); - buttons[0] && (await userEvent.click(buttons[0])); - buttons[1] && (await userEvent.click(buttons[1])); + const button0 = canvas.getAllByRole("button")[0]; + button0 && (await userEvent.click(button0)); + const button1 = canvas.getAllByRole("button")[1]; + button1 && (await userEvent.click(button1)); columnHeaders[4] && (await userEvent.click(columnHeaders[4])); - buttons[9] && (await userEvent.click(buttons[9])); - buttons[10] && (await userEvent.click(buttons[10])); + const button9 = canvas.getAllByRole("button")[9]; + button9 && (await userEvent.click(button9)); + const button10 = canvas.getAllByRole("button")[10]; + button10 && (await userEvent.click(button10)); columnHeaders[10] && (await userEvent.click(columnHeaders[10])); - buttons[16] && (await userEvent.click(buttons[16])); - buttons[43] && (await userEvent.click(buttons[43])); - buttons[36] && (await userEvent.click(buttons[36])); - buttons[37] && (await userEvent.click(buttons[37])); + const button16 = canvas.getAllByRole("button")[16]; + button16 && (await userEvent.click(button16)); + const button43 = canvas.getAllByRole("button")[43]; + button43 && (await userEvent.click(button43)); + const button36 = canvas.getAllByRole("button")[36]; + button36 && (await userEvent.click(button36)); + const button37 = canvas.getAllByRole("button")[37]; + button37 && (await userEvent.click(button37)); }, }; From 0773f4679cb7df99d2eaedb9d837bd449002a877 Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Mon, 23 Dec 2024 12:29:40 +0100 Subject: [PATCH 31/41] Fixed problem with interaction in DataGrid stories --- packages/lib/src/data-grid/DataGrid.stories.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index b5a906314b..b5b1d432b0 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -971,12 +971,14 @@ export const DataGridSortedExpanded: Story = { button0 && (await userEvent.click(button0)); const button1 = canvas.getAllByRole("button")[1]; button1 && (await userEvent.click(button1)); - columnHeaders[4] && (await userEvent.click(columnHeaders[4])); + const columnHeaders4 = canvas.getAllByRole("columnheader")[4]; + columnHeaders4 && (await userEvent.click(columnHeaders4)); const button9 = canvas.getAllByRole("button")[9]; button9 && (await userEvent.click(button9)); const button10 = canvas.getAllByRole("button")[10]; button10 && (await userEvent.click(button10)); - columnHeaders[10] && (await userEvent.click(columnHeaders[10])); + const columnHeaders10 = canvas.getAllByRole("columnheader")[10]; + columnHeaders10 && (await userEvent.click(columnHeaders10)); const button16 = canvas.getAllByRole("button")[16]; button16 && (await userEvent.click(button16)); const button43 = canvas.getAllByRole("button")[43]; From e1e5fbed6b9327b689840dd74fc67f158dbacf94 Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Mon, 23 Dec 2024 12:29:54 +0100 Subject: [PATCH 32/41] Fixed problem with interaction in DataGrid stories --- packages/lib/src/data-grid/DataGrid.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index b5b1d432b0..b7347c711e 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -966,7 +966,6 @@ export const DataGridSortedExpanded: Story = { render: DataGridSortedExpandable, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const columnHeaders = canvas.getAllByRole("columnheader"); const button0 = canvas.getAllByRole("button")[0]; button0 && (await userEvent.click(button0)); const button1 = canvas.getAllByRole("button")[1]; From 162780a09d303437d5f9218b8078cead42032a9a Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Mon, 23 Dec 2024 12:39:51 +0100 Subject: [PATCH 33/41] Fixed problem with interaction in DataGrid stories --- .../lib/src/data-grid/DataGrid.stories.tsx | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index b7347c711e..5304b91893 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -940,25 +940,28 @@ export const DataGridSortedWithChildren: Story = { render: DataGridSortedChildren, play: async ({ canvasElement }) => { const canvas = within(canvasElement); - const checkboxes = canvas.getAllByRole("checkbox"); - const columnheaders = canvas.getAllByRole("columnheader"); - - checkboxes[0] && (await userEvent.click(checkboxes[0])); + const checkbox0 = canvas.getAllByRole("checkbox")[0]; + 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")); - columnheaders[1] && (await userEvent.click(columnheaders[1])); - columnheaders[1] && (await userEvent.click(columnheaders[1])); - checkboxes[5] && (await userEvent.click(checkboxes[5])); - - checkboxes[13] && (await userEvent.click(checkboxes[13])); + const columnheader1_1 = canvas.getAllByRole("columnheader")[1]; + columnheader1_1 && (await userEvent.click(columnheader1_1)); + const columnheader1_2 = canvas.getAllByRole("columnheader")[1]; + columnheader1_2 && (await userEvent.click(columnheader1_2)); + const checkbox5 = canvas.getAllByRole("checkbox")[5]; + checkbox5 && (await userEvent.click(checkbox5)); + const checkbox13 = canvas.getAllByRole("checkbox")[13]; + 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")); - columnheaders[4] && (await userEvent.click(columnheaders[4])); - checkboxes[18] && (await userEvent.click(checkboxes[18])); + const columnheader4 = canvas.getAllByRole("columnheader")[4]; + columnheader4 && (await userEvent.click(columnheader4)); + const checkbox18 = canvas.getAllByRole("checkbox")[18]; + checkbox18 && (await userEvent.click(checkbox18)); }, }; From d266130d9a23b23dfa64b4dc08dd29d60fd1e38f Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Tue, 24 Dec 2024 08:07:06 +0100 Subject: [PATCH 34/41] Changed var names in tests for better readability --- packages/lib/src/select/Select.test.tsx | 36 +++++++++++------------ packages/lib/src/sidenav/Sidenav.test.tsx | 8 ++--- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/lib/src/select/Select.test.tsx b/packages/lib/src/select/Select.test.tsx index dc09a36a9e..6ac3256158 100644 --- a/packages/lib/src/select/Select.test.tsx +++ b/packages/lib/src/select/Select.test.tsx @@ -292,18 +292,18 @@ describe("Select component tests", () => { expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: [], error: "This field is required. Please, enter a value." }); await userEvent.click(select); - const optionsFirst = getAllByRole("option"); - optionsFirst[0] && (await userEvent.click(optionsFirst[0])); - optionsFirst[1] && (await userEvent.click(optionsFirst[1])); + let options = getAllByRole("option"); + options[0] && (await userEvent.click(options[0])); + options[1] && (await userEvent.click(options[1])); expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalledWith({ value: ["1", "2"] }); fireEvent.blur(select); expect(onBlur).toHaveBeenCalled(); expect(onBlur).toHaveBeenCalledWith({ value: ["1", "2"] }); await userEvent.click(select); - const optionsSecond = getAllByRole("option"); - optionsSecond[0] && (await userEvent.click(optionsSecond[0])); - optionsSecond[1] && (await userEvent.click(optionsSecond[1])); + options = getAllByRole("option"); + options[0] && (await userEvent.click(options[0])); + options[1] && (await userEvent.click(options[1])); expect(onChange).toHaveBeenCalled(); expect(onChange).toHaveBeenCalledWith({ value: [], error: "This field is required. Please, enter a value." }); fireEvent.blur(select); @@ -360,14 +360,14 @@ describe("Select component tests", () => { const select = getByRole("combobox"); const submitInput = container.querySelector(`input[name="test"]`); await userEvent.click(select); - const optionsFirst = getAllByRole("option"); - optionsFirst[2] && (await userEvent.click(optionsFirst[2])); + let options = getAllByRole("option"); + options[2] && (await userEvent.click(options[2])); expect(onChange).toHaveBeenCalledWith({ value: "3" }); expect(queryByRole("listbox")).toBeFalsy(); expect(getByText("Option 03")).toBeTruthy(); await userEvent.click(select); - const optionsSecond = getAllByRole("option"); - expect(optionsSecond[2]?.getAttribute("aria-selected")).toBe("true"); + options = getAllByRole("option"); + expect(options[2]?.getAttribute("aria-selected")).toBe("true"); expect(submitInput?.value).toBe("3"); }); @@ -741,14 +741,14 @@ describe("Select component tests", () => { const select = getByRole("combobox"); const submitInput = container.querySelector(`input[name="test"]`); await userEvent.click(select); - const optionsFirst = getAllByRole("option"); - optionsFirst[8] && (await userEvent.click(optionsFirst[8])); + let options = getAllByRole("option"); + options[8] && (await userEvent.click(options[8])); expect(onChange).toHaveBeenCalledWith({ value: "oviedo" }); expect(queryByRole("list")).toBeFalsy(); expect(getByText("Oviedo")).toBeTruthy(); await userEvent.click(select); - const optionsSecond = getAllByRole("option"); - expect(optionsSecond[8]?.getAttribute("aria-selected")).toBe("true"); + options = getAllByRole("option"); + expect(options[8]?.getAttribute("aria-selected")).toBe("true"); expect(submitInput?.value).toBe("oviedo"); }); @@ -868,15 +868,15 @@ describe("Select component tests", () => { expect(getAllByRole("option").length).toBe(5); expect(getByText("Colores")).toBeTruthy(); expect(getByText("Ríos españoles")).toBeTruthy(); - const optionsFirst = getAllByRole("option"); - optionsFirst[4] && (await userEvent.click(optionsFirst[4])); + let options = getAllByRole("option"); + options[4] && (await userEvent.click(options[4])); expect(onChange).toHaveBeenCalledWith({ value: "ebro" }); expect(queryByRole("list")).toBeFalsy(); expect(getByText("Ebro")).toBeTruthy(); expect(searchInput?.value).toBe(""); await userEvent.click(select); - const optionsSecond = getAllByRole("option"); - expect(optionsSecond[17]?.getAttribute("aria-selected")).toBe("true"); + options = getAllByRole("option"); + expect(options[17]?.getAttribute("aria-selected")).toBe("true"); }); test("Grouped Options: Searchable - Displays 'No matches found' when there are no filtering results", async () => { diff --git a/packages/lib/src/sidenav/Sidenav.test.tsx b/packages/lib/src/sidenav/Sidenav.test.tsx index 26c8733e74..f124b938aa 100644 --- a/packages/lib/src/sidenav/Sidenav.test.tsx +++ b/packages/lib/src/sidenav/Sidenav.test.tsx @@ -31,10 +31,10 @@ describe("Sidenav component tests", () => { ); expect(sidenav.getByText("Collapsable")).toBeTruthy(); - const buttonsFirst = sidenav.getAllByRole("button"); - expect(buttonsFirst[0]?.getAttribute("aria-expanded")).toBe("true"); + let buttons = sidenav.getAllByRole("button"); + expect(buttons[0]?.getAttribute("aria-expanded")).toBe("true"); fireEvent.click(sidenav.getByText("Collapsable")); - const buttonsSecond = sidenav.getAllByRole("button"); - expect(buttonsSecond[0]?.getAttribute("aria-expanded")).toBe("false"); + buttons = sidenav.getAllByRole("button"); + expect(buttons[0]?.getAttribute("aria-expanded")).toBe("false"); }); }); From 6e49d1a2fa8051339950107cd7e63e293444105f Mon Sep 17 00:00:00 2001 From: Mil4n0r Date: Tue, 24 Dec 2024 08:09:00 +0100 Subject: [PATCH 35/41] Changed var names in tests for better readability --- packages/lib/src/data-grid/DataGrid.stories.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/lib/src/data-grid/DataGrid.stories.tsx b/packages/lib/src/data-grid/DataGrid.stories.tsx index 5304b91893..5928392240 100644 --- a/packages/lib/src/data-grid/DataGrid.stories.tsx +++ b/packages/lib/src/data-grid/DataGrid.stories.tsx @@ -946,10 +946,10 @@ export const DataGridSortedWithChildren: Story = { 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")); - const columnheader1_1 = canvas.getAllByRole("columnheader")[1]; - columnheader1_1 && (await userEvent.click(columnheader1_1)); - const columnheader1_2 = canvas.getAllByRole("columnheader")[1]; - columnheader1_2 && (await userEvent.click(columnheader1_2)); + let columnheader1 = canvas.getAllByRole("columnheader")[1]; + columnheader1 && (await userEvent.click(columnheader1)); + columnheader1 = canvas.getAllByRole("columnheader")[1]; + columnheader1 && (await userEvent.click(columnheader1)); const checkbox5 = canvas.getAllByRole("checkbox")[5]; checkbox5 && (await userEvent.click(checkbox5)); const checkbox13 = canvas.getAllByRole("checkbox")[13]; From 7d3ea1bf7ee989cb741cb429c005b43256704d07 Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Mon, 30 Dec 2024 14:45:05 +0100 Subject: [PATCH 36/41] Removed no-longer-needed nullish coallescing operators --- packages/lib/src/quick-nav/QuickNav.tsx | 2 +- packages/lib/src/radio-group/RadioGroup.tsx | 2 +- packages/lib/src/text-input/TextInput.tsx | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/lib/src/quick-nav/QuickNav.tsx b/packages/lib/src/quick-nav/QuickNav.tsx index 0103d93c03..333d0e4c28 100644 --- a/packages/lib/src/quick-nav/QuickNav.tsx +++ b/packages/lib/src/quick-nav/QuickNav.tsx @@ -16,7 +16,7 @@ const DxcQuickNav = ({ title, links }: QuickNavTypes): JSX.Element => { - + {links.map((link) => (
  • diff --git a/packages/lib/src/radio-group/RadioGroup.tsx b/packages/lib/src/radio-group/RadioGroup.tsx index db782bfb7f..eee84f09f1 100644 --- a/packages/lib/src/radio-group/RadioGroup.tsx +++ b/packages/lib/src/radio-group/RadioGroup.tsx @@ -46,7 +46,7 @@ const DxcRadioGroup = forwardRef( ? [ ...options, { - label: optionalItemLabel ?? translatedLabels.radioGroup.optionalItemLabelDefault ?? "", + label: optionalItemLabel ?? translatedLabels.radioGroup.optionalItemLabelDefault, value: "", disabled, }, diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx index 6a508131b0..185f56c814 100644 --- a/packages/lib/src/text-input/TextInput.tsx +++ b/packages/lib/src/text-input/TextInput.tsx @@ -527,7 +527,7 @@ const DxcTextInput = forwardRef( onClick={handleClearActionOnClick} icon="close" tabIndex={tabIndex} - title={translatedLabels.textInput.clearFieldActionTitle ?? ""} + title={translatedLabels.textInput.clearFieldActionTitle} /> )} {numberInputContext?.typeNumber === "number" && ( @@ -537,7 +537,7 @@ const DxcTextInput = forwardRef( icon="remove" tabIndex={tabIndex} ref={actionRef} - title={translatedLabels.numberInput.decrementValueTitle ?? ""} + title={translatedLabels.numberInput.decrementValueTitle} disabled={disabled} /> ( icon="add" tabIndex={tabIndex} ref={actionRef} - title={translatedLabels.numberInput.incrementValueTitle ?? ""} + title={translatedLabels.numberInput.incrementValueTitle} disabled={disabled} /> From 3dd15f9fdf3fc08d163011324a7234a5c7390e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 2 Jan 2025 13:03:47 +0100 Subject: [PATCH 37/41] Removing Background Color Context --- packages/lib/src/BackgroundColorContext.tsx | 28 ------------------- packages/lib/src/select/Listbox.tsx | 2 +- packages/lib/src/select/Select.tsx | 2 +- .../src/select/{selectUtils.ts => utils.ts} | 0 4 files changed, 2 insertions(+), 30 deletions(-) delete mode 100644 packages/lib/src/BackgroundColorContext.tsx rename packages/lib/src/select/{selectUtils.ts => utils.ts} (100%) diff --git a/packages/lib/src/BackgroundColorContext.tsx b/packages/lib/src/BackgroundColorContext.tsx deleted file mode 100644 index df3e94060f..0000000000 --- a/packages/lib/src/BackgroundColorContext.tsx +++ /dev/null @@ -1,28 +0,0 @@ -// import { createContext, ReactNode, useMemo } from "react"; -// import Color from "color"; - -// type BackgroundColors = "dark" | "light"; -// const BackgroundColorContext = createContext(null); - -// const getColorType = (hexColor: string): BackgroundColors => { -// try { -// if (hexColor) { -// const hslColor = Color(hexColor).hsl(); -// const lightnessColor = hslColor.lightness(); -// return lightnessColor <= 30 ? "dark" : "light"; -// } else { -// return "light"; -// } -// } catch (e) { -// return "light"; -// } -// }; - -// type BackgroundColorProviderPropsType = { -// color: string; -// children: ReactNode; -// }; -// const BackgroundColorProvider = ({ color, children }: BackgroundColorProviderPropsType): JSX.Element => { -// const colorType = useMemo(() => getColorType(color), [color]); -// return {children}; -// }; diff --git a/packages/lib/src/select/Listbox.tsx b/packages/lib/src/select/Listbox.tsx index 2b8962364f..4096779b38 100644 --- a/packages/lib/src/select/Listbox.tsx +++ b/packages/lib/src/select/Listbox.tsx @@ -3,7 +3,7 @@ import styled from "styled-components"; import DxcIcon from "../icon/Icon"; import { HalstackLanguageContext } from "../HalstackContext"; import ListOption from "./ListOption"; -import { groupsHaveOptions } from "./selectUtils"; +import { groupsHaveOptions } from "./utils"; import { ListboxProps, ListOptionGroupType, ListOptionType } from "./types"; const Listbox = ({ diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index 320c181738..69f9ea8195 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -17,7 +17,7 @@ import { groupsHaveOptions, isArrayOfOptionGroups, notOptionalCheck, -} from "./selectUtils"; +} from "./utils"; import SelectPropsType, { ListOptionType, RefType } from "./types"; const DxcSelect = forwardRef( diff --git a/packages/lib/src/select/selectUtils.ts b/packages/lib/src/select/utils.ts similarity index 100% rename from packages/lib/src/select/selectUtils.ts rename to packages/lib/src/select/utils.ts From 55471703d57c71635dd5910375a1693105e2485b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:27:26 +0100 Subject: [PATCH 38/41] Code updates --- packages/lib/src/paginator/Paginator.tsx | 4 +-- packages/lib/src/switch/Switch.tsx | 2 +- packages/lib/src/tabs/Tab.tsx | 8 ++--- packages/lib/src/tabs/Tabs.tsx | 16 ++++----- packages/lib/src/tabs/TabsLegacy.tsx | 45 ++++++++++++------------ 5 files changed, 38 insertions(+), 37 deletions(-) diff --git a/packages/lib/src/paginator/Paginator.tsx b/packages/lib/src/paginator/Paginator.tsx index 0ffca5a4ba..3f2486c861 100644 --- a/packages/lib/src/paginator/Paginator.tsx +++ b/packages/lib/src/paginator/Paginator.tsx @@ -29,12 +29,12 @@ const DxcPaginator = ({ const translatedLabels = useContext(HalstackLanguageContext); return ( - + {itemsPerPageOptions && ( - {translatedLabels?.paginator?.itemsPerPageText} + {translatedLabels.paginator.itemsPerPageText} ({ diff --git a/packages/lib/src/switch/Switch.tsx b/packages/lib/src/switch/Switch.tsx index ba5167efd4..1666c59294 100644 --- a/packages/lib/src/switch/Switch.tsx +++ b/packages/lib/src/switch/Switch.tsx @@ -36,7 +36,7 @@ const DxcSwitch = forwardRef( case "Enter": case " ": event.preventDefault(); - refTrack?.current?.focus(); + refTrack.current?.focus(); setInnerChecked(!(checked ?? innerChecked)); onChange?.(!(checked ?? innerChecked)); break; diff --git a/packages/lib/src/tabs/Tab.tsx b/packages/lib/src/tabs/Tab.tsx index d23dc56052..2ddbff058e 100644 --- a/packages/lib/src/tabs/Tab.tsx +++ b/packages/lib/src/tabs/Tab.tsx @@ -42,16 +42,16 @@ const DxcTab = forwardRef( useEffect(() => { if (activeLabel === label) { - setActiveIndicatorWidth?.(tabRef?.current?.offsetWidth ?? 0); - setActiveIndicatorLeft?.(tabRef?.current?.offsetLeft ?? 0); + setActiveIndicatorWidth?.(tabRef.current?.offsetWidth ?? 0); + setActiveIndicatorLeft?.(tabRef.current?.offsetLeft ?? 0); } - }, [activeLabel, label]); + }, [activeLabel, label, setActiveIndicatorWidth, setActiveIndicatorLeft]); useEffect(() => { if (active) { setActiveLabel?.(label); } - }, [active, label]); + }, [active, label, setActiveLabel]); const handleOnKeyDown = (event: KeyboardEvent) => { switch (event.key) { diff --git a/packages/lib/src/tabs/Tabs.tsx b/packages/lib/src/tabs/Tabs.tsx index 21d7711b72..5375493e9c 100644 --- a/packages/lib/src/tabs/Tabs.tsx +++ b/packages/lib/src/tabs/Tabs.tsx @@ -124,7 +124,7 @@ const DxcTabs = ({ }, [iconPosition, tabIndex, innerFocusIndex, activeTabLabel, childrenArray, hasLabelAndIcon]); const scrollLeft = () => { - const scrollWidth = (refTabList?.current?.offsetHeight || 0) * 0.75; + const scrollWidth = (refTabList?.current?.offsetHeight ?? 0) * 0.75; let moveX = 0; if (countClick <= scrollWidth) { moveX = 0; @@ -140,10 +140,11 @@ const DxcTabs = ({ }; const scrollRight = () => { - const scrollWidth = (refTabList?.current?.offsetHeight || 0) * 0.75; + const offsetHeight = refTabList?.current?.offsetHeight ?? 0; + const scrollWidth = offsetHeight * 0.75; let moveX = 0; - if (countClick + scrollWidth + (refTabList?.current?.offsetHeight || 0) >= totalTabsWidth) { - moveX = totalTabsWidth - (refTabList?.current?.offsetHeight || 0); + if (countClick + scrollWidth + offsetHeight >= totalTabsWidth) { + moveX = totalTabsWidth - offsetHeight; setScrollRightEnabled(false); setScrollLeftEnabled(true); } else { @@ -188,7 +189,7 @@ const DxcTabs = ({ onClick={scrollLeft} enabled={enabledIndicator} disabled={!scrollLeftEnabled} - aria-label={translatedLabels?.tabs?.scrollLeft} + aria-label={translatedLabels.tabs.scrollLeft} tabIndex={scrollLeftEnabled ? tabIndex : -1} minHeightTabs={minHeightTabs} > @@ -212,7 +213,7 @@ const DxcTabs = ({ onClick={scrollRight} enabled={enabledIndicator} disabled={!scrollRightEnabled} - aria-label={translatedLabels?.tabs?.scrollRight} + aria-label={translatedLabels.tabs.scrollRight} tabIndex={scrollRightEnabled ? tabIndex : -1} minHeightTabs={minHeightTabs} > @@ -244,8 +245,6 @@ const DxcTabs = ({ ); }; -DxcTabs.Tab = DxcTab; - const Underline = styled.div` position: absolute; left: 0; @@ -370,4 +369,5 @@ const TabsContentScroll = styled.div<{ transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; `; +DxcTabs.Tab = DxcTab; export default DxcTabs; diff --git a/packages/lib/src/tabs/TabsLegacy.tsx b/packages/lib/src/tabs/TabsLegacy.tsx index 9e66e1a419..ee60a97ef0 100644 --- a/packages/lib/src/tabs/TabsLegacy.tsx +++ b/packages/lib/src/tabs/TabsLegacy.tsx @@ -10,7 +10,7 @@ const useResize = (refTabList: MutableRefObject) => { const [viewWidth, setViewWidth] = useState(0); const handleWindowSizeChange = useCallback(() => { - setViewWidth(refTabList?.current?.offsetWidth ?? 0); + setViewWidth(refTabList.current?.offsetWidth ?? 0); }, [refTabList]); useEffect(() => { @@ -60,21 +60,21 @@ const DxcTabs = ({ useEffect(() => { if (activeTabIndex != null || innerActiveTabIndex != null) { - const sumWidth = refTabs?.current?.reduce((count, obj) => count + obj.offsetWidth, 0); + const sumWidth = refTabs.current?.reduce((count, obj) => count + obj.offsetWidth, 0); setTotalTabsWidth(sumWidth); - setActiveIndicatorWidth(refTabs?.current[activeTabIndex ?? innerActiveTabIndex!]?.offsetWidth ?? 0); - setActiveIndicatorLeft(refTabs?.current[activeTabIndex ?? innerActiveTabIndex!]?.offsetLeft ?? 0); + setActiveIndicatorWidth(refTabs.current[activeTabIndex ?? innerActiveTabIndex!]?.offsetWidth ?? 0); + setActiveIndicatorLeft(refTabs.current[activeTabIndex ?? innerActiveTabIndex!]?.offsetLeft ?? 0); } - }, [refTabs]); + }, [activeTabIndex, innerActiveTabIndex]); useEffect(() => { - setMinHeightTabs((refTabList?.current?.offsetHeight || 0) + 1); - }, [refTabList]); + setMinHeightTabs((refTabList.current?.offsetHeight ?? 0) + 1); + }, []); useEffect(() => { if (activeTabIndex && activeTabIndex >= 0) { - setActiveIndicatorWidth(refTabs?.current[activeTabIndex]?.offsetWidth ?? 0); - setActiveIndicatorLeft(refTabs?.current[activeTabIndex]?.offsetLeft ?? 0); + setActiveIndicatorWidth(refTabs.current[activeTabIndex]?.offsetWidth ?? 0); + setActiveIndicatorLeft(refTabs.current[activeTabIndex]?.offsetLeft ?? 0); } }, [activeTabIndex]); @@ -84,13 +84,13 @@ const DxcTabs = ({ } onTabClick?.(newValue); if (activeTabIndex === undefined) { - setActiveIndicatorWidth(refTabs?.current[newValue]?.offsetWidth ?? 0); - setActiveIndicatorLeft(refTabs?.current[newValue]?.offsetLeft ?? 0); + setActiveIndicatorWidth(refTabs.current[newValue]?.offsetWidth ?? 0); + setActiveIndicatorLeft(refTabs.current[newValue]?.offsetLeft ?? 0); } }; const scrollLeft = () => { - const scrollWidth = (refTabList?.current?.offsetHeight || 0) * 0.75; + const scrollWidth = (refTabList?.current?.offsetHeight ?? 0) * 0.75; let moveX = 0; if (countClick <= scrollWidth) { moveX = 0; @@ -106,10 +106,11 @@ const DxcTabs = ({ }; const scrollRight = () => { - const scrollWidth = (refTabList?.current?.offsetHeight || 0) * 0.75; + const offsetHeight = refTabList?.current?.offsetHeight ?? 0; + const scrollWidth = offsetHeight * 0.75; let moveX = 0; - if (countClick + scrollWidth + (refTabList?.current?.offsetHeight || 0) >= totalTabsWidth) { - moveX = totalTabsWidth - (refTabList?.current?.offsetHeight || 0); + if (countClick + scrollWidth + offsetHeight >= totalTabsWidth) { + moveX = totalTabsWidth - offsetHeight; setScrollRightEnabled(false); setScrollLeftEnabled(true); } else { @@ -129,7 +130,7 @@ const DxcTabs = ({ while (tabs[index]?.isDisabled) { index = index === 0 ? tabs.length - 1 : index - 1; } - refTabs?.current[index]?.focus({ preventScroll: true }); + refTabs.current[index]?.focus({ preventScroll: true }); setScrollFocus(index); return index; } @@ -146,7 +147,7 @@ const DxcTabs = ({ while (tabs[index]?.isDisabled) { index = index === tabs.length - 1 ? 0 : index + 1; } - refTabs?.current[index]?.focus({ preventScroll: true }); + refTabs.current[index]?.focus({ preventScroll: true }); setScrollFocus(index); return index; } @@ -158,7 +159,7 @@ const DxcTabs = ({ const setScrollFocus = (actualIndex: number) => { if (tabs) { let sumPrev = 0; - refTabs?.current?.forEach((item, index) => { + refTabs.current?.forEach((item, index) => { if (index <= actualIndex) { sumPrev += item.offsetWidth; } @@ -207,7 +208,7 @@ const DxcTabs = ({ if (temporalFocusIndex !== currentFocusIndex) { event.preventDefault(); setTemporalFocusIndex(currentFocusIndex); - refTabs?.current[currentFocusIndex]?.focus(); + refTabs.current[currentFocusIndex]?.focus(); } handleSelected(currentFocusIndex); } @@ -224,7 +225,7 @@ const DxcTabs = ({ (tabs != null && activeTabIndex !== undefined && activeTabIndex >= 0 && !!tabs[activeTabIndex]?.isDisabled); return ( - + @@ -232,7 +233,7 @@ const DxcTabs = ({ onClick={scrollLeft} enabled={enabledIndicator} disabled={!scrollLeftEnabled} - aria-label={translatedLabels?.tabs?.scrollLeft} + aria-label={translatedLabels.tabs.scrollLeft} tabIndex={scrollLeftEnabled ? tabIndex : -1} minHeightTabs={minHeightTabs} > @@ -277,7 +278,7 @@ const DxcTabs = ({ onClick={scrollRight} enabled={enabledIndicator} disabled={!scrollRightEnabled} - aria-label={translatedLabels?.tabs?.scrollRight} + aria-label={translatedLabels.tabs.scrollRight} tabIndex={scrollRightEnabled ? tabIndex : -1} minHeightTabs={minHeightTabs} > From ca255a56ddf1d73a6ee225a26f1d145ec31c098f Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Tue, 7 Jan 2025 08:52:18 +0100 Subject: [PATCH 39/41] Includes improvements from strict mode PR review --- packages/lib/src/select/Listbox.tsx | 6 +- packages/lib/src/tag/Tag.tsx | 29 +++--- packages/lib/src/text-input/TextInput.tsx | 105 ++++++++++++---------- packages/lib/src/textarea/Textarea.tsx | 30 +++---- 4 files changed, 85 insertions(+), 85 deletions(-) diff --git a/packages/lib/src/select/Listbox.tsx b/packages/lib/src/select/Listbox.tsx index 2b8962364f..63aeeba797 100644 --- a/packages/lib/src/select/Listbox.tsx +++ b/packages/lib/src/select/Listbox.tsx @@ -24,13 +24,9 @@ const Listbox = ({ let globalIndex = optional && !multiple ? 0 : -1; - const isListOptionGroupType = (option: ListOptionType | ListOptionGroupType): option is ListOptionGroupType => { - return "options" in option && Array.isArray(option.options); - }; - const mapOptionFunc = (option: ListOptionType | ListOptionGroupType, mapIndex: number) => { const groupId = `${id}-group-${mapIndex}`; - if (isListOptionGroupType(option)) { + if ("options" in option) { return ( option.options.length > 0 && (
  • diff --git a/packages/lib/src/tag/Tag.tsx b/packages/lib/src/tag/Tag.tsx index 9615ef176f..88caf5a617 100644 --- a/packages/lib/src/tag/Tag.tsx +++ b/packages/lib/src/tag/Tag.tsx @@ -35,20 +35,6 @@ const DxcTag = ({ const colorsTheme = useContext(HalstackContext); const [isHovered, changeIsHovered] = useState(false); - const wrapperComponent = (children: ReactNode) => { - if (onClick) { - return {children}; - } - if (linkHref) { - return ( - - {children} - - ); - } - return <>{children}; - }; - return ( - + + onClick ? ( + {children} + ) : linkHref ? ( + + {children} + + ) : ( + <> + ) + } + > {labelPosition === "before" && size !== "small" && label && {label}} {icon && ( diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx index 185f56c814..7b11b45e37 100644 --- a/packages/lib/src/text-input/TextInput.tsx +++ b/packages/lib/src/text-input/TextInput.tsx @@ -62,13 +62,20 @@ const hasSuggestions = (suggestions: TextInputPropsType["suggestions"]) => const isRequired = (value: string, optional: boolean) => value === "" && !optional; -const isLengthIncorrect = (value: string, minLength: number | undefined, maxLength: number | undefined) => +const isLengthIncorrect = ( + value: string, + minLength: TextInputPropsType["minLength"], + maxLength: TextInputPropsType["maxLength"] +) => value != null && ((minLength != null && value.length < minLength) || (maxLength != null && value.length > maxLength)); -const isNumberIncorrect = (value: number, minNumber: number | undefined, maxNumber: number | undefined) => - (minNumber != null && value < minNumber) || (maxNumber != null && value > maxNumber); +const isNumberIncorrect = ( + value: number, + minNumber: TextInputPropsType["minLength"], + maxNumber: TextInputPropsType["maxLength"] +) => (minNumber != null && value < minNumber) || (maxNumber != null && value > maxNumber); -const patternMismatch = (pattern: string | undefined, value: string) => +const patternMismatch = (pattern: TextInputPropsType["pattern"], value: string) => pattern != null && !new RegExp(pattern).test(value); const DxcTextInput = forwardRef( @@ -120,49 +127,7 @@ const DxcTextInput = forwardRef( const colorsTheme = useContext(HalstackContext); const translatedLabels = useContext(HalstackLanguageContext); const numberInputContext = useContext(NumberInputContext); - // Define the wrapper function outside of the parent component - const autosuggestWrapperFunction = (children: ReactNode) => ( - 0 || isSearching || isAutosuggestError)}> - - {children} - - - { - // Avoid select to lose focus when the list is opened - event.preventDefault(); - }} - onCloseAutoFocus={(event) => { - // Avoid select to lose focus when the list is closed - event.preventDefault(); - }} - > - { - changeValue(suggestion); - closeSuggestions(); - }} - styles={{ width }} - /> - - - - ); + const getNumberErrorMessage = (checkedValue: number) => numberInputContext?.minNumber != null && checkedValue < numberInputContext?.minNumber ? translatedLabels.numberInput.valueGreaterThanOrEqualToErrorMessage?.(numberInputContext.minNumber) @@ -470,7 +435,51 @@ const DxcTextInput = forwardRef( )} {helperText && {helperText}} - + ( + 0 || isSearching || isAutosuggestError)}> + + {children} + + + { + // Avoid select to lose focus when the list is opened + event.preventDefault(); + }} + onCloseAutoFocus={(event) => { + // Avoid select to lose focus when the list is closed + event.preventDefault(); + }} + > + { + changeValue(suggestion); + closeSuggestions(); + }} + styles={{ width }} + /> + + + + )} + > new RegExp(pattern).test(value); @@ -232,14 +232,12 @@ const Textarea = styled.textarea<{ verticalGrow: TextareaPropsType["verticalGrow"]; error: TextareaPropsType["error"]; }>` - ${({ verticalGrow }) => - verticalGrow === "none" - ? "resize: none;" - : verticalGrow === "auto" - ? `resize: none; overflow: hidden;` - : verticalGrow === "manual" - ? "resize: vertical;" - : `resize: none;`}; + ${({ verticalGrow }) => { + if (verticalGrow === "none") return "resize: none;"; + else if (verticalGrow === "auto") return `resize: none; overflow: hidden;`; + else if (verticalGrow === "manual") return "resize: vertical;"; + else return `resize: none;`; + }}; ${(props) => props.disabled ? `background-color: ${props.theme.disabledContainerFillColor};` : `background-color: transparent;`} @@ -248,14 +246,12 @@ const Textarea = styled.textarea<{ box-shadow: 0 0 0 2px transparent; border-radius: 0.25rem; border: 1px solid - ${(props) => - props.disabled - ? props.theme.disabledBorderColor - : props.error - ? "transparent" - : props.readOnly - ? props.theme.readOnlyBorderColor - : props.theme.enabledBorderColor}; + ${(props) => { + if (props.disabled) return props.theme.disabledBorderColor; + else if (props.error) return "transparent"; + else if (props.readOnly) return props.theme.readOnlyBorderColor; + else props.theme.enabledBorderColor; + }}; ${(props) => props.error && From 527ccc3fb25d6a1d63b3315e49de52920e52097c Mon Sep 17 00:00:00 2001 From: Enrique Moreno Date: Tue, 7 Jan 2025 11:02:28 +0100 Subject: [PATCH 40/41] Refactorized textarea code --- packages/lib/src/textarea/Textarea.tsx | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/packages/lib/src/textarea/Textarea.tsx b/packages/lib/src/textarea/Textarea.tsx index a1fd728937..fd200fb28c 100644 --- a/packages/lib/src/textarea/Textarea.tsx +++ b/packages/lib/src/textarea/Textarea.tsx @@ -44,13 +44,13 @@ const DxcTextarea = forwardRef( const prevValueRef = useRef(null); const errorId = `error-${textareaId}`; - const isNotOptional = (checkedValue: string) => checkedValue === "" && !optional; + const isNotOptional = (value: string) => value === "" && !optional; - const isLengthIncorrect = (checkedValue: string) => - checkedValue !== "" && + const isLengthIncorrect = (value: string) => + value !== "" && minLength && maxLength && - (checkedValue.length < minLength || checkedValue.length > maxLength); + (value.length < minLength || value.length > maxLength); const changeValue = (newValue: string) => { if (value == null) { @@ -77,17 +77,6 @@ const DxcTextarea = forwardRef( } }; - const autoVerticalGrow = () => { - if (textareaRef?.current) { - const computedStyle = window.getComputedStyle(textareaRef?.current); - const textareaLineHeight = parseInt(computedStyle.lineHeight || "0", 10); - const textareaPaddingTopBottom = parseInt(computedStyle.paddingTop || "0", 10) * 2; - textareaRef.current.style.height = `${textareaLineHeight * rows}px`; - const newHeight = textareaRef.current.scrollHeight - textareaPaddingTopBottom; - textareaRef.current.style.height = `${newHeight}px`; - } - }; - const handleOnBlur = (event: FocusEvent) => { if (isNotOptional(event.target.value)) { onBlur?.({ @@ -111,14 +100,11 @@ const DxcTextarea = forwardRef( const handleOnChange = (event: ChangeEvent) => { changeValue(event.target.value); - if (verticalGrow === "auto") { - autoVerticalGrow(); - } }; useEffect(() => { - if (verticalGrow === "auto" && prevValueRef.current !== (value ?? innerValue) && textareaRef?.current) { - const computedStyle = window.getComputedStyle(textareaRef?.current); + if (verticalGrow === "auto" && prevValueRef.current !== (value ?? innerValue) && textareaRef.current) { + const computedStyle = window.getComputedStyle(textareaRef.current); const textareaLineHeight = parseInt(computedStyle.lineHeight || "0", 10); const textareaPaddingTopBottom = parseInt(computedStyle.paddingTop || "0", 10) * 2; textareaRef.current.style.height = `${textareaLineHeight * rows}px`; From 82a6a9c6c4c0470ba41f87a25503e92d49f39988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20G=C3=B3mez=20Pinta?= <44321109+GomezIvann@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:16:31 +0100 Subject: [PATCH 41/41] Small code updates --- packages/lib/src/select/Select.tsx | 57 +++++++++++++++-------- packages/lib/src/text-input/TextInput.tsx | 27 +++++------ 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index 69f9ea8195..334a34d9e9 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -1,5 +1,17 @@ import * as Popover from "@radix-ui/react-popover"; -import { ChangeEvent, FocusEvent, forwardRef, KeyboardEvent, MouseEvent, useCallback, useContext, useId, useMemo, useRef, useState } from "react"; +import { + ChangeEvent, + FocusEvent, + forwardRef, + KeyboardEvent, + MouseEvent, + useCallback, + useContext, + useId, + useMemo, + useRef, + useState, +} from "react"; import styled, { ThemeProvider } from "styled-components"; import { spaces } from "../common/variables"; import { getMargin } from "../common/utils"; @@ -82,27 +94,32 @@ const DxcSelect = forwardRef( } }; - const handleSelectChangeValue = (newOption: ListOptionType | undefined) => { - if (newOption) { - const currentValue = value ?? innerValue; - const newValue = multiple - ? (currentValue as string[]).includes(newOption.value) - ? (currentValue as string[]).filter((optionVal: string) => optionVal !== newOption.value) - : [...(currentValue as string[]), newOption.value] - : newOption.value; - - if (value == null) { - setInnerValue(newValue); + const handleSelectChangeValue = useCallback( + (newOption: ListOptionType | undefined) => { + if (newOption) { + let newValue: string | string[]; + + if (multiple) { + const currentValue = (value ?? innerValue) as string[]; + newValue = currentValue.includes(newOption.value) + ? currentValue.filter((optionVal) => optionVal !== newOption.value) + : [...currentValue, newOption.value]; + } else newValue = newOption.value; + + if (value == null) { + setInnerValue(newValue); + } + onChange?.({ + value: newValue as string & string[], + error: notOptionalCheck(newValue, multiple, optional) + ? translatedLabels.formFields.requiredValueErrorMessage + : undefined, + }); } + }, + [multiple, value, innerValue, onChange, optional, translatedLabels] + ); - onChange?.({ - value: newValue as string & string[], - ...(notOptionalCheck(newValue, multiple, optional) && { - error: translatedLabels.formFields.requiredValueErrorMessage, - }), - }); - } - }; const handleSelectOnClick = () => { if (searchable) { selectSearchInputRef?.current?.focus(); diff --git a/packages/lib/src/text-input/TextInput.tsx b/packages/lib/src/text-input/TextInput.tsx index 7b11b45e37..56957e107a 100644 --- a/packages/lib/src/text-input/TextInput.tsx +++ b/packages/lib/src/text-input/TextInput.tsx @@ -236,7 +236,7 @@ const DxcTextInput = forwardRef( const handleInputContainerOnClick = () => { if (document.activeElement !== actionRef.current) { - inputRef?.current?.focus(); + inputRef.current?.focus(); } }; const handleInputContainerOnMouseDown = (event: MouseEvent) => { @@ -341,16 +341,16 @@ const DxcTextInput = forwardRef( const handleNumberInputWheel = (event: WheelEvent) => { if (document.activeElement === inputRef.current) { if (event.deltaY < 0) { - incrementNumber(inputRef?.current?.value); + incrementNumber(inputRef.current?.value); } else { - decrementNumber(inputRef?.current?.value); + decrementNumber(inputRef.current?.value); } } }; const handleClearActionOnClick = () => { changeValue(""); - inputRef?.current?.focus(); + inputRef.current?.focus(); if (suggestions) { closeSuggestions(); } @@ -358,30 +358,25 @@ const DxcTextInput = forwardRef( const handleDecrementActionOnClick = () => { decrementNumber(); - inputRef?.current?.focus(); + inputRef.current?.focus(); }; const handleIncrementActionOnClick = () => { incrementNumber(); - inputRef?.current?.focus(); + inputRef.current?.focus(); }; - const setNumberProps = ( - type: string | undefined, - min: number | undefined, - max: number | undefined, - step: number | undefined - ) => { + const setNumberProps = (type?: string, min?: number, max?: number, step?: number) => { if (min != null) { - inputRef?.current?.setAttribute("min", min.toString()); + inputRef.current?.setAttribute("min", min.toString()); } if (max != null) { - inputRef?.current?.setAttribute("max", max.toString()); + inputRef.current?.setAttribute("max", max.toString()); } if (step != null) { - inputRef?.current?.setAttribute("step", step.toString()); + inputRef.current?.setAttribute("step", step.toString()); } if (type != null) { - inputRef?.current?.setAttribute("type", type); + inputRef.current?.setAttribute("type", type); } };