From 26394dd2b740f19306e8fbbbd6c2195fc5a3bbed Mon Sep 17 00:00:00 2001 From: David Menendez Date: Fri, 13 Dec 2024 09:26:13 -0600 Subject: [PATCH] fix: pageheader subtitle truncation visibility (#6551) * fix: pageheader subtitle truncation visibility * fix: pageheader useoverflow hook * chore: copyright * fix: refactor overflow utils and logic * fix: overflow check refactor * fix: use one ref instead * fix: remove unnecessary title attribute * fix: remove console log oops * fix: add testing * fix: description --------- Co-authored-by: Amal K Joy <153802538+amal-k-joy@users.noreply.github.com> --- .../components/PageHeader/_page-header.scss | 24 ++++++++--- .../PageHeader/PageHeader.stories.jsx | 2 +- .../src/components/PageHeader/PageHeader.tsx | 27 ++++++++++-- .../components/PageHeader/PageHeaderTitle.js | 42 ++++++------------- .../utils/__tests__/checkForOverflow.test.js | 40 ++++++++++++++++++ .../src/global/js/utils/checkForOverflow.ts | 24 +++++++++++ 6 files changed, 118 insertions(+), 41 deletions(-) create mode 100644 packages/ibm-products/src/global/js/utils/__tests__/checkForOverflow.test.js create mode 100644 packages/ibm-products/src/global/js/utils/checkForOverflow.ts diff --git a/packages/ibm-products-styles/src/components/PageHeader/_page-header.scss b/packages/ibm-products-styles/src/components/PageHeader/_page-header.scss index fa1adf8ebe..e2878df4b0 100644 --- a/packages/ibm-products-styles/src/components/PageHeader/_page-header.scss +++ b/packages/ibm-products-styles/src/components/PageHeader/_page-header.scss @@ -538,14 +538,8 @@ $right-section-alt-width: 100% - $left-section-alt-width; } .#{$block-class}__subtitle-row { - display: -webkit-box; - overflow: hidden; - max-width: 100%; margin-top: $spacing-03; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - @include breakpoint-up('md') { max-width: $left-section-std-width; } @@ -559,6 +553,24 @@ $right-section-alt-width: 100% - $left-section-alt-width; @include type.type-style('body-01'); } + .#{$block-class}__subtitle-tooltip .#{$carbon-prefix}--definition-term { + border-bottom: 0; + letter-spacing: inherit; + } + + // overwrites the existing styles to make the popover bigger because in some cases the narrow space can be too constricting for the header + .#{$block-class}__subtitle-tooltip + .#{$carbon-prefix}--popover-content.#{$carbon-prefix}--definition-tooltip { + max-inline-size: fit-content; + } + + .#{$block-class}__subtitle-text { + display: -webkit-box; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + } + .#{$block-class}__available-row { @include type.type-style('body-01'); diff --git a/packages/ibm-products/src/components/PageHeader/PageHeader.stories.jsx b/packages/ibm-products/src/components/PageHeader/PageHeader.stories.jsx index 22e6b8fd6b..74ac52968c 100644 --- a/packages/ibm-products/src/components/PageHeader/PageHeader.stories.jsx +++ b/packages/ibm-products/src/components/PageHeader/PageHeader.stories.jsx @@ -486,7 +486,7 @@ const pageActionsOverflowLabel = 'Page actions...'; const subtitle = 'Optional subtitle if necessary'; const longSubtitle = - 'Optional subtitle if necessary, which is very long in this case, but will need to be handled somehow. It just keeps going on and on and on and on and on.'; + 'Optional subtitle if necessary, which is very long in this case, but will need to be handled somehow. It just keeps going on and on and on and on and on and on and on and on and on and on and on.'; const demoSubtitle = 'This report details the monthly authentication failures'; const dummyPageContent = ( diff --git a/packages/ibm-products/src/components/PageHeader/PageHeader.tsx b/packages/ibm-products/src/components/PageHeader/PageHeader.tsx index aa2c4a6cc1..8d339ec5e2 100644 --- a/packages/ibm-products/src/components/PageHeader/PageHeader.tsx +++ b/packages/ibm-products/src/components/PageHeader/PageHeader.tsx @@ -15,6 +15,7 @@ import { usePrefix, ButtonProps, PopoverAlignment, + DefinitionTooltip, } from '@carbon/react'; import { TagProps } from '@carbon/react/lib/components/Tag/Tag'; import React, { @@ -51,6 +52,7 @@ import cx from 'classnames'; import { getDevtoolsProps } from '../../global/js/utils/devtools'; import { pkg } from '../../settings'; import { useResizeObserver } from '../../global/js/hooks/useResizeObserver'; +import { checkHeightOverflow } from '../../global/js/utils/checkForOverflow'; const componentName = 'PageHeader'; @@ -901,12 +903,20 @@ export let PageHeader = React.forwardRef( const displayedBreadcrumbs = getBreadcrumbs(); + const subtitleRef = useRef(null); + const isOverflowing = checkHeightOverflow(subtitleRef.current); + const subtitleContent = ( + + {subtitle} + + ); + return ( <>
+ />
) : null} - {subtitle ? ( + {subtitle && ( - {subtitle} + {isOverflowing ? ( + + {subtitleContent} + + ) : ( + subtitleContent + )} - ) : null} + )} {children ? ( diff --git a/packages/ibm-products/src/components/PageHeader/PageHeaderTitle.js b/packages/ibm-products/src/components/PageHeader/PageHeaderTitle.js index 188776d5f8..c0d8371e86 100644 --- a/packages/ibm-products/src/components/PageHeader/PageHeaderTitle.js +++ b/packages/ibm-products/src/components/PageHeader/PageHeaderTitle.js @@ -5,11 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import React, { useLayoutEffect, useRef, useState } from 'react'; +import React, { useRef } from 'react'; import PropTypes from 'prop-types'; import cx from 'classnames'; import { DefinitionTooltip, SkeletonText } from '@carbon/react'; import { EditInPlace } from '../EditInPlace'; +import { checkWidthOverflow } from '../../global/js/utils/checkForOverflow'; /** * @@ -39,25 +40,8 @@ export const PageHeaderTitle = ({ blockClass, hasBreadcrumbRow, title }) => { let titleText; let isEditable = !!onSave; - const [isEllipsisApplied, setIsEllipsisApplied] = useState(); - const longTitleRef = useRef(undefined); - const titleRef = useRef(undefined); - - useLayoutEffect(() => { - setIsEllipsisApplied(isEllipsisActive()); - }, [longTitleRef, titleRef, title]); - - const isEllipsisActive = () => { - if (longTitleRef.current) { - return ( - longTitleRef.current?.offsetWidth < longTitleRef.current?.scrollWidth - ); - } else if (titleRef.current) { - return titleRef.current?.offsetWidth < titleRef.current?.scrollWidth; - } - - return false; - }; + const titleRef = useRef(); + const isEllipsisApplied = checkWidthOverflow(titleRef.current); if (text || !content) { if (text === undefined && typeof title === 'string') { @@ -66,6 +50,12 @@ export const PageHeaderTitle = ({ blockClass, hasBreadcrumbRow, title }) => { } const TitleIcon = icon; + const titleContent = ( + + {text} + + ); + titleInnards = ( <> {icon && !loading ? ( @@ -97,18 +87,10 @@ export const PageHeaderTitle = ({ blockClass, hasBreadcrumbRow, title }) => { definition={text} className={`${blockClass}__tooltip`} > - - {text} - + {titleContent} ) : ( - - {text} - + titleContent )} ); diff --git a/packages/ibm-products/src/global/js/utils/__tests__/checkForOverflow.test.js b/packages/ibm-products/src/global/js/utils/__tests__/checkForOverflow.test.js new file mode 100644 index 0000000000..2144e23e88 --- /dev/null +++ b/packages/ibm-products/src/global/js/utils/__tests__/checkForOverflow.test.js @@ -0,0 +1,40 @@ +/** + * Copyright IBM Corp. 2024 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { checkWidthOverflow, checkHeightOverflow } from '../checkForOverflow'; + +const normalElm = { + offsetWidth: 200, + scrollWidth: 100, + offsetHeight: 200, + scrollHeight: 100, +}; + +const overflowElm = { + offsetWidth: 100, + scrollWidth: 200, + offsetHeight: 100, + scrollHeight: 200, +}; + +describe('checkForOverflow', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('detects width overflow', () => { + expect(checkWidthOverflow(normalElm)).toBe(false); + expect(checkWidthOverflow(overflowElm)).toBe(true); + expect(checkWidthOverflow()).toBe(false); + }); + + it('detects height overflow', () => { + expect(checkHeightOverflow(normalElm)).toBe(false); + expect(checkHeightOverflow(overflowElm)).toBe(true); + expect(checkHeightOverflow()).toBe(false); + }); +}); diff --git a/packages/ibm-products/src/global/js/utils/checkForOverflow.ts b/packages/ibm-products/src/global/js/utils/checkForOverflow.ts new file mode 100644 index 0000000000..386389d4b2 --- /dev/null +++ b/packages/ibm-products/src/global/js/utils/checkForOverflow.ts @@ -0,0 +1,24 @@ +// +// Copyright IBM Corp. 2024, 2024 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +/** + * used to calculate if a element is overflowing the width or height of an area + */ + +export const checkWidthOverflow = (el: HTMLElement | null): boolean => { + if (el) { + return el?.offsetWidth < el?.scrollWidth; + } + return false; +}; + +export const checkHeightOverflow = (el: HTMLElement | null): boolean => { + if (el) { + return el?.offsetHeight < el?.scrollHeight; + } + return false; +};