diff --git a/cspell.json b/cspell.json index d2332d1865..0423d88acb 100644 --- a/cspell.json +++ b/cspell.json @@ -85,6 +85,7 @@ "dasharray", "data-testid", "datagrid", + "denormalized", "disttag", "disttags", "dragbar", diff --git a/packages/ibm-products-styles/src/components/InterstitialScreen/_interstitial-screen.scss b/packages/ibm-products-styles/src/components/InterstitialScreen/_interstitial-screen.scss index 56e7fa8534..75aac6398e 100644 --- a/packages/ibm-products-styles/src/components/InterstitialScreen/_interstitial-screen.scss +++ b/packages/ibm-products-styles/src/components/InterstitialScreen/_interstitial-screen.scss @@ -17,13 +17,8 @@ @use '@carbon/react/scss/colors' as *; @use '@carbon/styles/scss/type'; -// Other Carbon settings if needed -// TODO: @use '@carbon/styles/scss/grid'; -// or -// TODO: @use '@carbon/react/scss/grid'; - // InterstitialScreen uses the following Carbon for IBM Products components: -// TODO: @use(s) of IBM Products component styles used by InterstitialScreen +@use '../Carousel/carousel'; // The block part of our conventional BEM class names (blockClass__E--M). $block-class: #{c4p-settings.$pkg-prefix}--interstitial-screen; diff --git a/packages/ibm-products/src/components/InterstitialScreen/InterstitialScreen.js b/packages/ibm-products/src/components/InterstitialScreen/InterstitialScreen.js index bc813b8bf4..b5bdca3f01 100644 --- a/packages/ibm-products/src/components/InterstitialScreen/InterstitialScreen.js +++ b/packages/ibm-products/src/components/InterstitialScreen/InterstitialScreen.js @@ -300,7 +300,13 @@ export let InterstitialScreen = React.forwardRef(
{isMultiStep ? (
- + { + scrollPercent === 0 && setProgStep(0); + }} + > {children}
@@ -336,7 +342,13 @@ export let InterstitialScreen = React.forwardRef(
{isMultiStep ? (
- + { + scrollPercent === 0 && setProgStep(0); + }} + > {children}
diff --git a/packages/ibm-products/src/components/InterstitialScreen/InterstitialScreen.stories.js b/packages/ibm-products/src/components/InterstitialScreen/InterstitialScreen.stories.js index b4b5165726..b931a18578 100644 --- a/packages/ibm-products/src/components/InterstitialScreen/InterstitialScreen.stories.js +++ b/packages/ibm-products/src/components/InterstitialScreen/InterstitialScreen.stories.js @@ -300,21 +300,21 @@ interstitialScreenModalMultiplesHeader.args = { diff --git a/packages/ibm-products/src/components/Tearsheet/TearsheetNarrow.tsx b/packages/ibm-products/src/components/Tearsheet/TearsheetNarrow.tsx index ae37e4fa60..69861357e3 100644 --- a/packages/ibm-products/src/components/Tearsheet/TearsheetNarrow.tsx +++ b/packages/ibm-products/src/components/Tearsheet/TearsheetNarrow.tsx @@ -6,7 +6,7 @@ */ // Import portions of React that are needed. -import React, { ReactNode, PropsWithChildren } from 'react'; +import React, { ReactNode, PropsWithChildren, ForwardedRef } from 'react'; // Other standard imports. import PropTypes from 'prop-types'; @@ -25,16 +25,12 @@ import { tearsheetHasCloseIcon, TearsheetShell, tearsheetShellWideProps as blocked, + CloseIconDescriptionTypes, } from './TearsheetShell'; import { portalType } from './TearsheetShell'; -type closeIconDescriptionTypes = { - hasCloseIcon: true; - closeIconDescription: string; -}; - -interface TearsheetNarrowProps extends PropsWithChildren { +interface TearsheetNarrowBaseProps extends PropsWithChildren { /** * The navigation actions to be shown as buttons in the action area at the * bottom of the tearsheet. Each action is specified as an object with @@ -61,14 +57,6 @@ interface TearsheetNarrowProps extends PropsWithChildren { */ className?: string; - /** - * The accessibility title for the close icon (if shown). - * - * **Note:** This prop is only required if a close icon is shown, i.e. if - * there are a no navigation actions and/or hasCloseIcon is true. - */ - closeIconDescription?: closeIconDescriptionTypes; - /** * A description of the flow, displayed in the header area of the tearsheet. */ @@ -123,6 +111,9 @@ interface TearsheetNarrowProps extends PropsWithChildren { verticalPosition?: 'normal' | 'lower'; } +type TearsheetNarrowProps = TearsheetNarrowBaseProps & + CloseIconDescriptionTypes; + const componentName = 'TearsheetNarrow'; // NOTE: the component SCSS is not imported here: it is rolled up separately. @@ -147,7 +138,7 @@ export let TearsheetNarrow = React.forwardRef( verticalPosition = defaults.verticalPosition, ...rest }: TearsheetNarrowProps, - ref + ref: ForwardedRef ) => ( void; + + /** + * Specifies whether the tearsheet is currently open. + */ + open?: boolean; + + /** + * The DOM element that the tearsheet should be rendered within. Defaults to document.body. + */ + portalTarget?: ReactNode; + + selectorPrimaryFocus?: string; + + /** + * Specifies the width of the tearsheet, 'narrow' or 'wide'. + */ + size: 'narrow' | 'wide'; + + /** + * **Experimental:** Provide a `Slug` component to be rendered inside the `Tearsheet` component + */ + slug?: ReactNode; + + /** + * The main title of the tearsheet, displayed in the header area. + */ + title?: ReactNode; + + verticalPosition?: 'normal' | 'lower'; +} + +export type CloseIconDescriptionTypes = + | { + hasCloseIcon?: false; + closeIconDescription?: string; + } + | { + hasCloseIcon: true; + closeIconDescription: string; + }; + // NOTE: the component SCSS is not imported here: it is rolled up separately. // Global data structure to communicate the state of tearsheet stacking @@ -47,7 +169,20 @@ const maxDepth = 3; // The 'sizes' array contains an array of the sizes for every stacked tearsheet. // This is so we can opt-out of including the stacking scale effect when there // are stacked tearsheets with mixed sizes (ie, using wide and narrow together) -const stack = { open: [], all: [], sizes: [] }; +type stackTypes = { + open: Array<{ + (a: number, b: number): void; + checkFocus?: () => void; + claimFocus?: () => void; + }>; + all: Array<{ + (a: number, b: number): void; + checkFocus?: () => void; + claimFocus?: () => void; + }>; + sizes: Array; +}; +const stack: stackTypes = { open: [], all: [], sizes: [] }; // these props are only applicable when size='wide' export const tearsheetShellWideProps = [ @@ -96,8 +231,8 @@ export const TearsheetShell = React.forwardRef( verticalPosition, // Collect any other property values passed in. ...rest - }, - ref + }: TearsheetShellProps & CloseIconDescriptionTypes, + ref: ForwardedRef ) => { const carbonPrefix = usePrefix(); const bcModalHeader = `${carbonPrefix}--modal-header`; @@ -108,6 +243,8 @@ export const TearsheetShell = React.forwardRef( const modalRef = ref || localRef; const { width } = useResizeObserver(resizer); const { firstElement, keyDownListener } = useFocus(modalRef); + const modalRefValue = (modalRef as MutableRefObject) + .current; const wide = size === 'wide'; @@ -116,7 +253,7 @@ export const TearsheetShell = React.forwardRef( const [position, setPosition] = useState(0); // Keep a record of the previous value of depth. - const prevDepth = useRef(); + const prevDepth = useRef(); useEffect(() => { prevDepth.current = depth; }); @@ -127,7 +264,7 @@ export const TearsheetShell = React.forwardRef( // Callback that will be called whenever the stacking order changes. // position is 1-based with 0 indicating closed. - function handleStackChange(newDepth, newPosition) { + function handleStackChange(newDepth: number, newPosition: number) { setDepth(newDepth); setPosition(newPosition); } @@ -137,8 +274,8 @@ export const TearsheetShell = React.forwardRef( if ( open && position === depth && - modalRef.current && - !modalRef.current.contains(document.activeElement) + modalRefValue && + !modalRefValue.contains(document.activeElement) ) { handleStackChange.claimFocus(); } @@ -174,7 +311,7 @@ export const TearsheetShell = React.forwardRef( Math.min(stack.open.length, maxDepth), stack.open.indexOf(handler) + 1 ); - handler.checkFocus(); + handler.checkFocus?.(); }); // Register this tearsheet's stack change callback/listener. @@ -213,7 +350,7 @@ export const TearsheetShell = React.forwardRef( // If something within us is receiving focus but we are not the topmost // stacked tearsheet, transfer focus to the topmost tearsheet instead if (position < depth) { - stack.open[stack.open.length - 1].claimFocus(); + stack.open[stack.open.length - 1].claimFocus?.(); } } @@ -257,7 +394,7 @@ export const TearsheetShell = React.forwardRef( className={cx(bc, className, { [`${bc}--stacked-${position}-of-${depth}`]: // Don't apply this on the initial open of a single tearsheet. - depth > 1 || (depth === 1 && prevDepth.current > 1), + depth > 1 || (depth === 1 && (prevDepth?.current ?? 0) > 1), [`${bc}--wide`]: wide, [`${bc}--narrow`]: !wide, [`${bc}--has-slug`]: slug, @@ -339,7 +476,9 @@ export const TearsheetShell = React.forwardRef( {children} @@ -357,7 +496,7 @@ export const TearsheetShell = React.forwardRef( tearsheetHasCloseIcon(actions, hasCloseIcon) ), @@ -470,6 +611,7 @@ TearsheetShell.propTypes = { * tearsheet"), and that behavior can be overridden if required by setting * this prop to either true or false. */ + /**@ts-ignore*/ hasCloseIcon: PropTypes.bool, /** @@ -528,11 +670,13 @@ TearsheetShell.propTypes = { /** * The DOM element that the tearsheet should be rendered within. Defaults to document.body. */ + /**@ts-ignore*/ portalTarget: portalType, /** * Specifies the width of the tearsheet, 'narrow' or 'wide'. */ + /**@ts-ignore*/ size: PropTypes.oneOf(['narrow', 'wide']).isRequired, /** diff --git a/packages/ibm-products/src/components/Toolbar/Toolbar.tsx b/packages/ibm-products/src/components/Toolbar/Toolbar.tsx index de57e8436f..6445717d14 100644 --- a/packages/ibm-products/src/components/Toolbar/Toolbar.tsx +++ b/packages/ibm-products/src/components/Toolbar/Toolbar.tsx @@ -26,8 +26,6 @@ const { checkComponentEnabled, prefix } = pkg; const blockClass = `${prefix}--toolbar`; interface ToolbarProps { - - /** Provide an optional class to be applied to the containing node */ className?: string; @@ -47,14 +45,13 @@ let Toolbar = forwardRef( { children, className, vertical, ...rest }: PropsWithChildren, r: React.Ref ) => { - const focusableElements = useRef(); + const focusableElements = useRef(); const getFocusableElements = useCallback( - (): HTMLElement[]|undefined => focusableElements.current, + (): HTMLElement[] | undefined => focusableElements.current, [focusableElements] ); - const localRef = useRef(null); const ref = r || localRef; @@ -65,7 +62,7 @@ let Toolbar = forwardRef( ref?.['current'] ) as HTMLElement[]; - focus !== -1 && + focus !== -1 && getFocusableElements()?.forEach((element, index) => { element[index !== focus ? 'setAttribute' : 'removeAttribute']( 'tabindex', @@ -75,14 +72,14 @@ let Toolbar = forwardRef( }); useEffect(() => { - focus !== -1 && getFocusableElements()?.[focus].focus(); + focus !== -1 && getFocusableElements()?.[focus].focus(); }, [focus, getFocusableElements]); const [arrowNext, arrowPrevious] = !vertical ? ['ArrowRight', 'ArrowLeft'] : ['ArrowDown', 'ArrowUp']; - function onArrowDown(increment:number) { + function onArrowDown(increment: number) { const nextFocus = focus + increment; getFocusableElements()?.[nextFocus] && setFocus(nextFocus); @@ -111,7 +108,7 @@ let Toolbar = forwardRef( return (
{ return result; }; -// Default values for props -const defaults = { - element: 'div', -}; +interface WrapProps extends PropsWithChildren { + /** + * Specify whether the wrapper element should render even if there are no + * children or the children are themselves empty wrappers. Useful if there + * are some conditions in which the wrapper element is still required. Note + * that this prop takes precedence over neverRender if both are set to true. + */ + alwaysRender?: boolean | null; + + /** + * The element name or component to use as a wrapper for the content. + */ + element?: (() => ReactNode) | string | ElementType; + + /** + * Specify whether nothing should be rendered even if there are children + * in the content. Useful if there are some circumstances in which the + * component should not render at all. Note that if alwaysRender is also + * set to true then it will take precedence and the wrapper element and + * content will be rendered. + */ + neverRender?: boolean; + + className?: string; +} /** * A simple conditional wrapper that encloses its children in a
(or other @@ -47,16 +73,17 @@ export const Wrap = React.forwardRef( alwaysRender, children, - element: Wrapper = defaults.element, + element: Wrapper = 'div', neverRender, + className, // Collect any other property values passed in. ...rest - }, - ref + }: WrapProps, + ref: ForwardedRef ) => (neverRender || isEmpty(children)) && !alwaysRender ? null : ( - + {children} ) @@ -80,6 +107,8 @@ Wrap.propTypes = { */ children: PropTypes.node, + className: PropTypes.string, + /** * The element name or component to use as a wrapper for the content. */ diff --git a/packages/ibm-products/src/global/js/utils/getNodeTextContent.js b/packages/ibm-products/src/global/js/utils/getNodeTextContent.js index 94e70d3d4c..8511d49eb8 100644 --- a/packages/ibm-products/src/global/js/utils/getNodeTextContent.js +++ b/packages/ibm-products/src/global/js/utils/getNodeTextContent.js @@ -16,7 +16,7 @@ *
  • Item 3
  • * * --> "Item 1Item 2Item 3" - * @param {Node} node A React node + * @param {ReactNode} node A React node * @returns {string} */ export const getNodeTextContent = (node) => {