From 217d3f0939b5bca75f24ea3020057946befea0da Mon Sep 17 00:00:00 2001 From: Pelayo Felgueroso Date: Mon, 1 Sep 2025 10:35:30 +0200 Subject: [PATCH 1/2] Fix Accessibility test errors --- .../Breadcrumbs.accessibility.test.tsx | 8 ++ packages/lib/src/dropdown/Dropdown.tsx | 2 +- .../src/header/Header.accessibility.test.tsx | 8 ++ .../Paginator.accessibility.test.tsx | 4 +- .../lib/src/paginator/Paginator.stories.tsx | 18 +++ packages/lib/src/paginator/Paginator.tsx | 121 ++++++++++-------- packages/lib/src/paginator/utils.ts | 3 + packages/lib/src/select/Select.tsx | 2 +- packages/lib/src/tabs/Tabs.tsx | 2 +- packages/lib/src/utils/useWidth.ts | 12 +- 10 files changed, 115 insertions(+), 65 deletions(-) create mode 100644 packages/lib/src/paginator/utils.ts diff --git a/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx b/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx index dbfe3efb38..e10125df95 100644 --- a/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx +++ b/packages/lib/src/breadcrumbs/Breadcrumbs.accessibility.test.tsx @@ -3,6 +3,14 @@ import { axe, formatRules } from "../../test/accessibility/axe-helper"; import DxcBreadcrumbs from "./Breadcrumbs"; import { disabledRules as rules } from "../../test/accessibility/rules/specific/breadcrumbs/disabledRules"; +(global as any).ResizeObserver = class ResizeObserver { + observe() {} + + unobserve() {} + + disconnect() {} +}; + const disabledRules = { rules: formatRules(rules), }; diff --git a/packages/lib/src/dropdown/Dropdown.tsx b/packages/lib/src/dropdown/Dropdown.tsx index 654046628a..90084ed57d 100644 --- a/packages/lib/src/dropdown/Dropdown.tsx +++ b/packages/lib/src/dropdown/Dropdown.tsx @@ -134,7 +134,7 @@ const DxcDropdown = ({ const triggerRef = useRef(null); const menuRef = useRef(null); - const width = useWidth(triggerRef.current); + const width = useWidth(triggerRef); const handleOnOpenMenu = () => { changeIsOpen(true); diff --git a/packages/lib/src/header/Header.accessibility.test.tsx b/packages/lib/src/header/Header.accessibility.test.tsx index de590f5913..d31e16d361 100644 --- a/packages/lib/src/header/Header.accessibility.test.tsx +++ b/packages/lib/src/header/Header.accessibility.test.tsx @@ -5,6 +5,14 @@ import DxcFlex from "../flex/Flex"; import DxcLink from "../link/Link"; import DxcHeader from "./Header"; +(global as any).ResizeObserver = class ResizeObserver { + observe() {} + + unobserve() {} + + disconnect() {} +}; + const disabledRules = { rules: formatRules(rules), }; diff --git a/packages/lib/src/paginator/Paginator.accessibility.test.tsx b/packages/lib/src/paginator/Paginator.accessibility.test.tsx index aa86a22377..424345aa82 100644 --- a/packages/lib/src/paginator/Paginator.accessibility.test.tsx +++ b/packages/lib/src/paginator/Paginator.accessibility.test.tsx @@ -4,9 +4,7 @@ import DxcPaginator from "./Paginator"; // Mocking DOMRect for Radix Primitive Popover (global as any).globalThis = global; -(global as any).DOMRect = { - fromRect: () => ({ top: 0, left: 0, bottom: 0, right: 0, width: 0, height: 0 }), -}; + (global as any).ResizeObserver = class ResizeObserver { observe() {} diff --git a/packages/lib/src/paginator/Paginator.stories.tsx b/packages/lib/src/paginator/Paginator.stories.tsx index 76593dd4cc..4bf6702b3e 100644 --- a/packages/lib/src/paginator/Paginator.stories.tsx +++ b/packages/lib/src/paginator/Paginator.stories.tsx @@ -8,6 +8,16 @@ export default { component: DxcPaginator, } as Meta; +const customViewports = { + resizedScreen: { + name: "Custom viewport", + styles: { + width: "400px", + height: "1600px", + }, + }, +}; + const Paginator = () => ( <> @@ -67,3 +77,11 @@ type Story = StoryObj; export const Chromatic: Story = { render: Paginator, }; + +export const ResponsivePaginator: Story = { + render: Paginator, + parameters: { + viewport: { viewports: customViewports, defaultViewport: "resizedScreen" }, + chromatic: { viewports: [400] }, + }, +}; diff --git a/packages/lib/src/paginator/Paginator.tsx b/packages/lib/src/paginator/Paginator.tsx index cf89b89698..3674fa6214 100644 --- a/packages/lib/src/paginator/Paginator.tsx +++ b/packages/lib/src/paginator/Paginator.tsx @@ -1,9 +1,65 @@ -import { useContext } from "react"; +import { useContext, useRef } from "react"; import styled from "@emotion/styled"; import DxcButton from "../button/Button"; import DxcSelect from "../select/Select"; import PaginatorPropsType from "./types"; import { HalstackLanguageContext } from "../HalstackContext"; +import { responsiveSizes } from "../common/variables"; +import useWidth from "../utils/useWidth"; +import { isResponsive } from "./utils"; + +const DxcPaginatorContainer = styled.div<{ width: number }>` + display: flex; + justify-content: ${({ width }) => (isResponsive(width) ? "center" : "flex-end")}; + flex-wrap: ${({ width }) => (isResponsive(width) ? "wrap" : "nowrap")}; + gap: ${({ width }) => (isResponsive(width) ? "var(--spacing-gap-s)" : "0")}; + align-items: center; + width: 100%; + min-height: 48px; + box-sizing: border-box; + font-family: var(--typography-font-family); + font-size: var(--typography-label-m); + font-weight: var(--typography-label-regular); + background-color: var(--color-bg-neutral-lighter); + color: var(--color-fg-neutral-dark); + padding: var(--spacing-padding-xs) var(--spacing-padding-xl); +`; + +const ItemsPerPageContainer = styled.span<{ width: number }>` + display: flex; + align-items: center; + gap: var(--spacing-gap-s); + margin-right: ${({ width }) => (isResponsive(width) ? "0" : "var(--spacing-gap-ml)")}; +`; + +const SelectContainer = styled.div` + min-width: 6.25rem; +`; + +const TotalItemsContainer = styled.span<{ width: number }>` + margin-right: ${({ width }) => (isResponsive(width) ? "0" : "var(--spacing-gap-xxl)")}; +`; + +const GoToPageContainer = styled.div` + display: flex; + align-items: center; + gap: var(--spacing-gap-ml); +`; + +const ButtonsContainer = styled.div` + display: flex; + align-items: center; + gap: var(--spacing-gap-s); + flex-shrink: 0; +`; + +const PageToSelectContainer = styled.span<{ width: number }>` + display: flex; + align-items: center; + gap: var(--spacing-gap-s); + flex-shrink: 0; + flex-wrap: wrap; +`; const DxcPaginator = ({ currentPage = 1, @@ -26,58 +82,13 @@ const DxcPaginator = ({ const translatedLabels = useContext(HalstackLanguageContext); - const DxcPaginatorContainer = styled.div` - display: flex; - justify-content: flex-end; - align-items: center; - width: 100%; - min-height: 48px; - box-sizing: border-box; - font-family: var(--typography-font-family); - font-size: var(--typography-label-m); - font-weight: var(--typography-label-regular); - background-color: var(--color-bg-neutral-lighter); - color: var(--color-fg-neutral-dark); - padding: var(--spacing-padding-xs) var(--spacing-padding-xl); - `; - - const ItemsPerPageContainer = styled.span` - display: flex; - align-items: center; - gap: var(--spacing-gap-s); - margin-right: var(--spacing-gap-ml); - `; - - const SelectContainer = styled.div` - min-width: 6.25rem; - `; - - const TotalItemsContainer = styled.span` - margin-right: var(--spacing-gap-xxl); - `; - - const GoToPageContainer = styled.div` - display: flex; - align-items: center; - gap: var(--spacing-gap-ml); - `; - - const ButtonsContainer = styled.div` - display: flex; - align-items: center; - gap: var(--spacing-gap-s); - `; - - const PageToSelectContainer = styled.span` - display: flex; - align-items: center; - gap: var(--spacing-gap-s); - `; + const containerRef = useRef(null); + const width = useWidth(containerRef); return ( - + {itemsPerPageOptions && ( - + {translatedLabels.paginator.itemsPerPageText} )} - + {translatedLabels.paginator.minToMaxOfText(minItemsPerPage, maxItemsPerPage, totalItems)} @@ -127,8 +138,10 @@ const DxcPaginator = ({ )} {showGoToPage ? ( - - {translatedLabels.paginator.goToPageText} + + {(width >= Number(responsiveSizes.small) * 16 || !onPageChange) && ( + {translatedLabels.paginator.goToPageText} + )} ({ @@ -178,4 +191,4 @@ const DxcPaginator = ({ ); }; -export default DxcPaginator; +export default DxcPaginator; \ No newline at end of file diff --git a/packages/lib/src/paginator/utils.ts b/packages/lib/src/paginator/utils.ts new file mode 100644 index 0000000000..a2bd89712e --- /dev/null +++ b/packages/lib/src/paginator/utils.ts @@ -0,0 +1,3 @@ +import { responsiveSizes } from "../common/variables"; + +export const isResponsive = (width: number) => width && width <= Number(responsiveSizes.medium) * 16; diff --git a/packages/lib/src/select/Select.tsx b/packages/lib/src/select/Select.tsx index 13aac549b7..afce7df427 100644 --- a/packages/lib/src/select/Select.tsx +++ b/packages/lib/src/select/Select.tsx @@ -211,7 +211,7 @@ const DxcSelect = forwardRef( const selectRef = useRef(null); const selectSearchInputRef = useRef(null); - const width = useWidth(selectRef.current); + const width = useWidth(selectRef); const translatedLabels = useContext(HalstackLanguageContext); const optionalItem = useMemo(() => ({ label: placeholder, value: "" }), [placeholder]); diff --git a/packages/lib/src/tabs/Tabs.tsx b/packages/lib/src/tabs/Tabs.tsx index 3d6d3dce12..06b6831a9a 100644 --- a/packages/lib/src/tabs/Tabs.tsx +++ b/packages/lib/src/tabs/Tabs.tsx @@ -112,7 +112,7 @@ const DxcTabs = ({ children, iconPosition = "left", margin, tabIndex = 0 }: Tabs const refTabListContainer = useRef(null); const refTabList = useRef(null); const translatedLabels = useContext(HalstackLanguageContext); - const viewWidth = useWidth(refTabList.current); + const viewWidth = useWidth(refTabList); const contextValue = useMemo(() => { const focusedChild = innerFocusIndex != null ? childrenArray[innerFocusIndex] : null; return { diff --git a/packages/lib/src/utils/useWidth.ts b/packages/lib/src/utils/useWidth.ts index a17ff906fb..35bab8d15b 100644 --- a/packages/lib/src/utils/useWidth.ts +++ b/packages/lib/src/utils/useWidth.ts @@ -2,13 +2,15 @@ import { useLayoutEffect, useState } from "react"; /** * Custom hook to get the width of an element and keep it updated when it changes. - * @param target - * @returns + * @param target + * @returns */ -const useWidth = (target: T | null) => { +const useWidth = (ref: React.RefObject) => { const [width, setWidth] = useState(0); useLayoutEffect(() => { + const target = ref?.current; + if (target != null) { setWidth(target.getBoundingClientRect().width); @@ -23,9 +25,9 @@ const useWidth = (target: T | null) => { triggerObserver.unobserve(target); }; } - }, [target]); + }, []); return width; }; -export default useWidth; +export default useWidth; \ No newline at end of file From cee3239f7118e1c68c7daf2f61627dc97691a4ad Mon Sep 17 00:00:00 2001 From: Pelayo Felgueroso Date: Wed, 10 Sep 2025 13:40:23 +0200 Subject: [PATCH 2/2] Fix installation docs --- apps/website/screens/overview/installation/InstallationPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/website/screens/overview/installation/InstallationPage.tsx b/apps/website/screens/overview/installation/InstallationPage.tsx index a4cf9ecac5..c140b3f9bf 100644 --- a/apps/website/screens/overview/installation/InstallationPage.tsx +++ b/apps/website/screens/overview/installation/InstallationPage.tsx @@ -47,7 +47,7 @@ const sections = [ have it as dependency of your project. Additionally, ensure that the styled-components{" "} library is also installed, as it is required by the Halstack components. - {`npm i react react-dom styled-components`} + {`npm i react react-dom react-data-grid @emotion/react @emotion/styled`} ), },